Compare commits

..

105 Commits

Author SHA1 Message Date
Michael Vines
acb992c3be Update to spl-token 2 2020-08-29 09:23:20 -07:00
Tyera Eulberg
61aca235a3 Bump spl-token version 2020-08-29 03:24:36 -06:00
mergify[bot]
bad0709ff1 Update to token pack/unpack changes (bp #11900) (#11903)
* Update to token pack/unpack changes (#11900)

(cherry picked from commit 2eff9a19c3)

# Conflicts:
#	account-decoder/Cargo.toml
#	core/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-29 05:46:07 +00:00
mergify[bot]
2a649e990d Update spl-token to v2.0 (bp #11884) (#11897)
* Update spl-token to v2.0 (#11884)

* Update account-decoder to spl-token v2.0

* Update transaction-status to spl-token v2.0

* Update rpc to spl-token v2.0

* Update getTokenSupply to pull from Mint directly

* Fixup to spl-token v2.0.1

(cherry picked from commit 76be36c9ce)

# Conflicts:
#	Cargo.lock
#	account-decoder/Cargo.toml
#	core/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-28 23:36:53 +00:00
mergify[bot]
1a25889f72 Take v0.19.3 of perf libs which improves sigverify perf 2x (#11894) (#11895)
(cherry picked from commit 9393dce1ff)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-28 20:37:26 +00:00
Trent Nelson
e15dca6961 Update devnet cluster docs since reboot 2020-08-28 10:36:11 -07:00
mergify[bot]
bb12f48014 Add SolFlare as stake-supporting wallet (#11891) (#11892)
Co-authored-by: publish-docs.sh <maintainers@solana.com>
(cherry picked from commit 8ba3a33129)

Co-authored-by: Dan Albert <dan@solana.com>
2020-08-28 16:17:24 +00:00
Jon Cinque
c6416fca6e Bump version to 1.3.6 (#11890) 2020-08-28 16:58:59 +02:00
Jack May
2f5d60bef7 Update epoch gating (#11880) 2020-08-27 21:17:45 -07:00
mergify[bot]
ef1a9df507 Small cleaning around consensus/bank_forks (#11873) (#11881)
(cherry picked from commit d8c529a9b8)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-28 01:06:21 +00:00
mergify[bot]
d2611f54a0 Make ledger-tool accounts print rent_epoch and slot (#11845) (#11870)
(cherry picked from commit 57174cdabe)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-27 04:49:24 +00:00
mergify[bot]
6117f8d64e Add SolFlare guide to docs (#11843) (#11868)
(cherry picked from commit 36e8441149)

Co-authored-by: Dan Albert <dan@solana.com>
2020-08-26 23:13:43 +00:00
mergify[bot]
67d9faaefc Bump compute budget (#11864) (#11867)
* Bump compute budget

* nudge

(cherry picked from commit ea179ad762)

Co-authored-by: Jack May <jack@solana.com>
2020-08-26 23:12:01 +00:00
mergify[bot]
aaa551ca7c Merge pull request #11857 from mvines/cache (#11863)
ci: cargo-target-cache is now channel specific
(cherry picked from commit 5c7080c1f4)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-26 19:50:06 +00:00
mergify[bot]
85df8cb4c5 Accounts hash calculation metrics (#11433) (#11860)
(cherry picked from commit 770d3d383c)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-26 19:31:52 +00:00
mergify[bot]
65f189d932 Rpc: Filter accounts with invalid mints from get_parsed_token_accounts (#11844) (#11859)
* Filter out accounts with invalid mints from get_parsed_token_accounts

* Explicit docs

(cherry picked from commit 1988ee9cd6)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-26 19:02:48 +00:00
mergify[bot]
99001f7f2e Bump MacOS nofile recommendation message (#11835)
(cherry picked from commit 8841c3398c)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-08-25 19:57:08 +00:00
mergify[bot]
39eeb0142e Add SystemInstruction::CreateAccount support to CPI (bp #11649) (#11831)
* Add SystemInstruction::CreateAccount support to CPI (#11649)

(cherry picked from commit e9b610b8df)

# Conflicts:
#	programs/bpf_loader/src/syscalls.rs
#	runtime/src/bank.rs
#	sdk/src/instruction.rs

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-08-25 19:52:27 +00:00
mergify[bot]
d5d1a344c3 Switch programs activation to whole-set based gating (bp #11750) (#11837)
* Switch programs activation to whole-set based gating (#11750)

* Implement Debug for MessageProcessor

* Switch from delta-based gating to whole-set gating

* Remove dbg!

* Fix clippy

* Clippy

* Add test

* add loader to stable operating mode at proper epoch

* refresh_programs_and_inflation after ancestor setup

* Callback via snapshot; avoid account re-add; Debug

* Fix test

* Fix test and fix the past history

* Make callback management stricter and cleaner

* Fix test

* Test overwrite and frozen for native programs

* Test epoch callback with genesis-programs

* Add assertions for parent bank

* Add tests and some minor cleaning

* Remove unsteady assertion...

* Fix test...

* Fix DOS

* Skip ensuring account by dual (whole/delta) gating

* Fix frozen abi implementation...

* Move compute budget constatnt init back into bank

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
(cherry picked from commit db4bbb3569)

# Conflicts:
#	genesis-programs/src/lib.rs

* Fix conflicts

Co-authored-by: Jack May <jack@solana.com>
Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-25 19:00:41 +00:00
mergify[bot]
62e3c084d3 Update system tuning and docs (bp #11680) (#11830)
* Sync FD limit and max maps to 500k

(cherry picked from commit 11951eb009)

* Expand system tuning docs

(cherry picked from commit 5354df8c1c)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-08-25 17:13:27 +00:00
mergify[bot]
3027ceb53a Document how to validate account pubkey (#11821) (#11833)
(cherry picked from commit 2c5366f259)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-25 17:11:09 +00:00
mergify[bot]
4e604e1211 Add (hidden) --use-deprecated-loader flag to solana deploy (#11828)
(cherry picked from commit de736e00ad)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-25 16:56:29 +00:00
mergify[bot]
6749bfd1d2 Gate aligned program heap (bp #11808) (#11814)
* Gate aligned program heap (#11808)


(cherry picked from commit c2e5dae7ba)

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-08-25 15:57:15 +00:00
mergify[bot]
d05b39c4f6 Specify loader when bootstrapping bpf programs (#11571) (#11822)
(cherry picked from commit 0a94e7e7fa)

Co-authored-by: Jack May <jack@solana.com>
2020-08-25 07:56:11 -07:00
mergify[bot]
c8e1fbd568 Update comment (#11826) (#11827)
(cherry picked from commit dbd079f54c)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-25 14:48:00 +00:00
mergify[bot]
ad36ddedf1 CPI support for bpf_loader_deprecated (bp #11695) (#11824)
* CPI support for bpf_loader_deprecated (#11695)

(cherry picked from commit 46830124f8)

# Conflicts:
#	programs/bpf_loader/src/syscalls.rs

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-08-25 11:02:06 +00:00
mergify[bot]
08bece7651 More efficient padding (#11656) (#11823)
(cherry picked from commit f1ba2387d3)

Co-authored-by: Jack May <jack@solana.com>
2020-08-25 10:10:59 +00:00
mergify[bot]
f162c6d1d0 Align host addresses (bp #11384) (#11817)
* Align host addresses (#11384)

* Align host addresses

* support new program abi

* update epoch rollout

* Enforce aligned pointers in cross-program invocations

(cherry picked from commit 9290e561e1)

# Conflicts:
#	core/src/validator.rs
#	genesis-programs/src/lib.rs
#	programs/bpf_loader/src/deprecated.rs
#	programs/bpf_loader/src/lib.rs
#	sdk/src/entrypoint_native.rs
#	sdk/src/lib.rs

* resolve conflicts

* nudge

Co-authored-by: Jack May <jack@solana.com>
2020-08-25 07:23:20 +00:00
mergify[bot]
f3904b5765 sdk: Make PubKey::create_program_address available in program unit tests (bp #11745) (#11810)
* sdk: Make PubKey::create_program_address available in program unit tests (#11745)

* sdk: Make PubKey::create_program_address available in program unit tests

This finishes the work started in #11604 to have
`create_program_address` available when `target_arch` is not `bpf` and
`program` is enabled.  Otherwise, there is an undefined reference error
to `sol_create_program_address`, which is only defined in `bpf`.

A small test to simply call the function has been added in order to catch
the problem in the future.

The default dependency to `solana-sdk/default` doesn't cause a problem with
existing programs since `build.sh` always specifies
`--no-default-features`, and programs in `solana-program-library` all
use it too.

* Add `default-features = false` for inter-program dependencies

Fix the build error found during CI.  The `--no-default-features` flag
only applies to the top-level package, and not to dependencies.  A program that
depends on another program, i.e. `128bit` which depends on `128bit_dep`,
must specify `default-features = false` when including that package,
otherwise the `bpf` build will try to pull in default packages, which
includes `std`.

(cherry picked from commit 9a366281d3)

# Conflicts:
#	programs/bpf/rust/128bit/Cargo.toml
#	programs/bpf/rust/invoke/Cargo.toml
#	programs/bpf/rust/many_args/Cargo.toml
#	programs/bpf/rust/param_passing/Cargo.toml

* resolve conflicts

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
Co-authored-by: Jack May <jack@solana.com>
2020-08-24 20:41:54 +00:00
mergify[bot]
67bf7515a7 Aligned program heap (#11657) (#11812)
(cherry picked from commit f8606fca4f)

Co-authored-by: Jack May <jack@solana.com>
2020-08-24 20:12:58 +00:00
mergify[bot]
0dcbc6d4d1 The constraints on compute power a program can consume is limited only to its instruction count (bp #11717) (#11800)
* The constraints on compute power a program can consume is limited only to its instruction count (#11717)

(cherry picked from commit 8d362f682b)

# Conflicts:
#	programs/bpf/Cargo.toml
#	programs/bpf_loader/Cargo.toml
#	programs/bpf_loader/src/lib.rs
#	programs/bpf_loader/src/syscalls.rs
#	runtime/src/bank.rs
#	sdk/src/instruction.rs

* Resolve conflicts

* nudge

Co-authored-by: Jack May <jack@solana.com>
2020-08-24 17:27:40 +00:00
mergify[bot]
79a2ccabb4 Return an error from create_program_address syscall (bp #11658) (#11789)
* Return an error from create_program_address syscall (#11658)

(cherry picked from commit 750e5344f1)

# Conflicts:
#	programs/bpf_loader/src/syscalls.rs

* resolve conflicts

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-08-24 04:30:29 +00:00
mergify[bot]
f5e583ef0d solana-gossip spy can now be given an identity keypair (--identity argument) (#11797)
(cherry picked from commit a1e2357d12)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-23 01:14:42 +00:00
mergify[bot]
132550cd7a RPC: Allow the sendTransaction preflight commitment level to be configured (bp #11792) (#11794)
* Allow the sendTransaction preflight commitment level to be configured

(cherry picked from commit b660704faa)

* rebase

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-22 16:41:25 +00:00
Michael Vines
08a789323f Fix typo 2020-08-22 09:22:41 -07:00
mergify[bot]
2d9781b101 Add option for repairing only from trusted validators (#11752) (#11773)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit c8d67aa8eb)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-21 20:40:37 -07:00
mergify[bot]
6540d3c63e Make BPF Loader static (bp #11516) (#11790)
* Make BPF Loader static (#11516)

(cherry picked from commit 7c736f71fe)

# Conflicts:
#	Cargo.lock
#	core/Cargo.toml
#	core/src/lib.rs
#	core/src/validator.rs
#	genesis-programs/src/lib.rs
#	programs/bpf_loader/src/deprecated.rs
#	programs/bpf_loader/src/lib.rs
#	sdk/src/entrypoint_native.rs
#	sdk/src/lib.rs

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-08-22 01:54:50 +00:00
mergify[bot]
a227b813d8 Feature check CPI up front (#11652) (#11787)
(cherry picked from commit 4196686acf)

Co-authored-by: Jack May <jack@solana.com>
2020-08-22 01:27:33 +00:00
mergify[bot]
2b4e0abb43 fix region checks (#11651) (#11785)
(cherry picked from commit 768b386f0a)

Co-authored-by: Jack May <jack@solana.com>
2020-08-22 01:14:13 +00:00
mergify[bot]
cdf6ff7907 Fix filter_crds_values output alignment with the inputs (#11734) (#11781)
(cherry picked from commit 418b483af6)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-08-21 20:56:26 +00:00
mergify[bot]
6775e01747 Add StakeInstruction::AuthorizeWithSeed (#11700) (#11779)
* Add StakeInstruction::AuthorizeWithSeed

* chore: add authorize-with-seed to web.js

* fix: add address_owner

* Add SystemInstruction::TransferWithSeed

* Update ABI hash

* chore: better variable names

* Add AuthorizeWithSeedArgs

* Reorder and rename arguments for clarity

(cherry picked from commit f02a78d8ff)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-08-21 19:39:36 +00:00
mergify[bot]
4be9d5030d Squash supermajority root on blockstore replay at startup (#11727) (#11765)
(cherry picked from commit f7adb68599)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-21 08:20:09 +00:00
carllin
a9f914da8e Cleanup test utilities (#11766)
* Add voting utility

* Add blockstore utility

Co-authored-by: Carl <carl@solana.com>
2020-08-21 06:52:01 +00:00
mergify[bot]
096375584b Rpc: Return error if block does not exist (#11743) (#11749)
* Return error if block does not exist

* Update docs

(cherry picked from commit 747f8d5877)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-20 23:26:55 +00:00
mergify[bot]
d3ab1ec9cf Do not delete any ledger when --limit-ledger-size is not provided (#11741)
(cherry picked from commit ea88bbdc33)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-20 19:27:48 +00:00
mergify[bot]
e41d9c87c5 Bump spl-token to clean up magic number (bp #11726) (#11738)
* Bump spl-token to clean up magic number (#11726)

(cherry picked from commit 2fd2aceeb2)

# Conflicts:
#	account-decoder/Cargo.toml
#	core/Cargo.toml

* Fix conflicts and toml order

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-20 18:02:28 +00:00
Greg Fitzgerald
e938925b95 Add BanksClient (#11721)
Cherry-picked from #10728, but without the changes to solana-tokens
2020-08-19 22:24:24 -06:00
mergify[bot]
6c03e6c4b5 Allow votes to timestamp subsequent slots with the same timestamp (#11715) (#11720)
(cherry picked from commit b1bc901a66)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-20 00:40:04 +00:00
Trent Nelson
abce60efdf Bump version to 1.3.5 2020-08-19 20:19:34 +00:00
mergify[bot]
b36510a565 Skip grace blocks if previous leader was on different fork (#11679) (#11709)
* Start leader blocks if previous leader was on different fork

* Fix test

Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 5f8d34feb3)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-19 09:29:08 +00:00
mergify[bot]
a8220ae653 The end_slot argument to purge is now optional (#11707)
(cherry picked from commit d1500ae229)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-19 04:41:54 +00:00
mergify[bot]
2c642d4639 Filter push/pulls from spies (#11620) (#11703)
* Filter push/pulls from spies

* Don't pull from peers with shred version == 0, don't push to people with shred_version == 0

Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 0f0a2ddafe)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-19 03:20:43 +00:00
mergify[bot]
04eb35e679 Remove old signatureSubscribe info (#11704) (#11706)
(cherry picked from commit 35828e8fe7)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-19 02:44:29 +00:00
mergify[bot]
8738241567 Get index (#11694) (#11697)
(cherry picked from commit 55ce2ebd53)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-18 18:54:17 +00:00
Justin Starry
0ef9185c9e Fully enable cross program support in mainnet-beta
(cherry picked from commit 9e89a963d9)
2020-08-18 06:39:24 -07:00
mergify[bot]
e41004f185 rpc: rework binary encoding. BREAKING CHANGE (bp #11646) (#11674)
* Add base64 (binary64) encoding for getConfirmedTransaction/getConfirmedBlock

(cherry picked from commit b5f3ced860)

* decode-transaction now supports binary64

(cherry picked from commit 2ebc68a9e2)

# Conflicts:
#	cli/src/cli.rs

* Rework UiAccountData encode/decode such that it works from Rust

(cherry picked from commit 757e147b3b)

# Conflicts:
#	account-decoder/src/lib.rs
#	cli/src/cli.rs

* Rename Binary64 to Base64.  Establish Base58 encoding

(cherry picked from commit adc984a225)

# Conflicts:
#	account-decoder/src/lib.rs

* Remove "binary" encoding. Document "encoding" as required

(cherry picked from commit e5281157fa)

* resolve conflicts

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-18 02:53:10 +00:00
mergify[bot]
41cad9ccd5 Faucet: Add per-request cap (#11665) (#11669)
* Add per-request cap; also use clap-utils

* Clean up arg names and take cap inputs as SOL

(cherry picked from commit 71d5409b3b)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-18 01:00:11 +00:00
mergify[bot]
d9ae092637 Re-do rent collection check on rent-exempt account (bp #11349) (#11655)
* Re-do rent collection check on rent-exempt account (#11349)

* wip: re-do rent collection check on rent-exempt account

* Let's see how the ci goes

* Restore previous code

* Well, almost all new changes are revertable

* Update doc

* Add test and gating

* Fix tests

* Fix tests, especially avoid to change abi...

* Fix more tests...

* Fix snapshot restore

* Align to _new_ with better uninitialized detection

(cherry picked from commit 23fa84b322)

# Conflicts:
#	core/src/rpc_subscriptions.rs

* Fix conflicts

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-17 07:20:24 +00:00
mergify[bot]
9015e47cc5 Rpc: Add until parameter for getConfirmedSignaturesForAddress2 (#11644) (#11648)
* Refactor bigtable apis to accept start and end keys

* Make helper fn to deserialize cell data

* Refactor get_confirmed_signatures_for_address to use get_row_data range

* Add until param to get_confirmed_signatures_for_address

* Add until param to blockstore api

* Plumb until through client/cli

* Simplify client params

(cherry picked from commit 6c5b8f324a)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-15 17:59:35 +00:00
mergify[bot]
15b92e9c8d Bigtable: Use index to filter address-signatures correctly (#11622) (#11643)
* Use index to filter address-signatures correctly

* Pull additional keys to account for filtered records

* Clarify variable name

(cherry picked from commit 820af533a4)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-14 21:02:13 +00:00
Trent Nelson
c72bd900cd Bump version to 1.3.4 2020-08-14 18:29:46 +00:00
mergify[bot]
ecb75ccdcf short_vec::decode_len() returns wrong size for aliased values (bp #11624) (#11631)
* Add failing test for decoding ShortU16 alias values

(cherry picked from commit 338f66f9aa)

* Factor out ShortU16 deser vistor logic to helper

(cherry picked from commit 6222fbcc66)

* Reimplement decode_len() with ShortU16 vistor helper

(cherry picked from commit 30dbe257cf)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-08-14 15:33:00 +00:00
mergify[bot]
cf22e7a273 Restore CLI usage page (#11619) (#11621)
Co-authored-by: publish-docs.sh <maintainers@solana.com>
(cherry picked from commit 6194a29875)

Co-authored-by: Dan Albert <dan@solana.com>
2020-08-13 22:17:47 +00:00
mergify[bot]
4f7bfbdbe9 RPC: getConfirmedSignaturesForAddress2 only returns confirmed signatures (#11615) (#11618)
* Add failing test case

* Limit to only rooted slots

(cherry picked from commit 99fb36fe45)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-13 18:19:54 +00:00
mergify[bot]
011e325359 Ensure highest_confirmed_root only grows (#11596) (#11608)
* Split out commitment-cache update for unit testing

* Add failing test

* Ensure highest_confirmed_root only grows

(cherry picked from commit 4da1e9833c)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-13 08:13:00 +00:00
mergify[bot]
ba05852475 generate_pull_response optimization (#11597) (#11606)
(cherry picked from commit f519fdecc2)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-13 06:53:45 +00:00
mergify[bot]
8ee656edde Return blockstore signatures-for-address despite bigtable error (#11594) (#11599)
(cherry picked from commit b1e452f876)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-13 01:12:16 +00:00
mergify[bot]
311d9a56c4 Add incoming pull response counter (#11591) (#11592)
(cherry picked from commit 54137e3446)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-12 22:17:50 +00:00
mergify[bot]
b9f46fd904 Fix assertion failure (#11572) (#11590)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 473b5249e3)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-12 20:19:54 +00:00
mergify[bot]
39f2d346b8 Fix typo: epoch => slot... (#11573) (#11580)
(cherry picked from commit 51e818ad64)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-12 09:27:01 +00:00
Trent Nelson
4a27bfa2fe Bump version to 1.3.3 2020-08-12 00:43:50 +00:00
mergify[bot]
69e53ec92a Gossip log (#11555) (#11562)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 1b238dd63e)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-11 22:55:18 +00:00
mergify[bot]
b8ac76066c Move cluster slots update to separate thread (#11523) (#11558)
* Add cluster_slots_service

Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 7ef50a9352)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-11 21:24:34 +00:00
mergify[bot]
1a1d7744bd Add getTokenLargestAccounts to docs (#11560) (#11564)
(cherry picked from commit 697a0e2947)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-11 21:21:35 +00:00
mergify[bot]
61af485732 filter out old gossip pull requests (#11448) (#11553)
* init

* builds

* stats

* revert

* tests

* clippy

* add some jitter

* shorter jitter timer

* update

* fixup! update

* use saturating_sub

* fix filters

(cherry picked from commit 713851b68d)

Co-authored-by: anatoly yakovenko <anatoly@solana.com>
2020-08-11 19:42:32 +00:00
mergify[bot]
6a60d7bf8e Adapt RpcClient to recent token method changes (#11519) (#11548)
* Avoid skip_serializing_if since that breaks deserialization

* Adapt RpcClient to recent token method changes

(cherry picked from commit 17645ee20c)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-11 17:31:57 +00:00
mergify[bot]
53e917b54f Fix bad rent in Bank::deposit as if since epoch 0 (#10468) (#11539)
* Fix bad rent in Bank::deposit as if since epoch 0

* Remove redundant predicate

* Rename

* Start to add tests with some cleanup

* Forgot to add refactor code...

* Enchance test

* Really fix rent timing in deposit with robust test

* Simplify new behavior by disabling rent altogether

(cherry picked from commit 6c242f3fec)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-11 16:20:06 +00:00
mergify[bot]
4d49c99ac9 Fix simulateTransaction JSON-RPC docs (#11533) (#11535)
(cherry picked from commit f12fc66a69)

Co-authored-by: Justin Starry <justin@solana.com>
2020-08-11 10:26:20 +00:00
mergify[bot]
83597a5ce1 Fix solana CLI deploy (#11520) (#11530)
* Refresh blockhash for program writes and finalize transactions

* Refactor to use current api, eliminating an rpc call

* Review comment

(cherry picked from commit c0d6761f63)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-11 09:52:11 +00:00
mergify[bot]
1589a41178 Add config param to specify offset/length for single and program account info (bp #11515) (#11518)
* Add config param to specify offset/length for single and program account info (#11515)

* Add config param to specify dataSlice for account info and program accounts

* Use match instead of if

(cherry picked from commit 88ca04dbdb)

# Conflicts:
#	cli/src/cli.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-11 00:03:18 +00:00
mergify[bot]
23a381b995 Fix parsing of spl-token Mint (#11512) (#11514)
* Add failing test

* Fix jsonParsed mint

(cherry picked from commit da210ddd51)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-10 21:51:20 +00:00
mergify[bot]
eb7ac42b2e Return account data size with parsed accounts (#11506) (#11511)
(cherry picked from commit 1925b0bd0b)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-10 20:09:13 +00:00
mergify[bot]
88cf5e79f5 Unified signature for create_program_address (#11460) (#11509)
(cherry picked from commit 140b2392f6)

Co-authored-by: Jack May <jack@solana.com>
2020-08-10 18:31:51 +00:00
mergify[bot]
520453e1f3 Blockstore address signatures: handle slots that cross primary indexes, and refactor get_confirmed_signatures_for_address2 (#11497) (#11508)
* Freeze address-signature index in the middle of slot to show failure case

* Secondary filter on signature

* Use AddressSignatures iterator instead of manually decrementing slots

* Remove unused method

* Add metrics

* Add transaction-status-index doccumentation

(cherry picked from commit de5fb3ba0e)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-10 17:43:00 +00:00
mergify[bot]
27815555a1 Fallback to base64 account encoding if json parse fails (#11483) (#11488)
* Fallback to base64 account encoding if json parse fails

* Remove default binary conversion

(cherry picked from commit ebc45bd73f)

Co-authored-by: Justin Starry <justin@solana.com>
2020-08-09 16:15:09 +00:00
mergify[bot]
3e483314b6 Decode native-program and sysvar accounts (bp #11463) (#11485)
* Decode native-program and sysvar accounts (#11463)

* Pass pubkey in to account-decoder for sysvars

* Decode sysvar accounts

* Decode config accounts; move validator-info lower

* Decode stake accounts

* Review comments

* Stringify any account lamports and epochs that can be set to u64::MAX

(cherry picked from commit a9f76862fb)

# Conflicts:
#	Cargo.lock
#	account-decoder/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-09 09:18:17 +00:00
mergify[bot]
8a67504578 Add Binary64 option for account data (#11474) (#11481)
* Add Binary64 option for account data

* Decode into binary64

* Reword docs

(cherry picked from commit 068d23f298)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-09 06:50:24 +00:00
mergify[bot]
a1b238280b Return delegated amount as UiTokenAmount (#11475) (#11477)
* Return delegated amount as UiTokenAmount

* Omit delegate and delegatedAmount when none

(cherry picked from commit 88d8d3d02a)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-09 00:01:01 +00:00
Trent Nelson
f9e07f575e Bump version to v1.3.2 2020-08-08 06:26:20 +00:00
Trent Nelson
c8bad57455 Bump version to v1.3.1 2020-08-07 19:21:41 -06:00
mergify[bot]
71654c0457 Fix cbindgen compatibility (#11455) (#11459)
(cherry picked from commit 5a7e99f283)

Co-authored-by: Jack May <jack@solana.com>
2020-08-07 23:44:49 +00:00
mergify[bot]
f9d6fb48a4 Send votes from banking stage to vote listener (#11434) (#11454)
*  Send votes from banking stage to vote listener

Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 7e25130529)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-07 19:45:40 +00:00
mergify[bot]
fa9aa0a1d7 Token Accounts: return ui_amount, decimals with decoded account (#11407) (#11453)
* Return ui_amount, decimals from token client methods

* Return ui_amount, decimals in RPC jsonParsed token accounts

* Fixup docs

* Return ui_amount, decimals in pubsub jsonParsed token accounts

* Remove unnecessary duplicate struct

* StringAmount rename

(cherry picked from commit b7c2681903)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-07 19:05:35 +00:00
mergify[bot]
9758ebfc99 Only run web3.js/explorer CI when targeting master branch (#11440)
(cherry picked from commit a3165c6a61)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-08-07 06:24:42 +00:00
mergify[bot]
8be23a2bb2 Add address-based lower bound to get_confirmed_signatures_for_address2 loop (#11426) (#11432)
(cherry picked from commit 5530ee4c95)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-07 00:06:44 +00:00
mergify[bot]
4ff9a6910d Fix blockstore empty panic (#11423) (#11430)
* Add panicking test

* Add failing test: fresh transaction-status column shouldn't point at valid root 0

* Prevent transaction status match outside of primary-index bounds

* Initialize transaction-status and address-signature primer entries with Slot::MAX

* Revert "Add failing test: fresh transaction-status column shouldn't point at valid root 0"

This reverts commit cbad2a9fae.

* Revert "Initialize transaction-status and address-signature primer entries with Slot::MAX"

This reverts commit ffaeac0669.

(cherry picked from commit 1061b50665)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-06 23:39:01 +00:00
mergify[bot]
fd192e3641 Link fix (#11368) (#11425)
* fixes logo

* cleans up homepage on docs

* adds icon files and tightens margins

* cleans up sidenav, adds top nav items

* fixes a link

* removes icon files

Co-authored-by: Dan Albert <dan@solana.com>
(cherry picked from commit 14dcaaee6c)

Co-authored-by: Raj Gokal <rajivgokal@gmail.com>
2020-08-06 19:37:04 +00:00
mergify[bot]
a8de467ef8 Realloc not supported (#11418)
(cherry picked from commit bc4c5c5a97)

Co-authored-by: Jack May <jack@solana.com>
2020-08-06 16:24:14 +00:00
Michael Vines
1a186beb5c Update lib.rs
(cherry picked from commit 5a63c9d535)
2020-08-06 07:58:05 -07:00
Michael Vines
66a21ed65e Enable cross program support in mainnet-beta epoch 63
(cherry picked from commit c9b1d08218)
2020-08-06 07:58:05 -07:00
mergify[bot]
1a42a40492 RPC: Plug getConfirmedSignaturesForAddress2 into bigtable storage (bp #11395) (#11406)
* Plug getConfirmedSignaturesForAddress2 into bigtable storage

(cherry picked from commit 4222932e08)

* Upgrade help description

(cherry picked from commit 9abb7db5f8)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-06 07:14:46 +00:00
mergify[bot]
5841e4d665 Long-term ledger storage with BigTable (bp #11222) (#11392)
* ledger-storage-bigtable boilerplate

(cherry picked from commit 9d2293bb32)

* $ wget https://pki.goog/roots.pem -O pki-goog-roots.pem

(cherry picked from commit 1617a025ce)

* Add access_token module

(cherry picked from commit 59d266a111)

* Add root_ca_certificate

(cherry picked from commit faa016e4b7)

* Add build-proto

(cherry picked from commit c31e1f5bf0)

* UiTransactionEncoding is now copy

(cherry picked from commit 494968be66)

* Increase timeout

(cherry picked from commit 57dfebc5ba)

* Add build-proto/build.sh output

(cherry picked from commit 54dae6ba2c)

* Supress doctest errors

(cherry picked from commit 019c75797d)

* Add compression

(cherry picked from commit 243e05d59f)

* Add bigtable

(cherry picked from commit 6e0353965a)

* Add configuration info

(cherry picked from commit 98cca1e774)

* Add ledger-tool bigtable subcommands

(cherry picked from commit f9049d6ee4)

# Conflicts:
#	ledger-tool/Cargo.toml

* Make room for tokio 0.2

(cherry picked from commit b876fb84ba)

# Conflicts:
#	core/Cargo.toml

* Setup a tokio 0.2 runtime for RPC usage

(cherry picked from commit 0e02740565)

# Conflicts:
#	core/Cargo.toml

* Plumb Bigtable ledger storage into the RPC subsystem

(cherry picked from commit dfae9a9864)

# Conflicts:
#	core/Cargo.toml

* Add RPC transaction history design

(cherry picked from commit e56ea138c7)

* Simplify access token refreshing

(cherry picked from commit 1f7af14386)

* Report block status more frequently

(cherry picked from commit 22c46ebf96)

* after -> before

(cherry picked from commit 227ea934ff)

* Rebase

* Cargo.lock

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-06 04:06:44 +00:00
mergify[bot]
6542a04521 Mark token-specific rpcs as unstable (#11402)
(cherry picked from commit 7430896c79)

Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-06 04:00:18 +00:00
mergify[bot]
5c27009758 Force program address off the curve (#11323) (#11398)
(cherry picked from commit 03263c850a)

Co-authored-by: Jack May <jack@solana.com>
2020-08-06 01:00:42 +00:00
mergify[bot]
888f3522d8 Add getConfirmedSignaturesForAddress2 RPC method (bp #11259) (#11394)
* Add getConfirmedSignaturesForAddress2 RPC method

(cherry picked from commit 1b2276520b)

* Reimplement transaction-history command with getConfirmedSignaturesForAddress2

(cherry picked from commit 087fd32ce3)

* Rework get_confirmed_signatures_for_address2

(cherry picked from commit a11f137810)

* Rename startAfter to before

(cherry picked from commit 02c0981ecf)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-05 22:47:50 +00:00
997 changed files with 42510 additions and 113597 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,7 +1,12 @@
{ {
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f", "_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
"_comment": "These credentials are encrypted and pose no risk",
"environment": { "environment": {
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]" "CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]",
"CRATES_IO_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:GGRTYDjMXksevzR6kq4Jx+FaIQZz50RU:xkbwDxcgoCyU+aT2tiI9mymigrEl6YiOr3axe3aX70ELIBKbCdPGilXP/wixvKi94g2u]",
"GEOLOCATION_API_KEY": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:U2PZLi5MU3Ru/zK1SilianEeizcMvxml:AJKf2OAtDHmJh0KyXrBnNnistItZvVVP3cZ7ZLtrVupjmWN/PzmKwSsXeCNObWS+]",
"GITHUB_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:0NJNlpD/O19mvOakCGBYDhIDfySxWFSC:Dz4NXv9x6ncRQ1u9sVoWOcqmkg0sI09qmefghB0GXZgPcFGgn6T0mw7ynNnbUvjyH8dLruKHauk=]",
"INFLUX_DATABASE": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:SzwHIeOVpmbTcGQOGngoFgYumsLZJUGq:t7Rpk49njsWvoM+ztv5Uwuiz]",
"INFLUX_PASSWORD": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:/MUs+q7pdGrUjzwcq+6pgIFxur4hxdqu:am22z2E2dtmw1f1J1Mq5JLcUHZsrEjQAJ0pp21M4AZeJbNO6bVb44d9zSkHj7xdN6U+GNlCk+wU=]",
"INFLUX_USERNAME": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:XjghH20xGVWro9B+epGlJaJcW8Wze0Bi:ZIdOtXudTY5TqKseDU7gVvQXfmXV99Xh]"
} }
} }

View File

@@ -31,9 +31,4 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
mkdir -p "$CARGO_TARGET_CACHE"/target mkdir -p "$CARGO_TARGET_CACHE"/target
rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target . rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target .
# Don't reuse BPF target build artifacts due to incremental build issues with
# `std:
# "found possibly newer version of crate `std` which `xyz` depends on
rm -rf target/bpfel-unknown-unknown
) )

38
.github/stale.yml vendored
View File

@@ -1,3 +1,11 @@
only: pulls
# Number of days of inactivity before a pull request becomes stale
daysUntilStale: 7
# Number of days of inactivity before a stale pull request is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale # Issues with these labels will never be considered stale
exemptLabels: exemptLabels:
- security - security
@@ -6,34 +14,12 @@ exemptLabels:
# Label to use when marking a pull request as stale # Label to use when marking a pull request as stale
staleLabel: stale staleLabel: stale
pulls: # Comment to post when marking a pull request as stale. Set to `false` to disable
# Number of days of inactivity before a pull request becomes stale markComment: >
daysUntilStale: 7
# Number of days of inactivity before a stale pull request is closed
daysUntilClose: 7
# Comment to post when marking a pull request as stale. Set to `false` to disable
markComment: >
This pull request has been automatically marked as stale because it has not had This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. recent activity. It will be closed if no further activity occurs.
# Comment to post when closing a stale pull request. Set to `false` to disable # Comment to post when closing a stale pull request. Set to `false` to disable
closeComment: > closeComment: >
This stale pull request has been automatically closed. This stale pull request has been automatically closed.
Thank you for your contributions. Thank you for your contributions.
issues:
# Number of days of inactivity before a issue becomes stale
daysUntilStale: 365
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Comment to post when marking a issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This stale issue has been automatically closed.
Thank you for your contributions.

2
.gitignore vendored
View File

@@ -14,8 +14,6 @@
/config/ /config/
.cache
# log files # log files
*.log *.log
log-*.txt log-*.txt

View File

@@ -50,6 +50,22 @@ pull_request_rules:
label: label:
add: add:
- automerge - automerge
- name: v1.1 backport
conditions:
- label=v1.1
actions:
backport:
ignore_conflicts: true
branches:
- v1.1
- name: v1.2 backport
conditions:
- label=v1.2
actions:
backport:
ignore_conflicts: true
branches:
- v1.2
- name: v1.3 backport - name: v1.3 backport
conditions: conditions:
- label=v1.3 - label=v1.3
@@ -58,27 +74,3 @@ pull_request_rules:
ignore_conflicts: true ignore_conflicts: true
branches: branches:
- v1.3 - v1.3
- name: v1.4 backport
conditions:
- label=v1.4
actions:
backport:
ignore_conflicts: true
branches:
- v1.4
- name: v1.5 backport
conditions:
- label=v1.5
actions:
backport:
ignore_conflicts: true
branches:
- v1.5
- name: v1.6 backport
conditions:
- label=v1.6
actions:
backport:
ignore_conflicts: true
branches:
- v1.6

View File

@@ -34,8 +34,6 @@ jobs:
- stable - stable
install: install:
- source ci/rust-version.sh - source ci/rust-version.sh
- PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
- readlink -f .
script: script:
- source ci/env.sh - source ci/env.sh
- ci/publish-tarball.sh - ci/publish-tarball.sh
@@ -126,8 +124,6 @@ jobs:
- ~/.npm - ~/.npm
before_install: before_install:
- source ci/env.sh
- .travis/channel_restriction.sh edge beta || travis_terminate 0
- .travis/affects.sh docs/ .travis || travis_terminate 0 - .travis/affects.sh docs/ .travis || travis_terminate 0
- cd docs/ - cd docs/
- source .travis/before_install.sh - source .travis/before_install.sh

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
#
# Only proceed if we are on one of the channels passed in, or a tag build
#
set -ex
[[ -n $CI_TAG ]] && exit 0
eval "$(ci/channel-info.sh)"
for acceptable_channel in "$@"; do
if [[ "$CHANNEL" == "$acceptable_channel" ]]; then
exit 0
fi
done
echo "Not running from one of the following channels: $*"
exit 1

View File

@@ -232,7 +232,7 @@ confused with 3-letter acronyms.
Solana's architecture is described by docs generated from markdown files in Solana's architecture is described by docs generated from markdown files in
the `docs/src/` directory, maintained by an *editor* (currently @garious). To the `docs/src/` directory, maintained by an *editor* (currently @garious). To
add a design proposal, you'll need to include it in the add a design proposal, you'll need to include it in the
[Accepted Design Proposals](https://docs.solana.com/proposals/accepted-design-proposals) [Accepted Design Proposals](https://docs.solana.com/proposals)
section of the Solana docs. Here's the full process: section of the Solana docs. Here's the full process:
1. Propose a design by creating a PR that adds a markdown document to the 1. Propose a design by creating a PR that adds a markdown document to the

2199
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,16 +10,15 @@ members = [
"banks-server", "banks-server",
"clap-utils", "clap-utils",
"cli-config", "cli-config",
"cli-output",
"client", "client",
"core", "core",
"dos", "dos",
"download-utils", "download-utils",
"faucet", "faucet",
"frozen-abi",
"perf", "perf",
"validator", "validator",
"genesis", "genesis",
"genesis-programs",
"gossip", "gossip",
"install", "install",
"keygen", "keygen",
@@ -31,15 +30,12 @@ members = [
"merkle-tree", "merkle-tree",
"stake-o-matic", "stake-o-matic",
"storage-bigtable", "storage-bigtable",
"storage-proto",
"streamer", "streamer",
"measure", "measure",
"metrics", "metrics",
"net-shaper", "net-shaper",
"notifier", "notifier",
"poh-bench", "poh-bench",
"program-test",
"programs/secp256k1",
"programs/bpf_loader", "programs/bpf_loader",
"programs/budget", "programs/budget",
"programs/config", "programs/config",
@@ -53,10 +49,7 @@ members = [
"remote-wallet", "remote-wallet",
"ramp-tps", "ramp-tps",
"runtime", "runtime",
"runtime/store-tool",
"sdk", "sdk",
"sdk/cargo-build-bpf",
"sdk/cargo-test-bpf",
"scripts", "scripts",
"stake-accounts", "stake-accounts",
"stake-monitor", "stake-monitor",
@@ -67,6 +60,7 @@ members = [
"upload-perf", "upload-perf",
"net-utils", "net-utils",
"version", "version",
"vote-signer",
"cli", "cli",
"rayon-threadlimit", "rayon-threadlimit",
"watchtower", "watchtower",

View File

@@ -19,7 +19,7 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt $ rustup component add rustfmt
``` ```
Please sure you are always using the latest stable rust version by running: If your rustc version is lower than 1.39.0, please update it:
```bash ```bash
$ rustup update $ rustup update
@@ -29,7 +29,7 @@ On Linux systems you may need to install libssl-dev, pkg-config, zlib1g-dev, etc
```bash ```bash
$ sudo apt-get update $ sudo apt-get update
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang make $ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang
``` ```
## **2. Download the source code.** ## **2. Download the source code.**
@@ -59,11 +59,10 @@ $ cargo test
``` ```
### Starting a local testnet ### Starting a local testnet
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/cluster/bench-tps). Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/bench-tps).
### Accessing the remote development cluster ### Accessing the remote testnet
* `devnet` - stable public cluster for development accessible via * `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
devnet.solana.com. Runs 24/7. Learn more about the [public clusters](https://docs.solana.com/clusters)
# Benchmarking # Benchmarking
@@ -108,5 +107,3 @@ send us that patch!
# Disclaimer # Disclaimer
All claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the author's best effort. It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment. All claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the author's best effort. It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment.
Any content produced by Solana, or developer resources that Solana provides, are for educational and inspiration purposes only. Solana does not encourage, induce or sanction the deployment of any such applications in violation of applicable laws or regulations.

View File

@@ -107,15 +107,11 @@ Alternatively use the Github UI.
1. If the Cargo.toml version field is **0.12.3**, then the release tag must be **v0.12.3** 1. If the Cargo.toml version field is **0.12.3**, then the release tag must be **v0.12.3**
1. Make sure the Target Branch field matches the branch you want to make a release on. 1. Make sure the Target Branch field matches the branch you want to make a release on.
1. If you want to release v0.12.0, the target branch must be v0.12 1. If you want to release v0.12.0, the target branch must be v0.12
1. Fill the release notes. 1. If this is the first release on the branch (e.g. v0.13.**0**), paste in [this
1. If this is the first release on the branch (e.g. v0.13.**0**), paste in [this template](https://raw.githubusercontent.com/solana-labs/solana/master/.github/RELEASE_TEMPLATE.md). Engineering Lead can provide summary contents for release notes if needed. If this is a patch release, review all the commits since the previous release on this branch and add details as needed.
template](https://raw.githubusercontent.com/solana-labs/solana/master/.github/RELEASE_TEMPLATE.md). Engineering Lead can provide summary contents for release notes if needed.
1. If this is a patch release, review all the commits since the previous release on this branch and add details as needed.
1. Click "Save Draft", then confirm the release notes look good and the tag name and branch are correct. 1. Click "Save Draft", then confirm the release notes look good and the tag name and branch are correct.
1. Ensure all desired commits (usually backports) are landed on the branch by now. 1. Ensure the release is marked **"This is a pre-release"**. This flag will then need to be be removed once the the Linux binary artifacts appear later.
1. Ensure the release is marked **"This is a pre-release"**. This flag will need to be be removed manually after confirming the the Linux binary artifacts appear at a later step. 1. Go back into edit the release and click "Publish release" when ready.
1. Go back into edit the release and click "Publish release" while being marked as a pre-release.
1. Confirm there is new git tag with intended version number at the intended revision after running `git fetch` locally.
### Update release branch with the next patch version ### Update release branch with the next patch version
@@ -135,8 +131,7 @@ Alternatively use the Github UI.
1. Open a PR against origin/vX.Y and then merge the PR after passing CI. 1. Open a PR against origin/vX.Y and then merge the PR after passing CI.
### Prepare for the next release ### Prepare for the next release
1. Go to [GitHub Releases](https://github.com/solana-labs/solana/releases) and create a new draft release for `X.Y.Z+1` with empty release notes. This allows people to incrementally add new release notes until it's time for the next release 1. Go to [GitHub Releases](https://github.com/solana-labs/solana/releases) and create a new draft release for `X.Y.Z+1` with empty release nodes. This allows people to incrementally add new release notes until it's time for the next release
1. Also, point the branch field to the same branch and mark the relese as **"This is a pre-release"**.
1. Go to the [Github Milestones](https://github.com/solana-labs/solana/milestones). Create a new milestone for the `X.Y.Z+1`, move over 1. Go to the [Github Milestones](https://github.com/solana-labs/solana/milestones). Create a new milestone for the `X.Y.Z+1`, move over
unresolved issues still in the `X.Y.Z` milestone, then close the `X.Y.Z` milestone. unresolved issues still in the `X.Y.Z` milestone, then close the `X.Y.Z` milestone.
@@ -152,5 +147,5 @@ appearing. To check for progress:
[Crates.io](https://crates.io/crates/solana) should have an updated Solana version. This can take 2-3 hours, and sometimes fails in the `solana-secondary` job. [Crates.io](https://crates.io/crates/solana) should have an updated Solana version. This can take 2-3 hours, and sometimes fails in the `solana-secondary` job.
If this happens and the error is non-fatal, click "Retry" on the "publish crate" job If this happens and the error is non-fatal, click "Retry" on the "publish crate" job
### Update software on devnet.solana.com/testnet.solana.com/mainnet-beta.solana.com ### Update software on devnet.solana.com/testnet.solama.com/mainnet-beta.solana.com
See the documentation at https://github.com/solana-labs/cluster-ops/ See the documentation at https://github.com/solana-labs/cluster-ops/

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-account-decoder" name = "solana-account-decoder"
version = "1.5.1" version = "1.3.6"
description = "Solana account decoder" description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -18,13 +18,12 @@ lazy_static = "1.4.0"
serde = "1.0.112" serde = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.56" serde_json = "1.0.56"
solana-config-program = { path = "../programs/config", version = "1.5.1" } solana-config-program = { path = "../programs/config", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-stake-program = { path = "../programs/stake", version = "1.5.1" } solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
solana-vote-program = { path = "../programs/vote", version = "1.5.1" } solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
spl-token-v2-0 = { package = "spl-token", version = "=3.0.1", features = ["no-entrypoint"] } spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
thiserror = "1.0" thiserror = "1.0"
zstd = "0.5.1"
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -12,14 +12,9 @@ pub mod parse_token;
pub mod parse_vote; pub mod parse_vote;
pub mod validator_info; pub mod validator_info;
use { use crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount};
crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount}, use solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey};
solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey}, use std::str::FromStr;
std::{
io::{Read, Write},
str::FromStr,
},
};
pub type StringAmount = String; pub type StringAmount = String;
@@ -49,8 +44,6 @@ pub enum UiAccountEncoding {
Base58, Base58,
Base64, Base64,
JsonParsed, JsonParsed,
#[serde(rename = "base64+zstd")]
Base64Zstd,
} }
impl UiAccount { impl UiAccount {
@@ -73,19 +66,6 @@ impl UiAccount {
base64::encode(slice_data(&account.data, data_slice_config)), base64::encode(slice_data(&account.data, data_slice_config)),
encoding, 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))
.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)),
UiAccountEncoding::Base64,
),
}
}
UiAccountEncoding::JsonParsed => { UiAccountEncoding::JsonParsed => {
if let Ok(parsed_data) = 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)
@@ -112,16 +92,6 @@ impl UiAccount {
UiAccountData::Binary(blob, encoding) => match encoding { UiAccountData::Binary(blob, encoding) => match encoding {
UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(), UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(),
UiAccountEncoding::Base64 => base64::decode(blob).ok(), UiAccountEncoding::Base64 => base64::decode(blob).ok(),
UiAccountEncoding::Base64Zstd => base64::decode(blob)
.ok()
.map(|zstd_data| {
let mut data = vec![];
zstd::stream::read::Decoder::new(zstd_data.as_slice())
.and_then(|mut reader| reader.read_to_end(&mut data))
.map(|_| data)
.ok()
})
.flatten(),
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None, UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
}, },
}?; }?;
@@ -209,25 +179,4 @@ mod test {
}); });
assert_eq!(slice_data(&data, slice_config), &[] as &[u8]); assert_eq!(slice_data(&data, slice_config), &[] as &[u8]);
} }
#[test]
fn test_base64_zstd() {
let encoded_account = UiAccount::encode(
&Pubkey::default(),
Account {
data: vec![0; 1024],
..Account::default()
},
UiAccountEncoding::Base64Zstd,
None,
None,
);
assert!(matches!(
encoded_account.data,
UiAccountData::Binary(_, UiAccountEncoding::Base64Zstd)
));
let decoded_account = encoded_account.decode().unwrap();
assert_eq!(decoded_account.data, vec![0; 1024]);
}
} }

View File

@@ -81,7 +81,7 @@ pub fn parse_account_data(
) -> Result<ParsedAccount, ParseAccountError> { ) -> Result<ParsedAccount, ParseAccountError> {
let program_name = PARSABLE_PROGRAM_IDS let program_name = PARSABLE_PROGRAM_IDS
.get(program_id) .get(program_id)
.ok_or(ParseAccountError::ProgramNotParsable)?; .ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
let additional_data = additional_data.unwrap_or_default(); let additional_data = additional_data.unwrap_or_default();
let parsed_json = match program_name { let parsed_json = match program_name {
ParsableAccount::Config => serde_json::to_value(parse_config(data, pubkey)?)?, ParsableAccount::Config => serde_json::to_value(parse_config(data, pubkey)?)?,
@@ -111,14 +111,14 @@ mod test {
#[test] #[test]
fn test_parse_account_data() { fn test_parse_account_data() {
let account_pubkey = solana_sdk::pubkey::new_rand(); let account_pubkey = Pubkey::new_rand();
let other_program = solana_sdk::pubkey::new_rand(); let other_program = Pubkey::new_rand();
let data = vec![0; 4]; let data = vec![0; 4];
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err()); assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
let vote_state = VoteState::default(); let vote_state = VoteState::default();
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()]; let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
let versioned = VoteStateVersions::new_current(vote_state); let versioned = VoteStateVersions::Current(Box::new(vote_state));
VoteState::serialize(&versioned, &mut vote_account_data).unwrap(); VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
let parsed = parse_account_data( let parsed = parse_account_data(
&account_pubkey, &account_pubkey,

View File

@@ -117,7 +117,7 @@ mod test {
})) }))
.unwrap(), .unwrap(),
}; };
let info_pubkey = solana_sdk::pubkey::new_rand(); let info_pubkey = Pubkey::new_rand();
let validator_info_config_account = create_config_account( let validator_info_config_account = create_config_account(
vec![(validator_info::id(), false), (info_pubkey, true)], vec![(validator_info::id(), false), (info_pubkey, true)],
&validator_info, &validator_info,

View File

@@ -134,6 +134,7 @@ impl From<Delegation> for UiDelegation {
mod test { mod test {
use super::*; use super::*;
use bincode::serialize; use bincode::serialize;
use solana_sdk::pubkey::Pubkey;
#[test] #[test]
fn test_parse_stake() { fn test_parse_stake() {
@@ -144,8 +145,8 @@ mod test {
StakeAccountType::Uninitialized StakeAccountType::Uninitialized
); );
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let custodian = solana_sdk::pubkey::new_rand(); let custodian = Pubkey::new_rand();
let authorized = Authorized::auto(&pubkey); let authorized = Authorized::auto(&pubkey);
let lockup = Lockup { let lockup = Lockup {
unix_timestamp: 0, unix_timestamp: 0,
@@ -179,7 +180,7 @@ mod test {
}) })
); );
let voter_pubkey = solana_sdk::pubkey::new_rand(); let voter_pubkey = Pubkey::new_rand();
let stake = Stake { let stake = Stake {
delegation: Delegation { delegation: Delegation {
voter_pubkey, voter_pubkey,

View File

@@ -105,7 +105,6 @@ pub enum SysvarAccountType {
pub struct UiClock { pub struct UiClock {
pub slot: Slot, pub slot: Slot,
pub epoch: Epoch, pub epoch: Epoch,
pub epoch_start_timestamp: UnixTimestamp,
pub leader_schedule_epoch: Epoch, pub leader_schedule_epoch: Epoch,
pub unix_timestamp: UnixTimestamp, pub unix_timestamp: UnixTimestamp,
} }
@@ -115,7 +114,6 @@ impl From<Clock> for UiClock {
Self { Self {
slot: clock.slot, slot: clock.slot,
epoch: clock.epoch, epoch: clock.epoch,
epoch_start_timestamp: clock.epoch_start_timestamp,
leader_schedule_epoch: clock.leader_schedule_epoch, leader_schedule_epoch: clock.leader_schedule_epoch,
unix_timestamp: clock.unix_timestamp, unix_timestamp: clock.unix_timestamp,
} }
@@ -214,13 +212,15 @@ pub struct UiStakeHistoryEntry {
mod test { mod test {
use super::*; use super::*;
use solana_sdk::{ use solana_sdk::{
account::create_account, fee_calculator::FeeCalculator, hash::Hash, fee_calculator::FeeCalculator,
sysvar::recent_blockhashes::IterItem, hash::Hash,
sysvar::{recent_blockhashes::IterItem, Sysvar},
}; };
use std::iter::FromIterator;
#[test] #[test]
fn test_parse_sysvars() { fn test_parse_sysvars() {
let clock_sysvar = create_account(&Clock::default(), 1); let clock_sysvar = Clock::default().create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(), parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
SysvarAccountType::Clock(UiClock::default()), SysvarAccountType::Clock(UiClock::default()),
@@ -233,13 +233,13 @@ mod test {
first_normal_epoch: 1, first_normal_epoch: 1,
first_normal_slot: 12, first_normal_slot: 12,
}; };
let epoch_schedule_sysvar = create_account(&epoch_schedule, 1); let epoch_schedule_sysvar = epoch_schedule.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(), parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
SysvarAccountType::EpochSchedule(epoch_schedule), SysvarAccountType::EpochSchedule(epoch_schedule),
); );
let fees_sysvar = create_account(&Fees::default(), 1); let fees_sysvar = Fees::default().create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(), parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
SysvarAccountType::Fees(UiFees::default()), SysvarAccountType::Fees(UiFees::default()),
@@ -249,10 +249,9 @@ mod test {
let fee_calculator = FeeCalculator { let fee_calculator = FeeCalculator {
lamports_per_signature: 10, lamports_per_signature: 10,
}; };
let recent_blockhashes: RecentBlockhashes = vec![IterItem(0, &hash, &fee_calculator)] let recent_blockhashes =
.into_iter() RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter());
.collect(); let recent_blockhashes_sysvar = recent_blockhashes.create_account(1);
let recent_blockhashes_sysvar = create_account(&recent_blockhashes, 1);
assert_eq!( assert_eq!(
parse_sysvar( parse_sysvar(
&recent_blockhashes_sysvar.data, &recent_blockhashes_sysvar.data,
@@ -270,13 +269,13 @@ mod test {
exemption_threshold: 2.0, exemption_threshold: 2.0,
burn_percent: 5, burn_percent: 5,
}; };
let rent_sysvar = create_account(&rent, 1); let rent_sysvar = rent.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(), parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
SysvarAccountType::Rent(rent.into()), SysvarAccountType::Rent(rent.into()),
); );
let rewards_sysvar = create_account(&Rewards::default(), 1); let rewards_sysvar = Rewards::default().create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(), parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
SysvarAccountType::Rewards(UiRewards::default()), SysvarAccountType::Rewards(UiRewards::default()),
@@ -284,7 +283,7 @@ mod test {
let mut slot_hashes = SlotHashes::default(); let mut slot_hashes = SlotHashes::default();
slot_hashes.add(1, hash); slot_hashes.add(1, hash);
let slot_hashes_sysvar = create_account(&slot_hashes, 1); let slot_hashes_sysvar = slot_hashes.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(), parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry { SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
@@ -295,7 +294,7 @@ mod test {
let mut slot_history = SlotHistory::default(); let mut slot_history = SlotHistory::default();
slot_history.add(42); slot_history.add(42);
let slot_history_sysvar = create_account(&slot_history, 1); let slot_history_sysvar = slot_history.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(), parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
SysvarAccountType::SlotHistory(UiSlotHistory { SysvarAccountType::SlotHistory(UiSlotHistory {
@@ -311,7 +310,7 @@ mod test {
deactivating: 3, deactivating: 3,
}; };
stake_history.add(1, stake_history_entry.clone()); stake_history.add(1, stake_history_entry.clone());
let stake_history_sysvar = create_account(&stake_history, 1); let stake_history_sysvar = stake_history.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(), parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry { SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
@@ -320,7 +319,7 @@ mod test {
}]), }]),
); );
let bad_pubkey = solana_sdk::pubkey::new_rand(); let bad_pubkey = Pubkey::new_rand();
assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err()); assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
let bad_data = vec![0; 4]; let bad_data = vec![0; 4];

View File

@@ -4,9 +4,9 @@ use crate::{
}; };
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use spl_token_v2_0::{ use spl_token_v2_0::{
solana_program::{ option::COption,
program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey, pack::Pack,
}, solana_sdk::pubkey::Pubkey as SplTokenPubkey,
state::{Account, AccountState, Mint, Multisig}, state::{Account, AccountState, Mint, Multisig},
}; };
use std::str::FromStr; use std::str::FromStr;
@@ -23,16 +23,6 @@ pub fn spl_token_v2_0_native_mint() -> Pubkey {
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap() 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::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::from_str(&pubkey.to_string()).unwrap()
}
pub fn parse_token( pub fn parse_token(
data: &[u8], data: &[u8],
mint_decimals: Option<u8>, mint_decimals: Option<u8>,
@@ -166,31 +156,6 @@ pub struct UiTokenAmount {
pub amount: StringAmount, pub amount: StringAmount,
} }
impl UiTokenAmount {
pub fn real_number_string(&self) -> String {
let decimals = self.decimals as usize;
if decimals > 0 {
let amount = u64::from_str(&self.amount).unwrap_or(0);
// Left-pad zeros to decimals + 1, so we at least have an integer zero
let mut s = format!("{:01$}", amount, decimals + 1);
// Add the decimal point (Sorry, "," locales!)
s.insert(s.len() - decimals, '.');
s
} else {
self.amount.clone()
}
}
pub fn real_number_string_trimmed(&self) -> String {
let s = self.real_number_string();
let zeros_trimmed = s.trim_end_matches('0');
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
decimal_trimmed.to_string()
}
}
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount { pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
// Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211 // Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211
let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64; let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64;
@@ -237,14 +202,16 @@ mod test {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]); let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let owner_pubkey = SplTokenPubkey::new(&[3; 32]); let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
let mut account_data = vec![0; Account::get_packed_len()]; let mut account_data = vec![0; Account::get_packed_len()];
let mut account = Account::unpack_unchecked(&account_data).unwrap(); Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
account.mint = mint_pubkey; account.mint = mint_pubkey;
account.owner = owner_pubkey; account.owner = owner_pubkey;
account.amount = 42; account.amount = 42;
account.state = AccountState::Initialized; account.state = AccountState::Initialized;
account.is_native = COption::None; account.is_native = COption::None;
account.close_authority = COption::Some(owner_pubkey); account.close_authority = COption::Some(owner_pubkey);
Account::pack(account, &mut account_data).unwrap(); Ok(())
})
.unwrap();
assert!(parse_token(&account_data, None).is_err()); assert!(parse_token(&account_data, None).is_err());
assert_eq!( assert_eq!(
@@ -267,13 +234,15 @@ mod test {
); );
let mut mint_data = vec![0; Mint::get_packed_len()]; let mut mint_data = vec![0; Mint::get_packed_len()];
let mut mint = Mint::unpack_unchecked(&mint_data).unwrap(); Mint::unpack_unchecked_mut(&mut mint_data, &mut |mint: &mut Mint| {
mint.mint_authority = COption::Some(owner_pubkey); mint.mint_authority = COption::Some(owner_pubkey);
mint.supply = 42; mint.supply = 42;
mint.decimals = 3; mint.decimals = 3;
mint.is_initialized = true; mint.is_initialized = true;
mint.freeze_authority = COption::Some(owner_pubkey); mint.freeze_authority = COption::Some(owner_pubkey);
Mint::pack(mint, &mut mint_data).unwrap(); Ok(())
})
.unwrap();
assert_eq!( assert_eq!(
parse_token(&mint_data, None).unwrap(), parse_token(&mint_data, None).unwrap(),
@@ -294,13 +263,14 @@ mod test {
signers[0] = signer1; signers[0] = signer1;
signers[1] = signer2; signers[1] = signer2;
signers[2] = signer3; signers[2] = signer3;
let mut multisig = Multisig::unpack_unchecked(&multisig_data).unwrap(); Multisig::unpack_unchecked_mut(&mut multisig_data, &mut |multisig: &mut Multisig| {
multisig.m = 2; multisig.m = 2;
multisig.n = 3; multisig.n = 3;
multisig.is_initialized = true; multisig.is_initialized = true;
multisig.signers = signers; multisig.signers = signers;
Multisig::pack(multisig, &mut multisig_data).unwrap(); Ok(())
})
.unwrap();
assert_eq!( assert_eq!(
parse_token(&multisig_data, None).unwrap(), parse_token(&multisig_data, None).unwrap(),
TokenAccountType::Multisig(UiMultisig { TokenAccountType::Multisig(UiMultisig {
@@ -323,9 +293,11 @@ mod test {
fn test_get_token_account_mint() { fn test_get_token_account_mint() {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]); let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let mut account_data = vec![0; Account::get_packed_len()]; let mut account_data = vec![0; Account::get_packed_len()];
let mut account = Account::unpack_unchecked(&account_data).unwrap(); Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
account.mint = mint_pubkey; account.mint = mint_pubkey;
Account::pack(account, &mut account_data).unwrap(); Ok(())
})
.unwrap();
let expected_mint_pubkey = Pubkey::new(&[2; 32]); let expected_mint_pubkey = Pubkey::new(&[2; 32]);
assert_eq!( assert_eq!(
@@ -333,20 +305,4 @@ mod test {
Some(expected_mint_pubkey) Some(expected_mint_pubkey)
); );
} }
#[test]
fn test_ui_token_amount_real_string() {
let token_amount = token_amount_to_ui_amount(1, 0);
assert_eq!(&token_amount.real_number_string(), "1");
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
let token_amount = token_amount_to_ui_amount(1, 9);
assert_eq!(&token_amount.real_number_string(), "0.000000001");
assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001");
let token_amount = token_amount_to_ui_amount(1_000_000_000, 9);
assert_eq!(&token_amount.real_number_string(), "1.000000000");
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
let token_amount = token_amount_to_ui_amount(1_234_567_890, 3);
assert_eq!(&token_amount.real_number_string(), "1234567.890");
assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89");
}
} }

View File

@@ -128,13 +128,11 @@ mod test {
fn test_parse_vote() { fn test_parse_vote() {
let vote_state = VoteState::default(); let vote_state = VoteState::default();
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()]; let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
let versioned = VoteStateVersions::new_current(vote_state); let versioned = VoteStateVersions::Current(Box::new(vote_state));
VoteState::serialize(&versioned, &mut vote_account_data).unwrap(); VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
let expected_vote_state = UiVoteState { let mut expected_vote_state = UiVoteState::default();
node_pubkey: Pubkey::default().to_string(), expected_vote_state.node_pubkey = Pubkey::default().to_string();
authorized_withdrawer: Pubkey::default().to_string(), expected_vote_state.authorized_withdrawer = Pubkey::default().to_string();
..UiVoteState::default()
};
assert_eq!( assert_eq!(
parse_vote(&vote_account_data).unwrap(), parse_vote(&vote_account_data).unwrap(),
VoteAccountType::Vote(expected_vote_state) VoteAccountType::Vote(expected_vote_state)

View File

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

View File

@@ -1,21 +1,20 @@
use clap::{crate_description, crate_name, value_t, App, Arg}; use clap::{value_t, App, Arg};
use rayon::prelude::*; use rayon::prelude::*;
use solana_measure::measure::Measure; use solana_measure::measure::Measure;
use solana_runtime::{ use solana_runtime::{
accounts::{create_test_accounts, update_accounts, Accounts}, accounts::{create_test_accounts, update_accounts, Accounts},
accounts_index::Ancestors, accounts_index::Ancestors,
}; };
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey}; use solana_sdk::pubkey::Pubkey;
use std::env;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
fn main() { fn main() {
solana_logger::setup(); solana_logger::setup();
let matches = App::new(crate_name!()) let matches = App::new("crate")
.about(crate_description!()) .about("about")
.version(solana_version::version!()) .version("version")
.arg( .arg(
Arg::with_name("num_slots") Arg::with_name("num_slots")
.long("num_slots") .long("num_slots")
@@ -51,12 +50,11 @@ fn main() {
let clean = matches.is_present("clean"); let clean = matches.is_present("clean");
println!("clean: {:?}", clean); println!("clean: {:?}", clean);
let path = PathBuf::from(env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_owned())) let path = PathBuf::from("farf/accounts-bench");
.join("accounts-bench");
if fs::remove_dir_all(path.clone()).is_err() { if fs::remove_dir_all(path.clone()).is_err() {
println!("Warning: Couldn't remove {:?}", path); println!("Warning: Couldn't remove {:?}", path);
} }
let accounts = Accounts::new(vec![path], &ClusterType::Testnet); let accounts = Accounts::new(vec![path]);
println!("Creating {} accounts", num_accounts); println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts"); let mut create_time = Measure::start("create accounts");
let pubkeys: Vec<_> = (0..num_slots) let pubkeys: Vec<_> = (0..num_slots)
@@ -88,7 +86,7 @@ fn main() {
for x in 0..iterations { for x in 0..iterations {
if clean { if clean {
let mut time = Measure::start("clean"); let mut time = Measure::start("clean");
accounts.accounts_db.clean_accounts(None); accounts.accounts_db.clean_accounts();
time.stop(); time.stop();
println!("{}", time); println!("{}", time);
for slot in 0..num_slots { for slot in 0..num_slots {
@@ -98,10 +96,7 @@ fn main() {
} else { } else {
let mut pubkeys: Vec<Pubkey> = vec![]; let mut pubkeys: Vec<Pubkey> = vec![];
let mut time = Measure::start("hash"); let mut time = Measure::start("hash");
let hash = accounts let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors);
.accounts_db
.update_accounts_hash(0, &ancestors, true)
.0;
time.stop(); time.stop();
println!("hash: {} {}", hash, time); println!("hash: {} {}", hash, time);
create_test_accounts(&accounts, &mut pubkeys, 1, 0); create_test_accounts(&accounts, &mut pubkeys, 1, 0);

View File

@@ -2,28 +2,27 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-banking-bench" name = "solana-banking-bench"
version = "1.5.1" version = "1.3.6"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
publish = false
[dependencies] [dependencies]
clap = "2.33.1" clap = "2.33.1"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
log = "0.4.11" log = "0.4.6"
rand = "0.7.0" rand = "0.7.0"
rayon = "1.4.0" rayon = "1.3.1"
solana-core = { path = "../core", version = "1.5.1" } solana-core = { path = "../core", version = "1.3.6" }
solana-clap-utils = { path = "../clap-utils", version = "1.5.1" } solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
solana-streamer = { path = "../streamer", version = "1.5.1" } solana-streamer = { path = "../streamer", version = "1.3.6" }
solana-perf = { path = "../perf", version = "1.5.1" } solana-perf = { path = "../perf", version = "1.3.6" }
solana-ledger = { path = "../ledger", version = "1.5.1" } solana-ledger = { path = "../ledger", version = "1.3.6" }
solana-logger = { path = "../logger", version = "1.5.1" } solana-logger = { path = "../logger", version = "1.3.6" }
solana-runtime = { path = "../runtime", version = "1.5.1" } solana-runtime = { path = "../runtime", version = "1.3.6" }
solana-measure = { path = "../measure", version = "1.5.1" } solana-measure = { path = "../measure", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-version = { path = "../version", version = "1.5.1" } solana-version = { path = "../version", version = "1.3.6" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -17,11 +17,10 @@ use solana_ledger::{
}; };
use solana_measure::measure::Measure; use solana_measure::measure::Measure;
use solana_perf::packet::to_packets_chunked; use solana_perf::packet::to_packets_chunked;
use solana_runtime::{ use solana_runtime::{bank::Bank, bank_forks::BankForks};
accounts_background_service::ABSRequestSender, bank::Bank, bank_forks::BankForks,
};
use solana_sdk::{ use solana_sdk::{
hash::Hash, hash::Hash,
pubkey::Pubkey,
signature::Keypair, signature::Keypair,
signature::Signature, signature::Signature,
system_transaction, system_transaction,
@@ -70,7 +69,7 @@ fn make_accounts_txs(
hash: Hash, hash: Hash,
same_payer: bool, same_payer: bool,
) -> Vec<Transaction> { ) -> Vec<Transaction> {
let to_pubkey = solana_sdk::pubkey::new_rand(); let to_pubkey = Pubkey::new_rand();
let payer_key = Keypair::new(); let payer_key = Keypair::new();
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash); let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
(0..total_num_transactions) (0..total_num_transactions)
@@ -79,9 +78,9 @@ fn make_accounts_txs(
let mut new = dummy.clone(); let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect(); let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
if !same_payer { if !same_payer {
new.message.account_keys[0] = solana_sdk::pubkey::new_rand(); new.message.account_keys[0] = Pubkey::new_rand();
} }
new.message.account_keys[1] = solana_sdk::pubkey::new_rand(); new.message.account_keys[1] = Pubkey::new_rand();
new.signatures = vec![Signature::new(&sig[0..64])]; new.signatures = vec![Signature::new(&sig[0..64])];
new new
}) })
@@ -242,7 +241,7 @@ fn main() {
let base_tx_count = bank.transaction_count(); let base_tx_count = bank.transaction_count();
let mut txs_processed = 0; let mut txs_processed = 0;
let mut root = 1; let mut root = 1;
let collector = solana_sdk::pubkey::new_rand(); let collector = Pubkey::new_rand();
let config = Config { let config = Config {
packets_per_batch: packets_per_chunk, packets_per_batch: packets_per_chunk,
chunk_len, chunk_len,
@@ -325,7 +324,7 @@ fn main() {
poh_recorder.lock().unwrap().set_bank(&bank); poh_recorder.lock().unwrap().set_bank(&bank);
assert!(poh_recorder.lock().unwrap().bank().is_some()); assert!(poh_recorder.lock().unwrap().bank().is_some());
if bank.slot() > 32 { if bank.slot() > 32 {
bank_forks.set_root(root, &ABSRequestSender::default(), None); bank_forks.set_root(root, &None, None);
root += 1; root += 1;
} }
debug!( debug!(

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-banks-client" name = "solana-banks-client"
version = "1.5.1" version = "1.3.6"
description = "Solana banks client" description = "Solana banks client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -9,18 +9,18 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
async-trait = "0.1.36"
bincode = "1.3.1" bincode = "1.3.1"
futures = "0.3" futures = "0.3"
mio = "0.7.6" solana-banks-interface = { path = "../banks-interface", version = "1.3.6" }
solana-banks-interface = { path = "../banks-interface", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } tarpc = { version = "0.21.0", features = ["full"] }
tarpc = { version = "0.23.0", features = ["full"] } tokio = "0.2"
tokio = { version = "0.3.5", features = ["full"] }
tokio-serde = { version = "0.6", features = ["bincode"] } tokio-serde = { version = "0.6", features = ["bincode"] }
[dev-dependencies] [dev-dependencies]
solana-runtime = { path = "../runtime", version = "1.5.1" } solana-runtime = { path = "../runtime", version = "1.3.6" }
solana-banks-server = { path = "../banks-server", version = "1.5.1" } solana-banks-server = { path = "../banks-server", version = "1.3.6" }
[lib] [lib]
crate-type = ["lib"] crate-type = ["lib"]

View File

@@ -5,250 +5,165 @@
//! but they are undocumented, may change over time, and are generally more //! but they are undocumented, may change over time, and are generally more
//! cumbersome to use. //! cumbersome to use.
use futures::{future::join_all, Future, FutureExt}; use async_trait::async_trait;
pub use solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus}; use futures::future::join_all;
pub use solana_banks_interface::{BanksClient, TransactionStatus};
use solana_banks_interface::{BanksRequest, BanksResponse}; use solana_banks_interface::{BanksRequest, BanksResponse};
use solana_sdk::{ use solana_sdk::{
account::{from_account, Account}, account::Account, clock::Slot, commitment_config::CommitmentLevel,
clock::Slot, fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, signature::Signature,
commitment_config::CommitmentLevel, transaction::Transaction, transport,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
rent::Rent,
signature::Signature,
sysvar,
transaction::{self, Transaction},
transport,
}; };
use std::io::{self, Error, ErrorKind}; use std::io::{self, Error, ErrorKind};
use tarpc::{ use tarpc::{
client::{self, channel::RequestDispatch, NewClient}, client, context,
context::{self, Context}, rpc::{transport::channel::UnboundedChannel, ClientMessage, Response},
rpc::{ClientMessage, Response},
serde_transport::tcp, serde_transport::tcp,
Transport,
}; };
use tokio::{net::ToSocketAddrs, time::Duration}; use tokio::{net::ToSocketAddrs, time::Duration};
use tokio_serde::formats::Bincode; use tokio_serde::formats::Bincode;
// This exists only for backward compatibility #[async_trait]
pub trait BanksClientExt {} pub trait BanksClientExt {
#[derive(Clone)]
pub struct BanksClient {
inner: TarpcClient,
}
impl BanksClient {
#[allow(clippy::new_ret_no_self)]
pub fn new<C>(
config: client::Config,
transport: C,
) -> NewClient<TarpcClient, RequestDispatch<BanksRequest, BanksResponse, C>>
where
C: Transport<ClientMessage<BanksRequest>, Response<BanksResponse>>,
{
TarpcClient::new(config, transport)
}
pub fn send_transaction_with_context(
&mut self,
ctx: Context,
transaction: Transaction,
) -> impl Future<Output = io::Result<()>> + '_ {
self.inner.send_transaction_with_context(ctx, transaction)
}
pub fn get_fees_with_commitment_and_context(
&mut self,
ctx: Context,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<(FeeCalculator, Hash, Slot)>> + '_ {
self.inner
.get_fees_with_commitment_and_context(ctx, commitment)
}
pub fn get_transaction_status_with_context(
&mut self,
ctx: Context,
signature: Signature,
) -> impl Future<Output = io::Result<Option<TransactionStatus>>> + '_ {
self.inner
.get_transaction_status_with_context(ctx, signature)
}
pub fn get_slot_with_context(
&mut self,
ctx: Context,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<Slot>> + '_ {
self.inner.get_slot_with_context(ctx, commitment)
}
pub fn process_transaction_with_commitment_and_context(
&mut self,
ctx: Context,
transaction: Transaction,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<Option<transaction::Result<()>>>> + '_ {
self.inner
.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
}
pub fn get_account_with_commitment_and_context(
&mut self,
ctx: Context,
address: Pubkey,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<Option<Account>>> + '_ {
self.inner
.get_account_with_commitment_and_context(ctx, address, commitment)
}
/// Send a transaction and return immediately. The server will resend the /// Send a transaction and return immediately. The server will resend the
/// transaction until either it is accepted by the cluster or the transaction's /// transaction until either it is accepted by the cluster or the transaction's
/// blockhash expires. /// blockhash expires.
pub fn send_transaction( async fn send_transaction(&mut self, transaction: Transaction) -> io::Result<()>;
&mut self,
transaction: Transaction,
) -> impl Future<Output = io::Result<()>> + '_ {
self.send_transaction_with_context(context::current(), transaction)
}
/// 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, Slot)>> + '_ {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::Root)
}
/// Return the cluster rent
pub fn get_rent(&mut self) -> impl Future<Output = io::Result<Rent>> + '_ {
self.get_account(sysvar::rent::id()).map(|result| {
let rent_sysvar = result?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Rent sysvar not present"))?;
from_account::<Rent>(&rent_sysvar).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
})
})
}
/// Return a recent, rooted blockhash from the server. The cluster will only accept /// Return a recent, rooted blockhash from the server. The cluster will only accept
/// transactions with a blockhash that has not yet expired. Use the `get_fees` /// transactions with a blockhash that has not yet expired. Use the `get_fees`
/// method to get both a blockhash and the blockhash's last valid slot. /// method to get both a blockhash and the blockhash's last valid slot.
pub fn get_recent_blockhash(&mut self) -> impl Future<Output = io::Result<Hash>> + '_ { async fn get_recent_blockhash(&mut self) -> io::Result<Hash>;
self.get_fees().map(|result| Ok(result?.1))
} /// 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.
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)>;
/// Send a transaction and return after the transaction has been rejected or /// Send a transaction and return after the transaction has been rejected or
/// reached the given level of commitment. /// reached the given level of commitment.
pub fn process_transaction_with_commitment( async fn process_transaction_with_commitment(
&mut self, &mut self,
transaction: Transaction, transaction: Transaction,
commitment: CommitmentLevel, commitment: CommitmentLevel,
) -> impl Future<Output = transport::Result<()>> + '_ { ) -> transport::Result<()>;
let mut ctx = context::current();
ctx.deadline += Duration::from_secs(50);
self.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
.map(|result| match result? {
None => {
Err(Error::new(ErrorKind::TimedOut, "invalid blockhash or fee-payer").into())
}
Some(transaction_result) => Ok(transaction_result?),
})
}
/// Send a transaction and return until the transaction has been finalized or rejected. /// Send a transaction and return after the transaction has been finalized or rejected.
pub fn process_transaction( async fn process_transaction(&mut self, transaction: Transaction) -> transport::Result<()>;
&mut self,
transaction: Transaction,
) -> impl Future<Output = transport::Result<()>> + '_ {
self.process_transaction_with_commitment(transaction, CommitmentLevel::default())
}
pub async fn process_transactions_with_commitment(
&mut self,
transactions: Vec<Transaction>,
commitment: CommitmentLevel,
) -> transport::Result<()> {
let mut clients: Vec<_> = transactions.iter().map(|_| self.clone()).collect();
let futures = clients
.iter_mut()
.zip(transactions)
.map(|(client, transaction)| {
client.process_transaction_with_commitment(transaction, commitment)
});
let statuses = join_all(futures).await;
statuses.into_iter().collect() // Convert Vec<Result<_, _>> to Result<Vec<_>>
}
/// Send transactions and return until the transaction has been finalized or rejected.
pub fn process_transactions(
&mut self,
transactions: Vec<Transaction>,
) -> impl Future<Output = transport::Result<()>> + '_ {
self.process_transactions_with_commitment(transactions, CommitmentLevel::default())
}
/// 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::Root)
}
/// 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(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<Option<Account>>> + '_ {
self.get_account_with_commitment_and_context(context::current(), address, commitment)
}
/// Return the account at the given address at the time of the most recent root slot.
/// If the account is not found, None is returned.
pub fn get_account(
&mut self,
address: Pubkey,
) -> impl Future<Output = io::Result<Option<Account>>> + '_ {
self.get_account_with_commitment(address, CommitmentLevel::default())
}
/// Return the balance in lamports of an account at the given address at the slot
/// corresponding to the given commitment level.
pub fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<u64>> + '_ {
self.get_account_with_commitment_and_context(context::current(), address, commitment)
.map(|result| Ok(result?.map(|x| x.lamports).unwrap_or(0)))
}
/// Return the balance in lamports of an account at the given address at the time
/// of the most recent root slot.
pub fn get_balance(&mut self, address: Pubkey) -> impl Future<Output = io::Result<u64>> + '_ {
self.get_balance_with_commitment(address, CommitmentLevel::default())
}
/// Return the status of a transaction with a signature matching the transaction's first /// Return the status of a transaction with a signature matching the transaction's first
/// signature. Return None if the transaction is not found, which may be because the /// signature. Return None if the transaction is not found, which may be because the
/// blockhash was expired or the fee-paying account had insufficient funds to pay the /// blockhash was expired or the fee-paying account had insufficient funds to pay the
/// transaction fee. Note that servers rarely store the full transaction history. This /// transaction fee. Note that servers rarely store the full transaction history. This
/// method may return None if the transaction status has been discarded. /// method may return None if the transaction status has been discarded.
pub fn get_transaction_status( async fn get_transaction_status(
&mut self, &mut self,
signature: Signature, signature: Signature,
) -> impl Future<Output = io::Result<Option<TransactionStatus>>> + '_ { ) -> io::Result<Option<TransactionStatus>>;
self.get_transaction_status_with_context(context::current(), signature)
}
/// Same as get_transaction_status, but for multiple transactions. /// Same as get_transaction_status, but for multiple transactions.
pub async fn get_transaction_statuses( async fn get_transaction_statuses(
&mut self,
signatures: Vec<Signature>,
) -> io::Result<Vec<Option<TransactionStatus>>>;
/// 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.
async fn get_root_slot(&mut self) -> io::Result<Slot>;
/// Return the account at the given address at the time of the most recent root slot.
/// If the account is not found, None is returned.
async fn get_account(&mut self, address: Pubkey) -> io::Result<Option<Account>>;
/// Return the balance in lamports of an account at the given address at the slot
/// corresponding to the given commitment level.
async fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<u64>;
/// Return the balance in lamports of an account at the given address at the time
/// of the most recent root slot.
async fn get_balance(&mut self, address: Pubkey) -> io::Result<u64>;
}
#[async_trait]
impl BanksClientExt for BanksClient {
async fn send_transaction(&mut self, transaction: Transaction) -> io::Result<()> {
self.send_transaction_with_context(context::current(), transaction)
.await
}
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)> {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::Root)
.await
}
async fn get_recent_blockhash(&mut self) -> io::Result<Hash> {
Ok(self.get_fees().await?.1)
}
async fn process_transaction_with_commitment(
&mut self,
transaction: Transaction,
commitment: CommitmentLevel,
) -> transport::Result<()> {
let mut ctx = context::current();
ctx.deadline += Duration::from_secs(50);
let result = self
.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
.await?;
match result {
None => Err(Error::new(ErrorKind::TimedOut, "invalid blockhash or fee-payer").into()),
Some(transaction_result) => Ok(transaction_result?),
}
}
async fn process_transaction(&mut self, transaction: Transaction) -> transport::Result<()> {
self.process_transaction_with_commitment(transaction, CommitmentLevel::default())
.await
}
async fn get_root_slot(&mut self) -> io::Result<Slot> {
self.get_slot_with_context(context::current(), CommitmentLevel::Root)
.await
}
async fn get_account(&mut self, address: Pubkey) -> io::Result<Option<Account>> {
self.get_account_with_commitment_and_context(
context::current(),
address,
CommitmentLevel::default(),
)
.await
}
async fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<u64> {
let account = self
.get_account_with_commitment_and_context(context::current(), address, commitment)
.await?;
Ok(account.map(|x| x.lamports).unwrap_or(0))
}
async fn get_balance(&mut self, address: Pubkey) -> io::Result<u64> {
self.get_balance_with_commitment(address, CommitmentLevel::default())
.await
}
async fn get_transaction_status(
&mut self,
signature: Signature,
) -> io::Result<Option<TransactionStatus>> {
self.get_transaction_status_with_context(context::current(), signature)
.await
}
async fn get_transaction_statuses(
&mut self, &mut self,
signatures: Vec<Signature>, signatures: Vec<Signature>,
) -> io::Result<Vec<Option<TransactionStatus>>> { ) -> io::Result<Vec<Option<TransactionStatus>>> {
@@ -269,20 +184,15 @@ impl BanksClient {
} }
} }
pub async fn start_client<C>(transport: C) -> io::Result<BanksClient> pub async fn start_client(
where transport: UnboundedChannel<Response<BanksResponse>, ClientMessage<BanksRequest>>,
C: Transport<ClientMessage<BanksRequest>, Response<BanksResponse>> + Send + 'static, ) -> io::Result<BanksClient> {
{ BanksClient::new(client::Config::default(), transport).spawn()
Ok(BanksClient {
inner: TarpcClient::new(client::Config::default(), transport).spawn()?,
})
} }
pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> io::Result<BanksClient> { pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> io::Result<BanksClient> {
let transport = tcp::connect(addr, Bincode::default).await?; let transport = tcp::connect(addr, Bincode::default()).await?;
Ok(BanksClient { BanksClient::new(client::Config::default(), transport).spawn()
inner: TarpcClient::new(client::Config::default(), transport).spawn()?,
})
} }
#[cfg(test)] #[cfg(test)]
@@ -290,10 +200,10 @@ mod tests {
use super::*; use super::*;
use solana_banks_server::banks_server::start_local_server; use solana_banks_server::banks_server::start_local_server;
use solana_runtime::{bank::Bank, bank_forks::BankForks, genesis_utils::create_genesis_config}; use solana_runtime::{bank::Bank, bank_forks::BankForks, genesis_utils::create_genesis_config};
use solana_sdk::{message::Message, signature::Signer, system_instruction}; use solana_sdk::{message::Message, pubkey::Pubkey, signature::Signer, system_instruction};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use tarpc::transport; use tarpc::transport;
use tokio::{runtime::Runtime, time::sleep}; use tokio::{runtime::Runtime, time::delay_for};
#[test] #[test]
fn test_banks_client_new() { fn test_banks_client_new() {
@@ -312,14 +222,15 @@ mod tests {
&genesis.genesis_config, &genesis.genesis_config,
)))); ))));
let bob_pubkey = solana_sdk::pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let mint_pubkey = genesis.mint_keypair.pubkey(); let mint_pubkey = genesis.mint_keypair.pubkey();
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1); let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey)); let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async { Runtime::new()?.block_on(async {
let client_transport = start_local_server(&bank_forks).await; let client_transport = start_local_server(&bank_forks).await;
let mut banks_client = start_client(client_transport).await?; let mut banks_client =
BanksClient::new(client::Config::default(), client_transport).spawn()?;
let recent_blockhash = banks_client.get_recent_blockhash().await?; let recent_blockhash = banks_client.get_recent_blockhash().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash); let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
@@ -341,13 +252,14 @@ mod tests {
)))); ))));
let mint_pubkey = &genesis.mint_keypair.pubkey(); let mint_pubkey = &genesis.mint_keypair.pubkey();
let bob_pubkey = solana_sdk::pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1); let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey)); let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async { Runtime::new()?.block_on(async {
let client_transport = start_local_server(&bank_forks).await; let client_transport = start_local_server(&bank_forks).await;
let mut banks_client = start_client(client_transport).await?; let mut banks_client =
BanksClient::new(client::Config::default(), client_transport).spawn()?;
let (_, recent_blockhash, last_valid_slot) = 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 transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
@@ -360,7 +272,7 @@ mod tests {
if root_slot > last_valid_slot { if root_slot > last_valid_slot {
break; break;
} }
sleep(Duration::from_millis(100)).await; delay_for(Duration::from_millis(100)).await;
status = banks_client.get_transaction_status(signature).await?; status = banks_client.get_transaction_status(signature).await?;
} }
assert!(status.unwrap().err.is_none()); assert!(status.unwrap().err.is_none());

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-banks-interface" name = "solana-banks-interface"
version = "1.5.1" version = "1.3.6"
description = "Solana banks RPC interface" description = "Solana banks RPC interface"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -9,13 +9,9 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
mio = "0.7.6"
serde = { version = "1.0.112", features = ["derive"] } serde = { version = "1.0.112", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
tarpc = { version = "0.23.0", features = ["full"] } tarpc = { version = "0.21.0", features = ["full"] }
[dev-dependencies]
tokio = { version = "0.3.5", features = ["full"] }
[lib] [lib]
crate-type = ["lib"] crate-type = ["lib"]

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-banks-server" name = "solana-banks-server"
version = "1.5.1" version = "1.3.6"
description = "Solana banks server" description = "Solana banks server"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -11,14 +11,11 @@ edition = "2018"
[dependencies] [dependencies]
bincode = "1.3.1" bincode = "1.3.1"
futures = "0.3" futures = "0.3"
log = "0.4.11" solana-banks-interface = { path = "../banks-interface", version = "1.3.6" }
mio = "0.7.6" solana-runtime = { path = "../runtime", version = "1.3.6" }
solana-banks-interface = { path = "../banks-interface", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-runtime = { path = "../runtime", version = "1.5.1" } tarpc = { version = "0.21.0", features = ["full"] }
solana-sdk = { path = "../sdk", version = "1.5.1" } tokio = "0.2"
solana-metrics = { path = "../metrics", version = "1.5.1" }
tarpc = { version = "0.23.0", features = ["full"] }
tokio = { version = "0.3", features = ["full"] }
tokio-serde = { version = "0.6", features = ["bincode"] } tokio-serde = { version = "0.6", features = ["bincode"] }
[lib] [lib]

View File

@@ -1,11 +1,15 @@
use crate::send_transaction_service::{SendTransactionService, TransactionInfo};
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use futures::{ use futures::{
future, future,
prelude::stream::{self, StreamExt}, prelude::stream::{self, StreamExt},
}; };
use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus}; use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus};
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache}; use solana_runtime::{
bank::Bank,
bank_forks::BankForks,
commitment::{BlockCommitmentCache, CommitmentSlots},
send_transaction_service::{SendTransactionService, TransactionInfo},
};
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
clock::Slot, clock::Slot,
@@ -17,9 +21,11 @@ use solana_sdk::{
transaction::{self, Transaction}, transaction::{self, Transaction},
}; };
use std::{ use std::{
collections::HashMap,
io, io,
net::{Ipv4Addr, SocketAddr}, net::SocketAddr,
sync::{ sync::{
atomic::AtomicBool,
mpsc::{channel, Receiver, Sender}, mpsc::{channel, Receiver, Sender},
Arc, RwLock, Arc, RwLock,
}, },
@@ -33,7 +39,7 @@ use tarpc::{
server::{self, Channel, Handler}, server::{self, Channel, Handler},
transport, transport,
}; };
use tokio::time::sleep; use tokio::time::delay_for;
use tokio_serde::formats::Bincode; use tokio_serde::formats::Bincode;
#[derive(Clone)] #[derive(Clone)]
@@ -79,9 +85,16 @@ impl BanksServer {
let (transaction_sender, transaction_receiver) = channel(); let (transaction_sender, transaction_receiver) = channel();
let bank = bank_forks.read().unwrap().working_bank(); let bank = bank_forks.read().unwrap().working_bank();
let slot = bank.slot(); let slot = bank.slot();
let block_commitment_cache = Arc::new(RwLock::new( let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
BlockCommitmentCache::new_for_tests_with_slots(slot, slot), HashMap::default(),
)); 0,
CommitmentSlots {
slot,
root: 0,
highest_confirmed_slot: 0,
highest_confirmed_root: 0,
},
)));
Builder::new() Builder::new()
.name("solana-bank-forks-client".to_string()) .name("solana-bank-forks-client".to_string())
.spawn(move || Self::run(&bank, transaction_receiver)) .spawn(move || Self::run(&bank, transaction_receiver))
@@ -102,36 +115,23 @@ impl BanksServer {
async fn poll_signature_status( async fn poll_signature_status(
self, self,
signature: &Signature, signature: Signature,
blockhash: &Hash,
last_valid_slot: Slot, last_valid_slot: Slot,
commitment: CommitmentLevel, commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> { ) -> Option<transaction::Result<()>> {
let mut status = self let mut status = self.bank(commitment).get_signature_status(&signature);
.bank(commitment)
.get_signature_status_with_blockhash(signature, blockhash);
while status.is_none() { while status.is_none() {
sleep(Duration::from_millis(200)).await; delay_for(Duration::from_millis(200)).await;
let bank = self.bank(commitment); let bank = self.bank(commitment);
if bank.slot() > last_valid_slot { if bank.slot() > last_valid_slot {
break; break;
} }
status = bank.get_signature_status_with_blockhash(signature, blockhash); status = bank.get_signature_status(&signature);
} }
status status
} }
} }
fn verify_transaction(transaction: &Transaction) -> transaction::Result<()> {
if let Err(err) = transaction.verify() {
Err(err)
} else if let Err(err) = transaction.verify_precompiles() {
Err(err)
} else {
Ok(())
}
}
#[tarpc::server] #[tarpc::server]
impl Banks for BanksServer { impl Banks for BanksServer {
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) { async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
@@ -193,23 +193,19 @@ impl Banks for BanksServer {
transaction: Transaction, transaction: Transaction,
commitment: CommitmentLevel, commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> { ) -> Option<transaction::Result<()>> {
if let Err(err) = verify_transaction(&transaction) {
return Some(Err(err));
}
let blockhash = &transaction.message.recent_blockhash; let blockhash = &transaction.message.recent_blockhash;
let last_valid_slot = self let last_valid_slot = self
.bank_forks .bank_forks
.read() .read()
.unwrap() .unwrap()
.root_bank() .root_bank()
.get_blockhash_last_valid_slot(blockhash) .get_blockhash_last_valid_slot(&blockhash)
.unwrap(); .unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default(); let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info = let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot); TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap(); self.transaction_sender.send(info).unwrap();
self.poll_signature_status(&signature, blockhash, last_valid_slot, commitment) self.poll_signature_status(signature, last_valid_slot, commitment)
.await .await
} }
@@ -249,18 +245,19 @@ pub async fn start_tcp_server(
.filter_map(|r| future::ready(r.ok())) .filter_map(|r| future::ready(r.ok()))
.map(server::BaseChannel::with_defaults) .map(server::BaseChannel::with_defaults)
// Limit channels to 1 per IP. // Limit channels to 1 per IP.
.max_channels_per_key(1, |t| { .max_channels_per_key(1, |t| t.as_ref().peer_addr().unwrap().ip())
t.as_ref()
.peer_addr()
.map(|x| x.ip())
.unwrap_or_else(|_| Ipv4Addr::new(0, 0, 0, 0).into())
})
// serve is generated by the service attribute. It takes as input any type implementing // serve is generated by the service attribute. It takes as input any type implementing
// the generated Banks trait. // the generated Banks trait.
.map(move |chan| { .map(move |chan| {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
let exit_send_transaction_service = Arc::new(AtomicBool::new(false));
SendTransactionService::new(tpu_addr, &bank_forks, receiver); SendTransactionService::new(
tpu_addr,
&bank_forks,
&exit_send_transaction_service,
receiver,
);
let server = let server =
BanksServer::new(bank_forks.clone(), block_commitment_cache.clone(), sender); BanksServer::new(bank_forks.clone(), block_commitment_cache.clone(), sender);

View File

@@ -1,6 +1,2 @@
pub mod banks_server; pub mod banks_server;
pub mod rpc_banks_service; pub mod rpc_banks_service;
pub mod send_transaction_service;
#[macro_use]
extern crate solana_metrics;

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-bench-exchange" name = "solana-bench-exchange"
version = "1.5.1" version = "1.3.6"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@@ -11,28 +11,28 @@ publish = false
[dependencies] [dependencies]
clap = "2.33.1" clap = "2.33.1"
itertools = "0.9.0" itertools = "0.9.0"
log = "0.4.11" log = "0.4.8"
num-derive = "0.3" num-derive = "0.3"
num-traits = "0.2" num-traits = "0.2"
rand = "0.7.0" rand = "0.7.0"
rayon = "1.4.0" rayon = "1.3.1"
serde_json = "1.0.56" serde_json = "1.0.56"
serde_yaml = "0.8.13" serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "1.5.1" } solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
solana-core = { path = "../core", version = "1.5.1" } solana-core = { path = "../core", version = "1.3.6" }
solana-genesis = { path = "../genesis", version = "1.5.1" } solana-genesis = { path = "../genesis", version = "1.3.6" }
solana-client = { path = "../client", version = "1.5.1" } solana-client = { path = "../client", version = "1.3.6" }
solana-faucet = { path = "../faucet", version = "1.5.1" } solana-faucet = { path = "../faucet", version = "1.3.6" }
solana-exchange-program = { path = "../programs/exchange", version = "1.5.1" } solana-exchange-program = { path = "../programs/exchange", version = "1.3.6" }
solana-logger = { path = "../logger", version = "1.5.1" } solana-logger = { path = "../logger", version = "1.3.6" }
solana-metrics = { path = "../metrics", version = "1.5.1" } solana-metrics = { path = "../metrics", version = "1.3.6" }
solana-net-utils = { path = "../net-utils", version = "1.5.1" } solana-net-utils = { path = "../net-utils", version = "1.3.6" }
solana-runtime = { path = "../runtime", version = "1.5.1" } solana-runtime = { path = "../runtime", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-version = { path = "../version", version = "1.5.1" } solana-version = { path = "../version", version = "1.3.6" }
[dev-dependencies] [dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.5.1" } solana-local-cluster = { path = "../local-cluster", version = "1.3.6" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -163,8 +163,7 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
) )
} }
#[allow(clippy::field_reassign_with_default)] pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
pub fn extract_args(matches: &ArgMatches) -> Config {
let mut args = Config::default(); let mut args = Config::default();
args.entrypoint_addr = solana_net_utils::parse_host_port( args.entrypoint_addr = solana_net_utils::parse_host_port(

View File

@@ -22,17 +22,15 @@ fn test_exchange_local_cluster() {
const NUM_NODES: usize = 1; const NUM_NODES: usize = 1;
let config = Config { let mut config = Config::default();
identity: Keypair::new(), config.identity = Keypair::new();
duration: Duration::from_secs(1), config.duration = Duration::from_secs(1);
fund_amount: 100_000, config.fund_amount = 100_000;
threads: 1, config.threads = 1;
transfer_delay: 20, // 15 config.transfer_delay = 20; // 15
batch_size: 100, // 1000 config.batch_size = 100; // 1000;
chunk_size: 10, // 200 config.chunk_size = 10; // 200;
account_groups: 1, // 10 config.account_groups = 1; // 10;
..Config::default()
};
let Config { let Config {
fund_amount, fund_amount,
batch_size, batch_size,
@@ -41,7 +39,7 @@ fn test_exchange_local_cluster() {
} = config; } = config;
let accounts_in_groups = batch_size * account_groups; let accounts_in_groups = batch_size * account_groups;
let cluster = LocalCluster::new(&mut ClusterConfig { let cluster = LocalCluster::new(&ClusterConfig {
node_stakes: vec![100_000; NUM_NODES], node_stakes: vec![100_000; NUM_NODES],
cluster_lamports: 100_000_000_000_000, cluster_lamports: 100_000_000_000_000,
validator_configs: vec![ValidatorConfig::default(); NUM_NODES], validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
@@ -88,21 +86,18 @@ fn test_exchange_bank_client() {
solana_logger::setup(); solana_logger::setup();
let (genesis_config, identity) = create_genesis_config(100_000_000_000_000); let (genesis_config, identity) = create_genesis_config(100_000_000_000_000);
let mut bank = Bank::new(&genesis_config); let mut bank = Bank::new(&genesis_config);
bank.add_builtin("exchange_program", id(), process_instruction); bank.add_builtin_program("exchange_program", id(), process_instruction);
let clients = vec![BankClient::new(bank)]; let clients = vec![BankClient::new(bank)];
do_bench_exchange( let mut config = Config::default();
clients, config.identity = identity;
Config { config.duration = Duration::from_secs(1);
identity, config.fund_amount = 100_000;
duration: Duration::from_secs(1), config.threads = 1;
fund_amount: 100_000, config.transfer_delay = 20; // 0;
threads: 1, config.batch_size = 100; // 1500;
transfer_delay: 20, // 0; config.chunk_size = 10; // 1500;
batch_size: 100, // 1500; config.account_groups = 1; // 50;
chunk_size: 10, // 1500;
account_groups: 1, // 50; do_bench_exchange(clients, config);
..Config::default()
},
);
} }

View File

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

View File

@@ -27,7 +27,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
let mut num = 0; let mut num = 0;
for p in &msgs.packets { for p in &msgs.packets {
let a = p.meta.addr(); let a = p.meta.addr();
assert!(p.meta.size <= PACKET_DATA_SIZE); assert!(p.meta.size < PACKET_DATA_SIZE);
send.send_to(&p.data[..p.meta.size], &a).unwrap(); send.send_to(&p.data[..p.meta.size], &a).unwrap();
num += 1; num += 1;
} }

View File

@@ -2,36 +2,35 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-bench-tps" name = "solana-bench-tps"
version = "1.5.1" version = "1.3.6"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
publish = false
[dependencies] [dependencies]
bincode = "1.3.1" bincode = "1.3.1"
clap = "2.33.1" clap = "2.33.1"
log = "0.4.11" log = "0.4.8"
rayon = "1.4.0" rayon = "1.3.1"
serde_json = "1.0.56" serde_json = "1.0.56"
serde_yaml = "0.8.13" serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "1.5.1" } solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
solana-core = { path = "../core", version = "1.5.1" } solana-core = { path = "../core", version = "1.3.6" }
solana-genesis = { path = "../genesis", version = "1.5.1" } solana-genesis = { path = "../genesis", version = "1.3.6" }
solana-client = { path = "../client", version = "1.5.1" } solana-client = { path = "../client", version = "1.3.6" }
solana-faucet = { path = "../faucet", version = "1.5.1" } solana-faucet = { path = "../faucet", version = "1.3.6" }
solana-logger = { path = "../logger", version = "1.5.1" } solana-logger = { path = "../logger", version = "1.3.6" }
solana-metrics = { path = "../metrics", version = "1.5.1" } solana-metrics = { path = "../metrics", version = "1.3.6" }
solana-measure = { path = "../measure", version = "1.5.1" } solana-measure = { path = "../measure", version = "1.3.6" }
solana-net-utils = { path = "../net-utils", version = "1.5.1" } solana-net-utils = { path = "../net-utils", version = "1.3.6" }
solana-runtime = { path = "../runtime", version = "1.5.1" } solana-runtime = { path = "../runtime", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-version = { path = "../version", version = "1.5.1" } solana-version = { path = "../version", version = "1.3.6" }
[dev-dependencies] [dev-dependencies]
serial_test = "0.4.0" serial_test = "0.4.0"
serial_test_derive = "0.4.0" serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.5.1" } solana-local-cluster = { path = "../local-cluster", version = "1.3.6" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -938,12 +938,10 @@ mod tests {
let bank = Bank::new(&genesis_config); let bank = Bank::new(&genesis_config);
let client = Arc::new(BankClient::new(bank)); let client = Arc::new(BankClient::new(bank));
let config = Config { let mut config = Config::default();
id, config.id = id;
tx_count: 10, config.tx_count = 10;
duration: Duration::from_secs(5), config.duration = Duration::from_secs(5);
..Config::default()
};
let keypair_count = config.tx_count * config.keypair_multiplier; let keypair_count = config.tx_count * config.keypair_multiplier;
let keypairs = let keypairs =

View File

@@ -196,7 +196,7 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
/// * `matches` - command line arguments parsed by clap /// * `matches` - command line arguments parsed by clap
/// # Panics /// # Panics
/// Panics if there is trouble parsing any of the arguments /// Panics if there is trouble parsing any of the arguments
pub fn extract_args(matches: &ArgMatches) -> Config { pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
let mut args = Config::default(); let mut args = Config::default();
if let Some(addr) = matches.value_of("entrypoint") { if let Some(addr) = matches.value_of("entrypoint") {

View File

@@ -15,7 +15,7 @@ fn test_bench_tps_local_cluster(config: Config) {
solana_logger::setup(); solana_logger::setup();
const NUM_NODES: usize = 1; const NUM_NODES: usize = 1;
let cluster = LocalCluster::new(&mut ClusterConfig { let cluster = LocalCluster::new(&ClusterConfig {
node_stakes: vec![999_990; NUM_NODES], node_stakes: vec![999_990; NUM_NODES],
cluster_lamports: 200_000_000, cluster_lamports: 200_000_000,
validator_configs: vec![ValidatorConfig::default(); NUM_NODES], validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
@@ -60,9 +60,9 @@ fn test_bench_tps_local_cluster(config: Config) {
#[test] #[test]
#[serial] #[serial]
fn test_bench_tps_local_cluster_solana() { fn test_bench_tps_local_cluster_solana() {
test_bench_tps_local_cluster(Config { let mut config = Config::default();
tx_count: 100, config.tx_count = 100;
duration: Duration::from_secs(10), config.duration = Duration::from_secs(10);
..Config::default()
}); test_bench_tps_local_cluster(config);
} }

31
cargo
View File

@@ -1,31 +0,0 @@
#!/usr/bin/env bash
# shellcheck source=ci/rust-version.sh
here=$(dirname "$0")
source "${here}"/ci/rust-version.sh all
toolchain=
case "$1" in
stable)
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
toolchain="$rust_stable"
shift
;;
nightly)
# shellcheck disable=SC2054 # rust_nightly is sourced from rust-version.sh
toolchain="$rust_nightly"
shift
;;
+*)
toolchain="${1#+}"
shift
;;
*)
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
toolchain="$rust_stable"
;;
esac
set -x
exec cargo "+${toolchain}" "${@}"

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
here=$(dirname "$0")
maybe_bpf_sdk="--bpf-sdk $here/sdk/bpf"
for a in "$@"; do
if [[ $a = --bpf-sdk ]]; then
maybe_bpf_sdk=
fi
done
set -x
exec "$here"/cargo run --manifest-path "$here"/sdk/cargo-build-bpf/Cargo.toml -- $maybe_bpf_sdk "$@"

View File

@@ -1,14 +0,0 @@
#!/usr/bin/env bash
here=$(dirname "$0")
maybe_bpf_sdk="--bpf-sdk $here/sdk/bpf"
for a in "$@"; do
if [[ $a = --bpf-sdk ]]; then
maybe_bpf_sdk=
fi
done
export CARGO_BUILD_BPF="$here"/cargo-build-bpf
set -x
exec "$here"/cargo run --manifest-path "$here"/sdk/cargo-test-bpf/Cargo.toml -- $maybe_bpf_sdk "$@"

View File

@@ -47,8 +47,6 @@ sudo ./setup-new-buildkite-agent/setup-buildkite.sh
``` ```
- Copy the pubkey contents from `~buildkite-agent/.ssh/id_ecdsa.pub` and - Copy the pubkey contents from `~buildkite-agent/.ssh/id_ecdsa.pub` and
add the pubkey as an authorized SSH key on github. add the pubkey as an authorized SSH key on github.
- In net/scripts/solana-user-authorized_keys.sh
- Bug mvines to add it to the "solana-grimes" github user
- Edit `/etc/buildkite-agent/buildkite-agent.cfg` and/or `/etc/systemd/system/buildkite-agent@*` to the desired configuration of the agent(s) - Edit `/etc/buildkite-agent/buildkite-agent.cfg` and/or `/etc/systemd/system/buildkite-agent@*` to the desired configuration of the agent(s)
- Copy `ejson` keys from another CI node at `/opt/ejson/keys/` - Copy `ejson` keys from another CI node at `/opt/ejson/keys/`
to the same location on the new node. to the same location on the new node.

View File

@@ -175,30 +175,6 @@ EOF
"Stable-perf skipped as no relevant files were modified" "Stable-perf skipped as no relevant files were modified"
fi fi
# Downstream backwards compatibility
if affects \
.rs$ \
Cargo.lock$ \
Cargo.toml$ \
^ci/rust-version.sh \
^ci/test-stable-perf.sh \
^ci/test-stable.sh \
^ci/test-local-cluster.sh \
^core/build.rs \
^fetch-perf-libs.sh \
^programs/ \
^sdk/ \
^scripts/build-downstream-projects.sh \
; then
cat >> "$output_file" <<"EOF"
- command: "scripts/build-downstream-projects.sh"
name: "downstream-projects"
timeout_in_minutes: 30
EOF
else
annotate --style info \
"downstream-projects skipped as no relevant files were modified"
fi
# Benches... # Benches...
if affects \ if affects \
.rs$ \ .rs$ \
@@ -263,7 +239,7 @@ if [[ $BUILDKITE_BRANCH =~ ^pull ]]; then
annotate --style info --context pr-backlink \ annotate --style info --context pr-backlink \
"Github Pull Request: https://github.com/solana-labs/solana/$BUILDKITE_BRANCH" "Github Pull Request: https://github.com/solana-labs/solana/$BUILDKITE_BRANCH"
if [[ $GITHUB_USER = "dependabot[bot]" ]]; then if [[ $GITHUB_USER = "dependabot-preview[bot]" ]]; then
command_step dependabot "ci/dependabot-pr.sh" 5 command_step dependabot "ci/dependabot-pr.sh" 5
wait_step wait_step
fi fi

View File

@@ -95,14 +95,12 @@ elif [[ -n $CI_BRANCH ]]; then
BRANCH="$CI_BRANCH" BRANCH="$CI_BRANCH"
fi fi
if [[ -z "$CHANNEL" ]]; then if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
CHANNEL=stable CHANNEL=stable
elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then
CHANNEL=edge CHANNEL=edge
elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then
CHANNEL=beta CHANNEL=beta
fi
fi fi
echo EDGE_CHANNEL="$EDGE_CHANNEL" echo EDGE_CHANNEL="$EDGE_CHANNEL"

View File

@@ -6,13 +6,13 @@ source ci/_
commit_range="$(git merge-base HEAD origin/master)..HEAD" commit_range="$(git merge-base HEAD origin/master)..HEAD"
parsed_update_args="$( parsed_update_args="$(
git log "$commit_range" --author "dependabot\[bot\]" --oneline -n1 | git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
grep -o '[Bb]ump.*$' | grep -o '[Bb]ump.*$' |
sed -r 's/[Bb]ump ([^ ]+) from ([^ ]+) to ([^ ]+)/-p \1:\2 --precise \3/' sed -r 's/[Bb]ump ([^ ]+) from ([^ ]+) to ([^ ]+)/-p \1:\2 --precise \3/'
)" )"
# relaxed_parsed_update_args is temporal measure... # relaxed_parsed_update_args is temporal measure...
relaxed_parsed_update_args="$( relaxed_parsed_update_args="$(
git log "$commit_range" --author "dependabot\[bot\]" --oneline -n1 | git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
grep -o '[Bb]ump.*$' | grep -o '[Bb]ump.*$' |
sed -r 's/[Bb]ump ([^ ]+) from [^ ]+ to ([^ ]+)/-p \1 --precise \2/' sed -r 's/[Bb]ump ([^ ]+) from [^ ]+ to ([^ ]+)/-p \1 --precise \2/'
)" )"

View File

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

View File

@@ -20,7 +20,7 @@ To update the pinned version:
specific YYYY-MM-DD that is desired (default is today's build). specific YYYY-MM-DD that is desired (default is today's build).
Check https://rust-lang.github.io/rustup-components-history/ for build Check https://rust-lang.github.io/rustup-components-history/ for build
status status
1. Update `ci/rust-version.sh` to reflect the new nightly `YYYY-MM-DD` 1. Update `ci/rust-version.sh` to reflect the new nightly `YYY-MM-DD`
1. Run `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-checks.sh` 1. Run `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-checks.sh`
and `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh [args]...` and `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh [args]...`
to confirm the new nightly image builds. Fix any issues as needed to confirm the new nightly image builds. Fix any issues as needed

View File

@@ -1,6 +1,6 @@
# Note: when the rust version is changed also modify # Note: when the rust version is changed also modify
# ci/rust-version.sh to pick up the new image tag # ci/rust-version.sh to pick up the new image tag
FROM rust:1.48.0 FROM rust:1.45.1
# Add Google Protocol Buffers for Libra's metrics library. # Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0 ENV PROTOC_VERSION 3.8.0

View File

@@ -26,9 +26,6 @@ declare print_free_tree=(
':runtime/src/**.rs' ':runtime/src/**.rs'
':sdk/bpf/rust/rust-utils/**.rs' ':sdk/bpf/rust/rust-utils/**.rs'
':sdk/**.rs' ':sdk/**.rs'
':^sdk/cargo-build-bpf/**.rs'
':^sdk/program/src/program_option.rs'
':^sdk/program/src/program_stubs.rs'
':programs/**.rs' ':programs/**.rs'
':^**bin**.rs' ':^**bin**.rs'
':^**bench**.rs' ':^**bench**.rs'

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python2.7
# #
# This script figures the order in which workspace crates must be published to # This script figures the order in which workspace crates must be published to
# crates.io. Along the way it also ensures there are no circular dependencies # crates.io. Along the way it also ensures there are no circular dependencies
@@ -45,27 +45,21 @@ def get_packages():
sorted_dependency_graph = [] sorted_dependency_graph = []
max_iterations = pow(len(dependency_graph),2) max_iterations = pow(len(dependency_graph),2)
while dependency_graph: while dependency_graph:
deleted_packages = []
if max_iterations == 0: if max_iterations == 0:
# One day be more helpful and find the actual cycle for the user... # One day be more helpful and find the actual cycle for the user...
sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys()))) sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys())))
max_iterations -= 1 max_iterations -= 1
for package, dependencies in dependency_graph.items(): for package, dependencies in dependency_graph.items():
if package in deleted_packages:
continue
for dependency in dependencies: for dependency in dependencies:
if dependency in dependency_graph: if dependency in dependency_graph:
break break
else: else:
deleted_packages.append(package) del dependency_graph[package]
sorted_dependency_graph.append((package, manifest_path[package])) sorted_dependency_graph.append((package, manifest_path[package]))
dependency_graph = {p: d for p, d in dependency_graph.items() if not p in deleted_packages }
return sorted_dependency_graph return sorted_dependency_graph
for package, manifest in get_packages(): for package, manifest in get_packages():
print(os.path.relpath(manifest)) print os.path.relpath(manifest)

View File

@@ -16,12 +16,21 @@ fi
[[ -f bpf-sdk.tar.bz2 ]] [[ -f bpf-sdk.tar.bz2 ]]
) )
source ci/upload-ci-artifact.sh
echo --- AWS S3 Store echo --- AWS S3 Store
if [[ -z $CHANNEL_OR_TAG ]]; then if [[ -z $CHANNEL_OR_TAG ]]; then
echo Skipped echo Skipped
else else
upload-s3-artifact "/solana/bpf-sdk.tar.bz2" "s3://solana-sdk/$CHANNEL_OR_TAG/bpf-sdk.tar.bz2" (
set -x
docker run \
--rm \
--env AWS_ACCESS_KEY_ID \
--env AWS_SECRET_ACCESS_KEY \
--volume "$PWD:/solana" \
eremite/aws-cli:2018.12.18 \
/usr/bin/s3cmd --acl-public put /solana/bpf-sdk.tar.bz2 \
s3://solana-sdk/"$CHANNEL_OR_TAG"/bpf-sdk.tar.bz2
)
fi fi
exit 0 exit 0

View File

@@ -4,8 +4,6 @@ cd "$(dirname "$0")/.."
source ci/semver_bash/semver.sh source ci/semver_bash/semver.sh
source ci/rust-version.sh stable source ci/rust-version.sh stable
cargo="$(readlink -f ./cargo)"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
is_crate_version_uploaded() { is_crate_version_uploaded() {
name=$1 name=$1
@@ -40,7 +38,7 @@ for Cargo_toml in $Cargo_tomls; do
crate_name=$(grep -m 1 '^name = ' "$Cargo_toml" | cut -f 3 -d ' ' | tr -d \") crate_name=$(grep -m 1 '^name = ' "$Cargo_toml" | cut -f 3 -d ' ' | tr -d \")
if grep -q "^publish = false" "$Cargo_toml"; then if grep -q "^publish = false" "$Cargo_toml"; then
echo "$crate_name is marked as unpublishable" echo "$crate_name is is marked as unpublishable"
continue continue
fi fi
@@ -68,11 +66,11 @@ for Cargo_toml in $Cargo_tomls; do
( (
set -x set -x
rm -rf crate-test rm -rf crate-test
"$cargo" stable init crate-test cargo +"$rust_stable" init crate-test
cd crate-test/ cd crate-test/
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
echo "[workspace]" >> Cargo.toml echo "[workspace]" >> Cargo.toml
"$cargo" stable check cargo +"$rust_stable" check
) && really_uploaded=1 ) && really_uploaded=1
if ((really_uploaded)); then if ((really_uploaded)); then
break; break;

View File

@@ -91,15 +91,17 @@ echo --- Creating release tarball
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml
) )
# Maybe tarballs are platform agnostic, only publish them from the Linux build # Metrics tarball is platform agnostic, only publish it from Linux
MAYBE_TARBALLS= MAYBE_TARBALLS=
if [[ "$CI_OS_NAME" = linux ]]; then if [[ "$CI_OS_NAME" = linux ]]; then
metrics/create-metrics-tarball.sh
( (
set -x set -x
sdk/bpf/scripts/package.sh sdk/bpf/scripts/package.sh
[[ -f bpf-sdk.tar.bz2 ]] [[ -f bpf-sdk.tar.bz2 ]]
) )
MAYBE_TARBALLS="bpf-sdk.tar.bz2" MAYBE_TARBALLS="bpf-sdk.tar.bz2 solana-metrics.tar.bz2"
fi fi
source ci/upload-ci-artifact.sh source ci/upload-ci-artifact.sh
@@ -113,10 +115,19 @@ for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.
if [[ -n $BUILDKITE ]]; then if [[ -n $BUILDKITE ]]; then
echo --- AWS S3 Store: "$file" echo --- AWS S3 Store: "$file"
upload-s3-artifact "/solana/$file" s3://release.solana.com/"$CHANNEL_OR_TAG"/"$file" (
set -x
$DRYRUN docker run \
--rm \
--env AWS_ACCESS_KEY_ID \
--env AWS_SECRET_ACCESS_KEY \
--volume "$PWD:/solana" \
eremite/aws-cli:2018.12.18 \
/usr/bin/s3cmd --acl-public put /solana/"$file" s3://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
echo Published to: echo Published to:
$DRYRUN ci/format-url.sh https://release.solana.com/"$CHANNEL_OR_TAG"/"$file" $DRYRUN ci/format-url.sh http://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
)
if [[ -n $TAG ]]; then if [[ -n $TAG ]]; then
ci/upload-github-release-asset.sh "$file" ci/upload-github-release-asset.sh "$file"
@@ -138,22 +149,4 @@ for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.
fi fi
done done
# Create install wrapper for release.solana.com
if [[ -n $DO_NOT_PUBLISH_TAR ]]; then
echo "Skipping publishing install wrapper"
elif [[ -n $BUILDKITE ]]; then
cat > release.solana.com-install <<EOF
SOLANA_RELEASE=$CHANNEL_OR_TAG
SOLANA_INSTALL_INIT_ARGS=$CHANNEL_OR_TAG
SOLANA_DOWNLOAD_ROOT=http://release.solana.com
EOF
cat install/solana-install-init.sh >> release.solana.com-install
echo --- AWS S3 Store: "install"
$DRYRUN upload-s3-artifact "/solana/release.solana.com-install" "s3://release.solana.com/$CHANNEL_OR_TAG/install"
echo Published to:
$DRYRUN ci/format-url.sh https://release.solana.com/"$CHANNEL_OR_TAG"/install
fi
echo --- ok echo --- ok

View File

@@ -18,13 +18,13 @@
if [[ -n $RUST_STABLE_VERSION ]]; then if [[ -n $RUST_STABLE_VERSION ]]; then
stable_version="$RUST_STABLE_VERSION" stable_version="$RUST_STABLE_VERSION"
else else
stable_version=1.48.0 stable_version=1.45.1
fi fi
if [[ -n $RUST_NIGHTLY_VERSION ]]; then if [[ -n $RUST_NIGHTLY_VERSION ]]; then
nightly_version="$RUST_NIGHTLY_VERSION" nightly_version="$RUST_NIGHTLY_VERSION"
else else
nightly_version=2020-12-13 nightly_version=2020-07-27
fi fi

View File

@@ -7,7 +7,6 @@ SOLANA_ROOT="$HERE"/../..
source "$HERE"/utils.sh source "$HERE"/utils.sh
ensure_env || exit 1 ensure_env || exit 1
check_ssh_authorized_keys || exit 1
set -ex set -ex

View File

@@ -6,11 +6,6 @@ HERE="$(dirname "$0")"
source "$HERE"/utils.sh source "$HERE"/utils.sh
ensure_env || exit 1 ensure_env || exit 1
# This is a last ditch effort to prevent the caller from locking themselves
# out of the machine. Exiting here will likely leave the system in some
# half-configured state. To prevent this, duplicate the next line at the top
# of the entrypoint script.
check_ssh_authorized_keys || exit 1
set -xe set -xe
# Setup sshd # Setup sshd

View File

@@ -14,33 +14,3 @@ ensure_env() {
$RC $RC
} }
# Some scripts disable SSH password logins. If no one hash setup authorized_keys
# this will result in the machine being remotely inaccessible. Check that the
# user running this script has setup their keys
check_ssh_authorized_keys() {
declare rc=false
declare user_home=
if [[ -n "$SUDO_USER" ]]; then
declare user uid gid home
declare passwd_entry
passwd_entry="$(grep "$SUDO_USER:[^:]*:$SUDO_UID:$SUDO_GID" /etc/passwd)"
IFS=: read -r user _ uid gid _ home _ <<<"$passwd_entry"
if [[ "$user" == "$SUDO_USER" && "$uid" == "$SUDO_UID" && "$gid" == "$SUDO_GID" ]]; then
user_home="$home"
fi
else
user_home="$HOME"
fi
declare authorized_keys="${user_home}/.ssh/authorized_keys"
if [[ -n "$user_home" ]]; then
[[ -s "$authorized_keys" ]] && rc=true
fi
if ! $rc; then
echo "ERROR! This script will disable SSH password logins and you don't"
echo "appear to have set up any authorized keys. Please add you SSH"
echo "public key to ${authorized_keys} before continuing!"
fi
$rc
}
check_ssh_authorized_keys

View File

@@ -6,8 +6,7 @@ source ci/_
source ci/upload-ci-artifact.sh source ci/upload-ci-artifact.sh
eval "$(ci/channel-info.sh)" eval "$(ci/channel-info.sh)"
source ci/rust-version.sh all
cargo="$(readlink -f "./cargo")"
set -o pipefail set -o pipefail
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@@ -28,35 +27,35 @@ test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# Ensure all dependencies are built # Ensure all dependencies are built
_ "$cargo" nightly build --release _ cargo +$rust_nightly build --release
# Remove "BENCH_FILE", if it exists so that the following commands can append # Remove "BENCH_FILE", if it exists so that the following commands can append
rm -f "$BENCH_FILE" rm -f "$BENCH_FILE"
# Run sdk benches # Run sdk benches
_ "$cargo" nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \ _ cargo +$rust_nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run runtime benches # Run runtime benches
_ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \ _ cargo +$rust_nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run core benches # Run core benches
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \ _ cargo +$rust_nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run bpf benches # Run bpf benches
_ "$cargo" nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \ _ cargo +$rust_nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \
-- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE"
# Run banking/accounts bench. Doesn't require nightly, but use since it is already built. # Run banking/accounts bench. Doesn't require nightly, but use since it is already built.
_ "$cargo" nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE" _ cargo +$rust_nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE"
_ "$cargo" nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE" _ cargo +$rust_nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE"
# `solana-upload-perf` disabled as it can take over 30 minutes to complete for some # `solana-upload-perf` disabled as it can take over 30 minutes to complete for some
# reason # reason
exit 0 exit 0
_ "$cargo" nightly run --release --package solana-upload-perf \ _ cargo +$rust_nightly run --release --package solana-upload-perf \
-- "$BENCH_FILE" "$TARGET_BRANCH" "$UPLOAD_METRICS" | tee "$BENCH_ARTIFACT" -- "$BENCH_FILE" "$TARGET_BRANCH" "$UPLOAD_METRICS" | tee "$BENCH_ARTIFACT"
upload-ci-artifact "$BENCH_FILE" upload-ci-artifact "$BENCH_FILE"

View File

@@ -8,9 +8,6 @@ source ci/_
source ci/rust-version.sh stable source ci/rust-version.sh stable
source ci/rust-version.sh nightly source ci/rust-version.sh nightly
eval "$(ci/channel-info.sh)" eval "$(ci/channel-info.sh)"
cargo="$(readlink -f "./cargo")"
scripts/increment-cargo-version.sh check
echo --- build environment echo --- build environment
( (
@@ -19,14 +16,14 @@ echo --- build environment
rustup run "$rust_stable" rustc --version --verbose rustup run "$rust_stable" rustc --version --verbose
rustup run "$rust_nightly" rustc --version --verbose rustup run "$rust_nightly" rustc --version --verbose
"$cargo" stable --version --verbose cargo +"$rust_stable" --version --verbose
"$cargo" nightly --version --verbose cargo +"$rust_nightly" --version --verbose
"$cargo" stable clippy --version --verbose cargo +"$rust_stable" clippy --version --verbose
"$cargo" nightly clippy --version --verbose cargo +"$rust_nightly" clippy --version --verbose
# audit is done only with stable # audit is done only with stable
"$cargo" stable audit --version cargo +"$rust_stable" audit --version
) )
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@@ -44,53 +41,32 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2 echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
exit "$check_status" exit "$check_status"
fi fi
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
else else
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL" echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
fi fi
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
_ ci/order-crates-for-publishing.py _ ci/order-crates-for-publishing.py
_ "$cargo" stable fmt --all -- --check _ cargo +"$rust_stable" fmt --all -- --check
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612 # -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 # 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 _ cargo +"$rust_nightly" clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
cargo_audit_ignores=( _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
# failure is officially deprecated/unmaintained
#
# Blocked on multiple upstream crates removing their `failure` dependency.
--ignore RUSTSEC-2020-0036
# `net2` crate has been deprecated; use `socket2` instead
#
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
--ignore RUSTSEC-2020-0016
# stdweb is unmaintained
#
# Blocked on multiple upstream crates removing their `stdweb` dependency.
--ignore RUSTSEC-2020-0056
# Potential segfault in the time crate
#
# Blocked on multiple crates updating `time` to >= 0.2.23
--ignore RUSTSEC-2020-0071
)
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
{ {
cd programs/bpf cd programs/bpf
_ "$cargo" stable audit _ cargo +"$rust_stable" audit
for project in rust/*/ ; do for project in rust/*/ ; do
echo "+++ do_bpf_checks $project" echo "+++ do_bpf_checks $project"
( (
cd "$project" cd "$project"
_ "$cargo" stable fmt -- --check _ cargo +"$rust_stable" fmt -- --check
_ "$cargo" nightly test _ cargo +"$rust_nightly" test
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc _ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
) )
done done
} }

View File

@@ -8,16 +8,10 @@ source ci/_
( (
echo --- git diff --check echo --- git diff --check
set -x set -x
if [[ -n $CI_BASE_BRANCH ]]
then branch="$CI_BASE_BRANCH"
else branch="master"
fi
# Look for failed mergify.io backports by searching leftover conflict markers # Look for failed mergify.io backports by searching leftover conflict markers
# Also check for any trailing whitespaces! # Also check for any trailing whitespaces!
git fetch origin "$branch" git fetch origin "$CI_BASE_BRANCH"
git diff "$(git merge-base HEAD "origin/$branch")" --check --oneline git diff "$(git merge-base HEAD "origin/$CI_BASE_BRANCH")..HEAD" --check --oneline
) )
echo echo

View File

@@ -2,8 +2,6 @@
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
cargo="$(readlink -f "./cargo")"
source ci/_ source ci/_
annotate() { annotate() {
@@ -21,6 +19,9 @@ export RUST_BACKTRACE=1
export RUSTFLAGS="-D warnings" export RUSTFLAGS="-D warnings"
source scripts/ulimit-n.sh source scripts/ulimit-n.sh
# Clear cached json keypair files
rm -rf "$HOME/.config/solana"
# Clear the C dependency files, if dependency moves these files are not regenerated # 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/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
@@ -36,15 +37,12 @@ NPROC=$((NPROC>14 ? 14 : NPROC))
echo "Executing $testName" echo "Executing $testName"
case $testName in case $testName in
test-stable) test-stable)
_ "$cargo" stable test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture _ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
;; ;;
test-stable-perf) test-stable-perf)
# BPF solana-sdk legacy compile test
./cargo-build-bpf --manifest-path sdk/Cargo.toml
# BPF program tests # BPF program tests
_ make -C programs/bpf/c tests _ make -C programs/bpf/c tests
_ "$cargo" stable test \ _ cargo +"$rust_stable" test \
--manifest-path programs/bpf/Cargo.toml \ --manifest-path programs/bpf/Cargo.toml \
--no-default-features --features=bpf_c,bpf_rust -- --nocapture --no-default-features --features=bpf_c,bpf_rust -- --nocapture
@@ -64,13 +62,13 @@ test-stable-perf)
export SOLANA_CUDA=1 export SOLANA_CUDA=1
fi fi
_ "$cargo" stable build --bins ${V:+--verbose} _ cargo +"$rust_stable" build --bins ${V:+--verbose}
_ "$cargo" stable test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture _ cargo +"$rust_stable" test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
_ "$cargo" stable run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10 _ cargo +"$rust_stable" run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
;; ;;
test-local-cluster) test-local-cluster)
_ "$cargo" stable build --release --bins ${V:+--verbose} _ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
_ "$cargo" stable test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1 _ cargo +"$rust_stable" test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1
exit 0 exit 0
;; ;;
*) *)

View File

@@ -16,16 +16,3 @@ upload-ci-artifact() {
fi fi
} }
upload-s3-artifact() {
echo "--- artifact: $1 to $2"
(
set -x
docker run \
--rm \
--env AWS_ACCESS_KEY_ID \
--env AWS_SECRET_ACCESS_KEY \
--volume "$PWD:/solana" \
eremite/aws-cli:2018.12.18 \
/usr/bin/s3cmd --acl-public put "$1" "$2"
)
}

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-clap-utils" name = "solana-clap-utils"
version = "1.5.1" version = "1.3.6"
description = "Solana utilities for the clap" description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -11,9 +11,9 @@ edition = "2018"
[dependencies] [dependencies]
clap = "2.33.0" clap = "2.33.0"
rpassword = "4.0" rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.1" } solana-remote-wallet = { path = "../remote-wallet", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
thiserror = "1.0.21" thiserror = "1.0.20"
tiny-bip39 = "0.7.0" tiny-bip39 = "0.7.0"
url = "2.1.0" url = "2.1.0"
chrono = "0.4" chrono = "0.4"

View File

@@ -15,7 +15,7 @@ pub fn commitment_arg_with_default<'a, 'b>(default_value: &'static str) -> Arg<'
Arg::with_name(COMMITMENT_ARG.name) Arg::with_name(COMMITMENT_ARG.name)
.long(COMMITMENT_ARG.long) .long(COMMITMENT_ARG.long)
.takes_value(true) .takes_value(true)
.possible_values(&["recent", "single", "singleGossip", "root", "max"]) .possible_values(&["recent", "single", "root", "max"])
.default_value(default_value) .default_value(default_value)
.value_name("COMMITMENT_LEVEL") .value_name("COMMITMENT_LEVEL")
.help(COMMITMENT_ARG.help) .help(COMMITMENT_ARG.help)

View File

@@ -1,19 +0,0 @@
use crate::{input_validators, ArgConstant};
use clap::Arg;
pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant {
name: "fee_payer",
long: "fee-payer",
help: "Specify the fee-payer account. This may be a keypair file, the ASK keyword \n\
or the pubkey of an offline signer, provided an appropriate --signer argument \n\
is also passed. Defaults to the client keypair.",
};
pub fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(FEE_PAYER_ARG.name)
.long(FEE_PAYER_ARG.long)
.takes_value(true)
.value_name("KEYPAIR")
.validator(input_validators::is_valid_signer)
.help(FEE_PAYER_ARG.help)
}

View File

@@ -8,7 +8,6 @@ use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
clock::UnixTimestamp, clock::UnixTimestamp,
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
genesis_config::ClusterType,
native_token::sol_to_lamports, native_token::sol_to_lamports,
pubkey::Pubkey, pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer}, signature::{read_keypair_file, Keypair, Signature, Signer},
@@ -179,17 +178,12 @@ pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
value_of(matches, name).map(sol_to_lamports) value_of(matches, name).map(sol_to_lamports)
} }
pub fn cluster_type_of(matches: &ArgMatches<'_>, name: &str) -> Option<ClusterType> {
value_of(matches, name)
}
pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> { pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
matches.value_of(name).map(|value| match value { matches.value_of(name).map(|value| match value {
"max" => CommitmentConfig::max(), "max" => CommitmentConfig::max(),
"recent" => CommitmentConfig::recent(), "recent" => CommitmentConfig::recent(),
"root" => CommitmentConfig::root(), "root" => CommitmentConfig::root(),
"single" => CommitmentConfig::single(), "single" => CommitmentConfig::single(),
"singleGossip" => CommitmentConfig::single_gossip(),
_ => CommitmentConfig::default(), _ => CommitmentConfig::default(),
}) })
} }
@@ -229,8 +223,8 @@ mod tests {
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39])); assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
assert_eq!(values_of::<u64>(&matches, "single"), None); assert_eq!(values_of::<u64>(&matches, "single"), None);
let pubkey0 = solana_sdk::pubkey::new_rand(); let pubkey0 = Pubkey::new_rand();
let pubkey1 = solana_sdk::pubkey::new_rand(); let pubkey1 = Pubkey::new_rand();
let matches = app().clone().get_matches_from(vec![ let matches = app().clone().get_matches_from(vec![
"test", "test",
"--multiple", "--multiple",
@@ -252,7 +246,7 @@ mod tests {
assert_eq!(value_of(&matches, "single"), Some(50)); assert_eq!(value_of(&matches, "single"), Some(50));
assert_eq!(value_of::<u64>(&matches, "multiple"), None); assert_eq!(value_of::<u64>(&matches, "multiple"), None);
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let matches = app() let matches = app()
.clone() .clone()
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]); .get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
@@ -332,8 +326,8 @@ mod tests {
#[test] #[test]
fn test_pubkeys_sigs_of() { fn test_pubkeys_sigs_of() {
let key1 = solana_sdk::pubkey::new_rand(); let key1 = Pubkey::new_rand();
let key2 = solana_sdk::pubkey::new_rand(); let key2 = Pubkey::new_rand();
let sig1 = Keypair::new().sign_message(&[0u8]); let sig1 = Keypair::new().sign_message(&[0u8]);
let sig2 = Keypair::new().sign_message(&[1u8]); let sig2 = Keypair::new().sign_message(&[1u8]);
let signer1 = format!("{}={}", key1, sig1); let signer1 = format!("{}={}", key1, sig1);

View File

@@ -1,7 +1,7 @@
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD}; use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
use chrono::DateTime; use chrono::DateTime;
use solana_sdk::{ use solana_sdk::{
clock::{Epoch, Slot}, clock::Slot,
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::{read_keypair_file, Signature}, signature::{read_keypair_file, Signature},
@@ -148,13 +148,6 @@ where
} }
} }
pub fn is_epoch<T>(epoch: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
is_parsable_generic::<Epoch, _>(epoch)
}
pub fn is_slot<T>(slot: T) -> Result<(), String> pub fn is_slot<T>(slot: T) -> Result<(), String>
where where
T: AsRef<str> + Display, T: AsRef<str> + Display,

View File

@@ -11,7 +11,6 @@ use solana_remote_wallet::{
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager}, remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
}; };
use solana_sdk::{ use solana_sdk::{
hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::{ signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair, keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
@@ -26,90 +25,6 @@ use std::{
sync::Arc, sync::Arc,
}; };
pub struct SignOnly {
pub blockhash: Hash,
pub present_signers: Vec<(Pubkey, Signature)>,
pub absent_signers: Vec<Pubkey>,
pub bad_signers: Vec<Pubkey>,
}
impl SignOnly {
pub fn has_all_signers(&self) -> bool {
self.absent_signers.is_empty() && self.bad_signers.is_empty()
}
pub fn presigner_of(&self, pubkey: &Pubkey) -> Option<Presigner> {
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
}
}
pub type CliSigners = Vec<Box<dyn Signer>>;
pub type SignerIndex = usize;
pub struct CliSignerInfo {
pub signers: CliSigners,
}
impl CliSignerInfo {
pub fn index_of(&self, pubkey: Option<Pubkey>) -> Option<usize> {
if let Some(pubkey) = pubkey {
self.signers
.iter()
.position(|signer| signer.pubkey() == pubkey)
} else {
Some(0)
}
}
pub fn index_of_or_none(&self, pubkey: Option<Pubkey>) -> Option<usize> {
if let Some(pubkey) = pubkey {
self.signers
.iter()
.position(|signer| signer.pubkey() == pubkey)
} else {
None
}
}
}
pub struct DefaultSigner {
pub arg_name: String,
pub path: String,
}
impl DefaultSigner {
pub fn generate_unique_signers(
&self,
bulk_signers: Vec<Option<Box<dyn Signer>>>,
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliSignerInfo, Box<dyn error::Error>> {
let mut unique_signers = vec![];
// Determine if the default signer is needed
if bulk_signers.iter().any(|signer| signer.is_none()) {
let default_signer = self.signer_from_path(matches, wallet_manager)?;
unique_signers.push(default_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 {
signers: unique_signers,
})
}
pub fn signer_from_path(
&self,
matches: &ArgMatches,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
}
}
pub enum KeypairUrl { pub enum KeypairUrl {
Ask, Ask,
Filepath(String), Filepath(String),
@@ -163,7 +78,7 @@ pub fn signer_from_path(
KeypairUrl::Filepath(path) => match read_keypair_file(&path) { KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new( Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other, std::io::ErrorKind::Other,
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e), format!("could not find keypair file: {} error: {}", path, e),
) )
.into()), .into()),
Ok(file) => Ok(Box::new(file)), Ok(file) => Ok(Box::new(file)),
@@ -234,7 +149,7 @@ pub fn resolve_signer_from_path(
KeypairUrl::Filepath(path) => match read_keypair_file(&path) { KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new( Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other, std::io::ErrorKind::Other,
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e), format!("could not find keypair file: {} error: {}", path, e),
) )
.into()), .into()),
Ok(_) => Ok(Some(path.to_string())), Ok(_) => Ok(Some(path.to_string())),
@@ -307,24 +222,7 @@ pub fn keypair_from_seed_phrase(
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)? keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
} else { } else {
let sanitized = sanitize_seed_phrase(seed_phrase); let sanitized = sanitize_seed_phrase(seed_phrase);
let parse_language_fn = || { let mnemonic = Mnemonic::from_phrase(&sanitized, Language::English)?;
for language in &[
Language::English,
Language::ChineseSimplified,
Language::ChineseTraditional,
Language::Japanese,
Language::Spanish,
Language::Korean,
Language::French,
Language::Italian,
] {
if let Ok(mnemonic) = Mnemonic::from_phrase(&sanitized, *language) {
return Ok(mnemonic);
}
}
Err("Can't get mnemonic from seed phrases")
};
let mnemonic = parse_language_fn()?;
let passphrase = prompt_passphrase(&passphrase_prompt)?; let passphrase = prompt_passphrase(&passphrase_prompt)?;
let seed = Seed::new(&mnemonic, &passphrase); let seed = Seed::new(&mnemonic, &passphrase);
keypair_from_seed(seed.as_bytes())? keypair_from_seed(seed.as_bytes())?

View File

@@ -24,9 +24,7 @@ impl std::fmt::Debug for DisplayError {
} }
pub mod commitment; pub mod commitment;
pub mod fee_payer;
pub mod input_parsers; pub mod input_parsers;
pub mod input_validators; pub mod input_validators;
pub mod keypair; pub mod keypair;
pub mod nonce;
pub mod offline; pub mod offline;

View File

@@ -1,50 +0,0 @@
use crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant};
use clap::{App, Arg};
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
name: "nonce",
long: "nonce",
help: "Provide the nonce account to use when creating a nonced \n\
transaction. Nonced transactions are useful when a transaction \n\
requires a lengthy signing process. Learn more about nonced \n\
transactions at https://docs.solana.com/offline-signing/durable-nonce",
};
pub const NONCE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant {
name: "nonce_authority",
long: "nonce-authority",
help: "Provide the nonce authority keypair to use when signing a nonced transaction",
};
fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(NONCE_ARG.name)
.long(NONCE_ARG.long)
.takes_value(true)
.value_name("PUBKEY")
.requires(BLOCKHASH_ARG.name)
.validator(is_valid_pubkey)
.help(NONCE_ARG.help)
}
pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(NONCE_AUTHORITY_ARG.name)
.long(NONCE_AUTHORITY_ARG.long)
.takes_value(true)
.value_name("KEYPAIR")
.validator(is_valid_signer)
.help(NONCE_AUTHORITY_ARG.help)
}
pub trait NonceArgs {
fn nonce_args(self, global: bool) -> Self;
}
impl NonceArgs for App<'_, '_> {
fn nonce_args(self, global: bool) -> Self {
self.arg(nonce_arg().global(global)).arg(
nonce_authority_arg()
.requires(NONCE_ARG.name)
.global(global),
)
}
}

View File

@@ -1,5 +1,4 @@
use crate::{input_validators::*, ArgConstant}; use crate::ArgConstant;
use clap::{App, Arg};
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant { pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash", name: "blockhash",
@@ -18,61 +17,3 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
long: "signer", long: "signer",
help: "Provide a public-key/signature pair for the transaction", help: "Provide a public-key/signature pair for the transaction",
}; };
pub fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(BLOCKHASH_ARG.name)
.long(BLOCKHASH_ARG.long)
.takes_value(true)
.value_name("BLOCKHASH")
.validator(is_hash)
.help(BLOCKHASH_ARG.help)
}
pub fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(SIGN_ONLY_ARG.name)
.long(SIGN_ONLY_ARG.long)
.takes_value(false)
.requires(BLOCKHASH_ARG.name)
.help(SIGN_ONLY_ARG.help)
}
fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(SIGNER_ARG.name)
.long(SIGNER_ARG.long)
.takes_value(true)
.value_name("PUBKEY=SIGNATURE")
.validator(is_pubkey_sig)
.requires(BLOCKHASH_ARG.name)
.multiple(true)
.help(SIGNER_ARG.help)
}
pub trait ArgsConfig {
fn blockhash_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
arg
}
fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
arg
}
fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
arg
}
}
pub trait OfflineArgs {
fn offline_args(self) -> Self;
fn offline_args_config(self, config: &dyn ArgsConfig) -> Self;
}
impl OfflineArgs for App<'_, '_> {
fn offline_args_config(self, config: &dyn ArgsConfig) -> Self {
self.arg(config.blockhash_arg(blockhash_arg()))
.arg(config.sign_only_arg(sign_only_arg()))
.arg(config.signer_arg(signer_arg()))
}
fn offline_args(self) -> Self {
struct NullArgsConfig {}
impl ArgsConfig for NullArgsConfig {}
self.offline_args_config(&NullArgsConfig {})
}
}

View File

@@ -3,13 +3,13 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-cli-config" name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.5.1" version = "1.3.6"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
[dependencies] [dependencies]
dirs-next = "2.0.0" dirs = "2.0.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
serde = "1.0.112" serde = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"

View File

@@ -5,7 +5,7 @@ use url::Url;
lazy_static! { lazy_static! {
pub static ref CONFIG_FILE: Option<String> = { pub static ref CONFIG_FILE: Option<String> = {
dirs_next::home_dir().map(|mut path| { dirs::home_dir().map(|mut path| {
path.extend(&[".config", "solana", "cli", "config.yml"]); path.extend(&[".config", "solana", "cli", "config.yml"]);
path.to_str().unwrap().to_string() path.to_str().unwrap().to_string()
}) })
@@ -25,7 +25,7 @@ pub struct Config {
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
let keypair_path = { let keypair_path = {
let mut keypair_path = dirs_next::home_dir().expect("home directory"); let mut keypair_path = dirs::home_dir().expect("home directory");
keypair_path.extend(&[".config", "solana", "id.json"]); keypair_path.extend(&[".config", "solana", "id.json"]);
keypair_path.to_str().unwrap().to_string() keypair_path.to_str().unwrap().to_string()
}; };
@@ -76,6 +76,17 @@ impl Config {
ws_url.to_string() ws_url.to_string()
} }
pub fn compute_rpc_banks_url(json_rpc_url: &str) -> String {
let json_rpc_url: Option<Url> = json_rpc_url.parse().ok();
if json_rpc_url.is_none() {
return "".to_string();
}
let mut url = json_rpc_url.unwrap();
let port = url.port_or_known_default().unwrap_or(80);
url.set_port(Some(port + 2)).expect("unable to set port");
url.to_string()
}
pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error> pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error>
where where
P: AsRef<Path>, P: AsRef<Path>,
@@ -122,4 +133,28 @@ mod test {
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new()); assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
} }
#[test]
fn compute_rpc_banks_url() {
assert_eq!(
Config::compute_rpc_banks_url(&"http://devnet.solana.com"),
"http://devnet.solana.com:82/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
"https://devnet.solana.com:445/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"http://example.com:8899"),
"http://example.com:8901/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://example.com:1234"),
"https://example.com:1236/".to_string()
);
assert_eq!(Config::compute_rpc_banks_url(&"garbage"), String::new());
}
} }

View File

@@ -1,29 +0,0 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-cli-output"
description = "Blockchain, Rebuilt for Scale"
version = "1.5.1"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
chrono = { version = "0.4.11", features = ["serde"] }
console = "0.11.3"
humantime = "2.0.1"
Inflector = "0.11.4"
indicatif = "0.15.0"
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.5.1" }
solana-clap-utils = { path = "../clap-utils", version = "1.5.1" }
solana-client = { path = "../client", version = "1.5.1" }
solana-sdk = { path = "../sdk", version = "1.5.1" }
solana-stake-program = { path = "../programs/stake", version = "1.5.1" }
solana-transaction-status = { path = "../transaction-status", version = "1.5.1" }
solana-vote-program = { path = "../programs/vote", version = "1.5.1" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,15 +0,0 @@
mod cli_output;
pub mod display;
pub use cli_output::*;
pub trait QuietDisplay: std::fmt::Display {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "{}", self)
}
}
pub trait VerboseDisplay: std::fmt::Display {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "{}", self)
}
}

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-cli" name = "solana-cli"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.5.1" version = "1.3.6"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@@ -16,40 +16,40 @@ clap = "2.33.1"
criterion-stats = "0.3.0" criterion-stats = "0.3.0"
ctrlc = { version = "3.1.5", features = ["termination"] } ctrlc = { version = "3.1.5", features = ["termination"] }
console = "0.11.3" console = "0.11.3"
dirs-next = "2.0.0" dirs = "2.0.2"
log = "0.4.11" log = "0.4.8"
Inflector = "0.11.4" Inflector = "0.11.4"
indicatif = "0.15.0" indicatif = "0.15.0"
humantime = "2.0.1" humantime = "2.0.1"
num-traits = "0.2" num-traits = "0.2"
pretty-hex = "0.2.1" pretty-hex = "0.1.1"
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] } reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde = "1.0.112" serde = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.56" serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.5.1" } solana-account-decoder = { path = "../account-decoder", version = "1.3.6" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.5.1" } solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
solana-clap-utils = { path = "../clap-utils", version = "1.5.1" } solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
solana-cli-config = { path = "../cli-config", version = "1.5.1" } solana-cli-config = { path = "../cli-config", version = "1.3.6" }
solana-cli-output = { path = "../cli-output", version = "1.5.1" } solana-client = { path = "../client", version = "1.3.6" }
solana-client = { path = "../client", version = "1.5.1" } solana-config-program = { path = "../programs/config", version = "1.3.6" }
solana-config-program = { path = "../programs/config", version = "1.5.1" } solana-faucet = { path = "../faucet", version = "1.3.6" }
solana-faucet = { path = "../faucet", version = "1.5.1" } solana-logger = { path = "../logger", version = "1.3.6" }
solana-logger = { path = "../logger", version = "1.5.1" } solana-net-utils = { path = "../net-utils", version = "1.3.6" }
solana-net-utils = { path = "../net-utils", version = "1.5.1" } solana-remote-wallet = { path = "../remote-wallet", version = "1.3.6" }
solana_rbpf = "=0.2.2" solana-runtime = { path = "../runtime", version = "1.3.6" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.1" } solana-sdk = { path = "../sdk", version = "1.3.6" }
solana-sdk = { path = "../sdk", version = "1.5.1" } solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
solana-stake-program = { path = "../programs/stake", version = "1.5.1" } solana-transaction-status = { path = "../transaction-status", version = "1.3.6" }
solana-transaction-status = { path = "../transaction-status", version = "1.5.1" } solana-version = { path = "../version", version = "1.3.6" }
solana-version = { path = "../version", version = "1.5.1" } solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
solana-vote-program = { path = "../programs/vote", version = "1.5.1" } solana-vote-signer = { path = "../vote-signer", version = "1.3.6" }
thiserror = "1.0.21" thiserror = "1.0.20"
tiny-bip39 = "0.7.0"
url = "2.1.1" url = "2.1.1"
[dev-dependencies] [dev-dependencies]
solana-core = { path = "../core", version = "1.5.1" } solana-core = { path = "../core", version = "1.3.6" }
solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
tempfile = "3.1.0" tempfile = "3.1.0"
[[bin]] [[bin]]

View File

@@ -54,43 +54,13 @@ pub fn check_account_for_multiple_fees_with_commitment(
fee_calculator: &FeeCalculator, fee_calculator: &FeeCalculator,
messages: &[&Message], messages: &[&Message],
commitment: CommitmentConfig, commitment: CommitmentConfig,
) -> Result<(), CliError> {
check_account_for_spend_multiple_fees_with_commitment(
rpc_client,
account_pubkey,
0,
fee_calculator,
messages,
commitment,
)
}
pub fn check_account_for_spend_multiple_fees_with_commitment(
rpc_client: &RpcClient,
account_pubkey: &Pubkey,
balance: u64,
fee_calculator: &FeeCalculator,
messages: &[&Message],
commitment: CommitmentConfig,
) -> Result<(), CliError> { ) -> Result<(), CliError> {
let fee = calculate_fee(fee_calculator, messages); let fee = calculate_fee(fee_calculator, messages);
if !check_account_for_balance_with_commitment( if !check_account_for_balance_with_commitment(rpc_client, account_pubkey, fee, commitment)
rpc_client,
account_pubkey,
balance + fee,
commitment,
)
.map_err(Into::<ClientError>::into)? .map_err(Into::<ClientError>::into)?
{ {
if balance > 0 {
return Err(CliError::InsufficientFundsForSpendAndFee(
lamports_to_sol(balance),
lamports_to_sol(fee),
));
} else {
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee))); return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
} }
}
Ok(()) Ok(())
} }
@@ -161,7 +131,7 @@ mod tests {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1 },
value: json!(account_balance), value: json!(account_balance),
}); });
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let fee_calculator = FeeCalculator::new(1); let fee_calculator = FeeCalculator::new(1);
let pubkey0 = Pubkey::new(&[0; 32]); let pubkey0 = Pubkey::new(&[0; 32]);
@@ -221,7 +191,7 @@ mod tests {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1 },
value: json!(account_balance), value: json!(account_balance),
}); });
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();
mocks.insert(RpcRequest::GetBalance, account_balance_response); mocks.insert(RpcRequest::GetBalance, account_balance_response);
@@ -267,9 +237,9 @@ mod tests {
#[test] #[test]
fn test_check_unique_pubkeys() { fn test_check_unique_pubkeys() {
let pubkey0 = solana_sdk::pubkey::new_rand(); let pubkey0 = Pubkey::new_rand();
let pubkey_clone = pubkey0; let pubkey_clone = pubkey0;
let pubkey1 = solana_sdk::pubkey::new_rand(); let pubkey1 = Pubkey::new_rand();
check_unique_pubkeys((&pubkey0, "foo".to_string()), (&pubkey1, "bar".to_string())) check_unique_pubkeys((&pubkey0, "foo".to_string()), (&pubkey1, "bar".to_string()))
.expect("unexpected result"); .expect("unexpected result");

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,20 @@
use crate::{ use crate::{
display::{build_balance_message, format_labeled_address, writeln_name_value}, cli::build_balance_message,
QuietDisplay, VerboseDisplay, display::{format_labeled_address, writeln_name_value},
}; };
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc}; use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use console::{style, Emoji}; use console::{style, Emoji};
use inflector::cases::titlecase::to_title_case; use inflector::cases::titlecase::to_title_case;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use solana_account_decoder::parse_token::UiTokenAccount;
use solana_clap_utils::keypair::SignOnly;
use solana_client::rpc_response::{ use solana_client::rpc_response::{
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo, RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
}; };
use solana_sdk::{ use solana_sdk::{
clock::{self, Epoch, Slot, UnixTimestamp}, clock::{self, Epoch, Slot, UnixTimestamp},
epoch_info::EpochInfo, epoch_info::EpochInfo,
hash::Hash,
native_token::lamports_to_sol, native_token::lamports_to_sol,
pubkey::Pubkey,
signature::Signature,
stake_history::StakeHistoryEntry, stake_history::StakeHistoryEntry,
transaction::Transaction,
}; };
use solana_stake_program::stake_state::{Authorized, Lockup}; use solana_stake_program::stake_state::{Authorized, Lockup};
use solana_vote_program::{ use solana_vote_program::{
@@ -30,7 +24,6 @@ use solana_vote_program::{
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
fmt, fmt,
str::FromStr,
time::Duration, time::Duration,
}; };
@@ -41,27 +34,15 @@ pub enum OutputFormat {
Display, Display,
Json, Json,
JsonCompact, JsonCompact,
DisplayQuiet,
DisplayVerbose,
} }
impl OutputFormat { impl OutputFormat {
pub fn formatted_string<T>(&self, item: &T) -> String pub fn formatted_string<T>(&self, item: &T) -> String
where where
T: Serialize + fmt::Display + QuietDisplay + VerboseDisplay, T: Serialize + fmt::Display,
{ {
match self { match self {
OutputFormat::Display => format!("{}", item), OutputFormat::Display => format!("{}", item),
OutputFormat::DisplayQuiet => {
let mut s = String::new();
QuietDisplay::write_str(item, &mut s).unwrap();
s
}
OutputFormat::DisplayVerbose => {
let mut s = String::new();
VerboseDisplay::write_str(item, &mut s).unwrap();
s
}
OutputFormat::Json => serde_json::to_string_pretty(item).unwrap(), OutputFormat::Json => serde_json::to_string_pretty(item).unwrap(),
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(), OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
} }
@@ -76,9 +57,6 @@ pub struct CliAccount {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliAccount {}
impl VerboseDisplay for CliAccount {}
impl fmt::Display for CliAccount { impl fmt::Display for CliAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -121,9 +99,6 @@ pub struct CliBlockProduction {
pub verbose: bool, pub verbose: bool,
} }
impl QuietDisplay for CliBlockProduction {}
impl VerboseDisplay for CliBlockProduction {}
impl fmt::Display for CliBlockProduction { impl fmt::Display for CliBlockProduction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -132,7 +107,7 @@ impl fmt::Display for CliBlockProduction {
"{}", "{}",
style(format!( style(format!(
" {:<44} {:>15} {:>15} {:>15} {:>23}", " {:<44} {:>15} {:>15} {:>15} {:>23}",
"Identity", "Identity Pubkey",
"Leader Slots", "Leader Slots",
"Blocks Produced", "Blocks Produced",
"Skipped Slots", "Skipped Slots",
@@ -228,9 +203,6 @@ impl From<EpochInfo> for CliEpochInfo {
} }
} }
impl QuietDisplay for CliEpochInfo {}
impl VerboseDisplay for CliEpochInfo {}
impl fmt::Display for CliEpochInfo { impl fmt::Display for CliEpochInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -241,9 +213,6 @@ impl fmt::Display for CliEpochInfo {
)?; )?;
writeln_name_value(f, "Slot:", &self.epoch_info.absolute_slot.to_string())?; writeln_name_value(f, "Slot:", &self.epoch_info.absolute_slot.to_string())?;
writeln_name_value(f, "Epoch:", &self.epoch_info.epoch.to_string())?; writeln_name_value(f, "Epoch:", &self.epoch_info.epoch.to_string())?;
if let Some(transaction_count) = &self.epoch_info.transaction_count {
writeln_name_value(f, "Transaction Count:", &transaction_count.to_string())?;
}
let start_slot = self.epoch_info.absolute_slot - self.epoch_info.slot_index; let start_slot = self.epoch_info.absolute_slot - self.epoch_info.slot_index;
let end_slot = start_slot + self.epoch_info.slots_in_epoch; let end_slot = start_slot + self.epoch_info.slots_in_epoch;
writeln_name_value( writeln_name_value(
@@ -304,7 +273,7 @@ pub struct CliValidatorsStakeByVersion {
pub struct CliValidators { pub struct CliValidators {
pub total_active_stake: u64, pub total_active_stake: u64,
pub total_current_stake: u64, pub total_current_stake: u64,
pub total_delinquent_stake: u64, pub total_deliquent_stake: u64,
pub current_validators: Vec<CliValidator>, pub current_validators: Vec<CliValidator>,
pub delinquent_validators: Vec<CliValidator>, pub delinquent_validators: Vec<CliValidator>,
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>, pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
@@ -312,9 +281,6 @@ pub struct CliValidators {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliValidators {}
impl VerboseDisplay for CliValidators {}
impl fmt::Display for CliValidators { impl fmt::Display for CliValidators {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn write_vote_account( fn write_vote_account(
@@ -334,7 +300,7 @@ impl fmt::Display for CliValidators {
writeln!( writeln!(
f, f,
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>8} {}", "{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>17} {}",
if delinquent { if delinquent {
WARNING.to_string() WARNING.to_string()
} else { } else {
@@ -363,7 +329,7 @@ impl fmt::Display for CliValidators {
"Active Stake:", "Active Stake:",
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true), &build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
)?; )?;
if self.total_delinquent_stake > 0 { if self.total_deliquent_stake > 0 {
writeln_name_value( writeln_name_value(
f, f,
"Current Stake:", "Current Stake:",
@@ -379,11 +345,11 @@ impl fmt::Display for CliValidators {
&format!( &format!(
"{} ({:0.2}%)", "{} ({:0.2}%)",
&build_balance_message( &build_balance_message(
self.total_delinquent_stake, self.total_deliquent_stake,
self.use_lamports_unit, self.use_lamports_unit,
true true
), ),
100. * self.total_delinquent_stake as f64 / self.total_active_stake as f64 100. * self.total_deliquent_stake as f64 / self.total_active_stake as f64
), ),
)?; )?;
} }
@@ -393,7 +359,7 @@ impl fmt::Display for CliValidators {
for (version, info) in self.stake_by_version.iter() { for (version, info) in self.stake_by_version.iter() {
writeln!( writeln!(
f, f,
"{:<8} - {:3} current validators ({:>5.2}%){}", "{:<16} - {:3} current validators ({:>5.2}%){}",
version, version,
info.current_validators, info.current_validators,
100. * info.current_active_stake as f64 / self.total_active_stake as f64, 100. * info.current_active_stake as f64 / self.total_active_stake as f64,
@@ -414,9 +380,9 @@ impl fmt::Display for CliValidators {
f, f,
"{}", "{}",
style(format!( style(format!(
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}", " {:<44} {:<38} {} {} {} {:>10} {:^17} {}",
"Identity", "Identity Pubkey",
"Vote Account", "Vote Account Pubkey",
"Commission", "Commission",
"Last Vote", "Last Vote",
"Root Block", "Root Block",
@@ -503,9 +469,6 @@ pub struct CliNonceAccount {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliNonceAccount {}
impl VerboseDisplay for CliNonceAccount {}
impl fmt::Display for CliNonceAccount { impl fmt::Display for CliNonceAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!( writeln!(
@@ -523,7 +486,7 @@ impl fmt::Display for CliNonceAccount {
) )
)?; )?;
let nonce = self.nonce.as_deref().unwrap_or("uninitialized"); let nonce = self.nonce.as_deref().unwrap_or("uninitialized");
writeln!(f, "Nonce blockhash: {}", nonce)?; writeln!(f, "Nonce: {}", nonce)?;
if let Some(fees) = self.lamports_per_signature { if let Some(fees) = self.lamports_per_signature {
writeln!(f, "Fee: {} lamports per signature", fees)?; writeln!(f, "Fee: {} lamports per signature", fees)?;
} else { } else {
@@ -543,17 +506,6 @@ impl CliStakeVec {
} }
} }
impl QuietDisplay for CliStakeVec {}
impl VerboseDisplay for CliStakeVec {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
for state in &self.0 {
writeln!(w)?;
VerboseDisplay::write_str(state, w)?;
}
Ok(())
}
}
impl fmt::Display for CliStakeVec { impl fmt::Display for CliStakeVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for state in &self.0 { for state in &self.0 {
@@ -572,14 +524,6 @@ pub struct CliKeyedStakeState {
pub stake_state: CliStakeState, pub stake_state: CliStakeState,
} }
impl QuietDisplay for CliKeyedStakeState {}
impl VerboseDisplay for CliKeyedStakeState {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
writeln!(w, "Stake Pubkey: {}", self.stake_pubkey)?;
VerboseDisplay::write_str(&self.stake_state, w)
}
}
impl fmt::Display for CliKeyedStakeState { impl fmt::Display for CliKeyedStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?; writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?;
@@ -587,98 +531,12 @@ impl fmt::Display for CliKeyedStakeState {
} }
} }
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliEpochReward {
pub epoch: Epoch,
pub effective_slot: Slot,
pub amount: u64, // lamports
pub post_balance: u64, // lamports
pub percent_change: f64,
pub apr: Option<f64>,
}
fn show_votes_and_credits(
f: &mut fmt::Formatter,
votes: &[CliLockout],
epoch_voting_history: &[CliEpochVotingHistory],
) -> fmt::Result {
if votes.is_empty() {
return Ok(());
}
writeln!(f, "Recent Votes:")?;
for vote in votes {
writeln!(f, "- slot: {}", vote.slot)?;
writeln!(f, " confirmation count: {}", vote.confirmation_count)?;
}
writeln!(f, "Epoch Voting History:")?;
writeln!(
f,
"* missed credits include slots unavailable to vote on due to delinquent leaders",
)?;
for entry in epoch_voting_history {
writeln!(
f, // tame fmt so that this will be folded like following
"- epoch: {}",
entry.epoch
)?;
writeln!(
f,
" credits range: [{}..{})",
entry.prev_credits, entry.credits
)?;
writeln!(
f,
" credits/slots: {}/{}",
entry.credits_earned, entry.slots_in_epoch
)?;
}
Ok(())
}
fn show_epoch_rewards(
f: &mut fmt::Formatter,
epoch_rewards: &Option<Vec<CliEpochReward>>,
) -> fmt::Result {
if let Some(epoch_rewards) = epoch_rewards {
if epoch_rewards.is_empty() {
return Ok(());
}
writeln!(f, "Epoch Rewards:")?;
writeln!(
f,
" {:<6} {:<11} {:<16} {:<16} {:>14} {:>14}",
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR"
)?;
for reward in epoch_rewards {
writeln!(
f,
" {:<6} {:<11} ◎{:<16.9} ◎{:<14.9} {:>13.2}% {}",
reward.epoch,
reward.effective_slot,
lamports_to_sol(reward.amount),
lamports_to_sol(reward.post_balance),
reward.percent_change,
reward
.apr
.map(|apr| format!("{:>13.2}%", apr))
.unwrap_or_default(),
)?;
}
}
Ok(())
}
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CliStakeState { pub struct CliStakeState {
pub stake_type: CliStakeType, pub stake_type: CliStakeType,
pub account_balance: u64, pub account_balance: u64,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub credits_observed: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegated_stake: Option<u64>, pub delegated_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub delegated_vote_account_address: Option<String>, pub delegated_vote_account_address: Option<String>,
@@ -702,19 +560,6 @@ pub struct CliStakeState {
pub activating_stake: Option<u64>, pub activating_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub deactivating_stake: Option<u64>, pub deactivating_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub epoch_rewards: Option<Vec<CliEpochReward>>,
}
impl QuietDisplay for CliStakeState {}
impl VerboseDisplay for CliStakeState {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "{}", self)?;
if let Some(credits) = self.credits_observed {
writeln!(w, "Credits Observed: {}", credits)?;
}
Ok(())
}
} }
impl fmt::Display for CliStakeState { impl fmt::Display for CliStakeState {
@@ -729,8 +574,13 @@ impl fmt::Display for CliStakeState {
if lockup.unix_timestamp != UnixTimestamp::default() { if lockup.unix_timestamp != UnixTimestamp::default() {
writeln!( writeln!(
f, f,
"Lockup Timestamp: {}", "Lockup Timestamp: {} (UnixTimestamp: {})",
unix_timestamp_to_string(lockup.unix_timestamp) DateTime::<Utc>::from_utc(
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
Utc
)
.to_rfc3339_opts(SecondsFormat::Secs, true),
lockup.unix_timestamp
)?; )?;
} }
if lockup.epoch != Epoch::default() { if lockup.epoch != Epoch::default() {
@@ -860,14 +710,13 @@ impl fmt::Display for CliStakeState {
} }
show_authorized(f, self.authorized.as_ref().unwrap())?; show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref())?; show_lockup(f, self.lockup.as_ref())?;
show_epoch_rewards(f, &self.epoch_rewards)?
} }
} }
Ok(()) Ok(())
} }
} }
#[derive(Serialize, Deserialize, PartialEq)] #[derive(Serialize, Deserialize)]
pub enum CliStakeType { pub enum CliStakeType {
Stake, Stake,
RewardsPool, RewardsPool,
@@ -889,9 +738,6 @@ pub struct CliStakeHistory {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliStakeHistory {}
impl VerboseDisplay for CliStakeHistory {}
impl fmt::Display for CliStakeHistory { impl fmt::Display for CliStakeHistory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -986,9 +832,6 @@ impl CliValidatorInfoVec {
} }
} }
impl QuietDisplay for CliValidatorInfoVec {}
impl VerboseDisplay for CliValidatorInfoVec {}
impl fmt::Display for CliValidatorInfoVec { impl fmt::Display for CliValidatorInfoVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.0.is_empty() { if self.0.is_empty() {
@@ -1010,13 +853,10 @@ pub struct CliValidatorInfo {
pub info: Map<String, Value>, pub info: Map<String, Value>,
} }
impl QuietDisplay for CliValidatorInfo {}
impl VerboseDisplay for CliValidatorInfo {}
impl fmt::Display for CliValidatorInfo { impl fmt::Display for CliValidatorInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Validator Identity:", &self.identity_pubkey)?; writeln_name_value(f, "Validator Identity Pubkey:", &self.identity_pubkey)?;
writeln_name_value(f, " Info Address:", &self.info_pubkey)?; writeln_name_value(f, " Info Pubkey:", &self.info_pubkey)?;
for (key, value) in self.info.iter() { for (key, value) in self.info.iter() {
writeln_name_value( writeln_name_value(
f, f,
@@ -1044,13 +884,8 @@ pub struct CliVoteAccount {
pub epoch_voting_history: Vec<CliEpochVotingHistory>, pub epoch_voting_history: Vec<CliEpochVotingHistory>,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub epoch_rewards: Option<Vec<CliEpochReward>>,
} }
impl QuietDisplay for CliVoteAccount {}
impl VerboseDisplay for CliVoteAccount {}
impl fmt::Display for CliVoteAccount { impl fmt::Display for CliVoteAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!( writeln!(
@@ -1071,14 +906,25 @@ impl fmt::Display for CliVoteAccount {
None => "~".to_string(), None => "~".to_string(),
} }
)?; )?;
writeln!(f, "Recent Timestamp: {:?}", self.recent_timestamp)?;
if !self.votes.is_empty() {
writeln!(f, "Recent Votes:")?;
for vote in &self.votes {
writeln!( writeln!(
f, f,
"Recent Timestamp: {} from slot {}", "- slot: {}\n confirmation count: {}",
unix_timestamp_to_string(self.recent_timestamp.timestamp), vote.slot, vote.confirmation_count
self.recent_timestamp.slot
)?; )?;
show_votes_and_credits(f, &self.votes, &self.epoch_voting_history)?; }
show_epoch_rewards(f, &self.epoch_rewards)?; writeln!(f, "Epoch Voting History:")?;
for epoch_info in &self.epoch_voting_history {
writeln!(
f,
"- epoch: {}\n slots in epoch: {}\n credits earned: {}",
epoch_info.epoch, epoch_info.slots_in_epoch, epoch_info.credits_earned,
)?;
}
}
Ok(()) Ok(())
} }
} }
@@ -1089,9 +935,6 @@ pub struct CliAuthorizedVoters {
authorized_voters: BTreeMap<Epoch, String>, authorized_voters: BTreeMap<Epoch, String>,
} }
impl QuietDisplay for CliAuthorizedVoters {}
impl VerboseDisplay for CliAuthorizedVoters {}
impl fmt::Display for CliAuthorizedVoters { impl fmt::Display for CliAuthorizedVoters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.authorized_voters) write!(f, "{:?}", self.authorized_voters)
@@ -1116,8 +959,6 @@ pub struct CliEpochVotingHistory {
pub epoch: Epoch, pub epoch: Epoch,
pub slots_in_epoch: u64, pub slots_in_epoch: u64,
pub credits_earned: u64, pub credits_earned: u64,
pub credits: u64,
pub prev_credits: u64,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@@ -1143,25 +984,19 @@ pub struct CliBlockTime {
pub timestamp: UnixTimestamp, pub timestamp: UnixTimestamp,
} }
impl QuietDisplay for CliBlockTime {}
impl VerboseDisplay for CliBlockTime {}
fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
format!(
"{} (UnixTimestamp: {})",
match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
Some(ndt) =>
DateTime::<Utc>::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true),
None => "unknown".to_string(),
},
unix_timestamp,
)
}
impl fmt::Display for CliBlockTime { impl fmt::Display for CliBlockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Block:", &self.slot.to_string())?; writeln_name_value(f, "Block:", &self.slot.to_string())?;
writeln_name_value(f, "Date:", &unix_timestamp_to_string(self.timestamp)) writeln_name_value(
f,
"Date:",
&format!(
"{} (UnixTimestamp: {})",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.timestamp, 0), Utc)
.to_rfc3339_opts(SecondsFormat::Secs, true),
self.timestamp
),
)
} }
} }
@@ -1177,9 +1012,6 @@ pub struct CliSignOnlyData {
pub bad_sig: Vec<String>, pub bad_sig: Vec<String>,
} }
impl QuietDisplay for CliSignOnlyData {}
impl VerboseDisplay for CliSignOnlyData {}
impl fmt::Display for CliSignOnlyData { impl fmt::Display for CliSignOnlyData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -1212,9 +1044,6 @@ pub struct CliSignature {
pub signature: String, pub signature: String,
} }
impl QuietDisplay for CliSignature {}
impl VerboseDisplay for CliSignature {}
impl fmt::Display for CliSignature { impl fmt::Display for CliSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -1229,9 +1058,6 @@ pub struct CliAccountBalances {
pub accounts: Vec<RpcAccountBalance>, pub accounts: Vec<RpcAccountBalance>,
} }
impl QuietDisplay for CliAccountBalances {}
impl VerboseDisplay for CliAccountBalances {}
impl fmt::Display for CliAccountBalances { impl fmt::Display for CliAccountBalances {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!( writeln!(
@@ -1274,9 +1100,6 @@ impl From<RpcSupply> for CliSupply {
} }
} }
impl QuietDisplay for CliSupply {}
impl VerboseDisplay for CliSupply {}
impl fmt::Display for CliSupply { impl fmt::Display for CliSupply {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?; writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?;
@@ -1310,9 +1133,6 @@ pub struct CliFees {
pub last_valid_slot: Slot, pub last_valid_slot: Slot,
} }
impl QuietDisplay for CliFees {}
impl VerboseDisplay for CliFees {}
impl fmt::Display for CliFees { impl fmt::Display for CliFees {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Blockhash:", &self.blockhash)?; writeln_name_value(f, "Blockhash:", &self.blockhash)?;
@@ -1325,240 +1145,3 @@ impl fmt::Display for CliFees {
Ok(()) Ok(())
} }
} }
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliTokenAccount {
pub address: String,
#[serde(flatten)]
pub token_account: UiTokenAccount,
}
impl QuietDisplay for CliTokenAccount {}
impl VerboseDisplay for CliTokenAccount {}
impl fmt::Display for CliTokenAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln_name_value(f, "Address:", &self.address)?;
let account = &self.token_account;
writeln_name_value(
f,
"Balance:",
&account.token_amount.real_number_string_trimmed(),
)?;
let mint = format!(
"{}{}",
account.mint,
if account.is_native { " (native)" } else { "" }
);
writeln_name_value(f, "Mint:", &mint)?;
writeln_name_value(f, "Owner:", &account.owner)?;
writeln_name_value(f, "State:", &format!("{:?}", account.state))?;
if let Some(delegate) = &account.delegate {
writeln!(f, "Delegation:")?;
writeln_name_value(f, " Delegate:", delegate)?;
let allowance = account.delegated_amount.as_ref().unwrap();
writeln_name_value(f, " Allowance:", &allowance.real_number_string_trimmed())?;
}
writeln_name_value(
f,
"Close authority:",
&account.close_authority.as_ref().unwrap_or(&String::new()),
)?;
Ok(())
}
}
pub fn return_signers(
tx: &Transaction,
output_format: &OutputFormat,
) -> Result<String, Box<dyn std::error::Error>> {
let verify_results = tx.verify_with_results();
let mut signers = Vec::new();
let mut absent = Vec::new();
let mut bad_sig = Vec::new();
tx.signatures
.iter()
.zip(tx.message.account_keys.iter())
.zip(verify_results.into_iter())
.for_each(|((sig, key), res)| {
if res {
signers.push(format!("{}={}", key, sig))
} else if *sig == Signature::default() {
absent.push(key.to_string());
} else {
bad_sig.push(key.to_string());
}
});
let cli_command = CliSignOnlyData {
blockhash: tx.message.recent_blockhash.to_string(),
signers,
absent,
bad_sig,
};
Ok(output_format.formatted_string(&cli_command))
}
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
let object: Value = serde_json::from_str(&reply).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let blockhash = blockhash_str.parse::<Hash>().unwrap();
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
let signer_strings = object.get("signers");
if let Some(sig_strings) = signer_strings {
present_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
}
let mut absent_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("absent");
if let Some(sig_strings) = signer_strings {
absent_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
let mut bad_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("badSig");
if let Some(sig_strings) = signer_strings {
bad_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
SignOnly {
blockhash,
present_signers,
absent_signers,
bad_signers,
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::{
message::Message,
pubkey::Pubkey,
signature::{keypair_from_seed, NullSigner, Signature, Signer, SignerError},
system_instruction,
transaction::Transaction,
};
#[test]
fn test_return_signers() {
struct BadSigner {
pubkey: Pubkey,
}
impl BadSigner {
pub fn new(pubkey: Pubkey) -> Self {
Self { pubkey }
}
}
impl Signer for BadSigner {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(self.pubkey)
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::new(&[1u8; 64]))
}
}
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
let bad: Box<dyn Signer> = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32])));
let to = Pubkey::new(&[5u8; 32]);
let nonce = Pubkey::new(&[6u8; 32]);
let from = present.pubkey();
let fee_payer = absent.pubkey();
let nonce_auth = bad.pubkey();
let mut tx = Transaction::new_unsigned(Message::new_with_nonce(
vec![system_instruction::transfer(&from, &to, 42)],
Some(&fee_payer),
&nonce,
&nonce_auth,
));
let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()];
let blockhash = Hash::new(&[7u8; 32]);
tx.try_partial_sign(&signers, blockhash).unwrap();
let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap();
let sign_only = parse_sign_only_reply_string(&res);
assert_eq!(sign_only.blockhash, blockhash);
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
}
#[test]
fn test_verbose_quiet_output_formats() {
#[derive(Deserialize, Serialize)]
struct FallbackToDisplay {}
impl std::fmt::Display for FallbackToDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "display")
}
}
impl QuietDisplay for FallbackToDisplay {}
impl VerboseDisplay for FallbackToDisplay {}
let f = FallbackToDisplay {};
assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "display");
assert_eq!(
&OutputFormat::DisplayVerbose.formatted_string(&f),
"display"
);
#[derive(Deserialize, Serialize)]
struct DiscreteVerbosityDisplay {}
impl std::fmt::Display for DiscreteVerbosityDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "display")
}
}
impl QuietDisplay for DiscreteVerbosityDisplay {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "quiet")
}
}
impl VerboseDisplay for DiscreteVerbosityDisplay {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "verbose")
}
}
let f = DiscreteVerbosityDisplay {};
assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "quiet");
assert_eq!(
&OutputFormat::DisplayVerbose.formatted_string(&f),
"verbose"
);
}
}

View File

@@ -1,43 +1,25 @@
use crate::{ use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::*,
display::{format_labeled_address, new_spinner_progress_bar, println_name_value},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
stake::is_stake_program_v2_enabled,
}; };
use chrono::{Local, TimeZone};
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji}; use console::{style, Emoji};
use solana_clap_utils::{ use solana_clap_utils::{
commitment::{commitment_arg, commitment_arg_with_default}, commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path,
input_parsers::*,
input_validators::*,
keypair::DefaultSigner,
offline::{blockhash_arg, BLOCKHASH_ARG},
};
use solana_cli_output::{
display::{
format_labeled_address, new_spinner_progress_bar, println_name_value, println_transaction,
},
*,
}; };
use solana_client::{ use solana_client::{
client_error::ClientErrorKind, pubsub_client::{PubsubClient, SlotInfoMessage},
pubsub_client::PubsubClient,
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient}, rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
rpc_config::{ rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
RpcProgramAccountsConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter,
},
rpc_filter,
rpc_response::SlotInfo,
}; };
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::from_account,
account_utils::StateMut, account_utils::StateMut,
clock::{self, Clock, Slot}, clock::{self, Clock, Slot},
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
epoch_schedule::Epoch, epoch_schedule::Epoch,
hash::Hash,
message::Message, message::Message,
native_token::lamports_to_sol, native_token::lamports_to_sol,
pubkey::{self, Pubkey}, pubkey::{self, Pubkey},
@@ -45,11 +27,11 @@ use solana_sdk::{
system_instruction, system_program, system_instruction, system_program,
sysvar::{ sysvar::{
self, self,
stake_history::{self}, stake_history::{self, StakeHistory},
Sysvar,
}, },
transaction::Transaction, transaction::Transaction,
}; };
use solana_transaction_status::UiTransactionEncoding;
use std::{ use std::{
collections::{BTreeMap, HashMap, VecDeque}, collections::{BTreeMap, HashMap, VecDeque},
net::SocketAddr, net::SocketAddr,
@@ -58,7 +40,7 @@ use std::{
Arc, Arc,
}, },
thread::sleep, thread::sleep,
time::{Duration, Instant, SystemTime, UNIX_EPOCH}, time::{Duration, Instant},
}; };
static CHECK_MARK: Emoji = Emoji("", ""); static CHECK_MARK: Emoji = Emoji("", "");
@@ -71,18 +53,6 @@ pub trait ClusterQuerySubCommands {
impl ClusterQuerySubCommands for App<'_, '_> { impl ClusterQuerySubCommands for App<'_, '_> {
fn cluster_query_subcommands(self) -> Self { fn cluster_query_subcommands(self) -> Self {
self.subcommand( self.subcommand(
SubCommand::with_name("block")
.about("Get a confirmed block")
.arg(
Arg::with_name("slot")
.long("slot")
.validator(is_slot)
.value_name("SLOT")
.takes_value(true)
.index(1),
),
)
.subcommand(
SubCommand::with_name("catchup") SubCommand::with_name("catchup")
.about("Wait for a validator to catch up to the cluster") .about("Wait for a validator to catch up to the cluster")
.arg( .arg(
@@ -117,10 +87,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.about("Get the version of the cluster entrypoint"), .about("Get the version of the cluster entrypoint"),
) )
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees")) .subcommand(SubCommand::with_name("fees").about("Display current cluster fees"))
.subcommand(
SubCommand::with_name("first-available-block")
.about("Get the first available block in the storage"),
)
.subcommand(SubCommand::with_name("block-time") .subcommand(SubCommand::with_name("block-time")
.about("Get estimated production time of a block") .about("Get estimated production time of a block")
.alias("get-block-time") .alias("get-block-time")
@@ -132,17 +98,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.help("Slot number of the block to query") .help("Slot number of the block to query")
) )
) )
.subcommand(SubCommand::with_name("leader-schedule") .subcommand(SubCommand::with_name("leader-schedule").about("Display leader schedule"))
.about("Display leader schedule")
.arg(
Arg::with_name("epoch")
.long("epoch")
.takes_value(true)
.value_name("EPOCH")
.validator(is_epoch)
.help("Epoch to show leader schedule for. (default: current)")
)
)
.subcommand( .subcommand(
SubCommand::with_name("epoch-info") SubCommand::with_name("epoch-info")
.about("Get information about the current epoch") .about("Get information about the current epoch")
@@ -224,13 +180,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(true) .takes_value(true)
.help("Stop after submitting count transactions"), .help("Stop after submitting count transactions"),
) )
.arg(
Arg::with_name("print_timestamp")
.short("D")
.long("print-timestamp")
.takes_value(false)
.help("Print timestamp (unix time + microseconds as in gettimeofday) before each line"),
)
.arg( .arg(
Arg::with_name("lamports") Arg::with_name("lamports")
.long("lamports") .long("lamports")
@@ -249,33 +198,12 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.default_value("15") .default_value("15")
.help("Wait up to timeout seconds for transaction confirmation"), .help("Wait up to timeout seconds for transaction confirmation"),
) )
.arg(blockhash_arg())
.arg(commitment_arg()), .arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("live-slots") SubCommand::with_name("live-slots")
.about("Show information about the current slot progression"), .about("Show information about the current slot progression"),
) )
.subcommand(
SubCommand::with_name("logs")
.about("Stream transaction logs")
.arg(
pubkey!(Arg::with_name("address")
.index(1)
.value_name("ADDRESS"),
"Account address to monitor \
[default: monitor all transactions except for votes] \
")
)
.arg(
Arg::with_name("include_votes")
.long("include-votes")
.takes_value(false)
.conflicts_with("address")
.help("Include vote transactions when monitoring all transactions")
)
.arg(commitment_arg_with_default("singleGossip")),
)
.subcommand( .subcommand(
SubCommand::with_name("block-production") SubCommand::with_name("block-production")
.about("Show information about block production") .about("Show information about block production")
@@ -354,23 +282,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(true) .takes_value(true)
.help("Start with the first signature older than this one"), .help("Start with the first signature older than this one"),
) )
.arg(
Arg::with_name("show_transactions")
.long("show-transactions")
.takes_value(false)
.help("Display the full transactions"),
)
)
.subcommand(
SubCommand::with_name("wait-for-max-stake")
.about("Wait for the max stake of any one node to drop below a percentage of total.")
.arg(
Arg::with_name("max_percent")
.long("max-percent")
.value_name("PERCENT")
.takes_value(true)
.index(1),
),
) )
} }
} }
@@ -394,7 +305,7 @@ pub fn parse_catchup(
pub fn parse_cluster_ping( pub fn parse_cluster_ping(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let lamports = value_t_or_exit!(matches, "lamports", u64); let lamports = value_t_or_exit!(matches, "lamports", u64);
@@ -405,26 +316,19 @@ pub fn parse_cluster_ping(
None None
}; };
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64)); let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
let print_timestamp = matches.is_present("print_timestamp");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Ping { command: CliCommand::Ping {
lamports, lamports,
interval, interval,
count, count,
timeout, timeout,
blockhash,
print_timestamp,
}, },
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], signers: vec![signer_from_path(
}) matches,
} default_signer_path,
"keypair",
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { wallet_manager,
let slot = value_of(matches, "slot"); )?],
Ok(CliCommandInfo {
command: CliCommand::GetBlock { slot },
signers: vec![],
}) })
} }
@@ -549,7 +453,6 @@ pub fn parse_transaction_history(
None => None, None => None,
}; };
let limit = value_t_or_exit!(matches, "limit", usize); let limit = value_t_or_exit!(matches, "limit", usize);
let show_transactions = matches.is_present("show_transactions");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::TransactionHistory { command: CliCommand::TransactionHistory {
@@ -557,7 +460,6 @@ pub fn parse_transaction_history(
before, before,
until, until,
limit, limit,
show_transactions,
}, },
signers: vec![], signers: vec![],
}) })
@@ -600,20 +502,7 @@ pub fn process_catchup(
RpcClient::new_socket(rpc_addr) RpcClient::new_socket(rpc_addr)
}; };
let reported_node_pubkey = loop { let reported_node_pubkey = node_client.get_identity()?;
match node_client.get_identity() {
Ok(reported_node_pubkey) => break reported_node_pubkey,
Err(err) => {
if let ClientErrorKind::Reqwest(err) = err.kind() {
progress_bar.set_message(&format!("Connection failed: {}", err));
sleep(Duration::from_secs(sleep_interval as u64));
continue;
}
return Err(Box::new(err));
}
}
};
if reported_node_pubkey != *node_pubkey { if reported_node_pubkey != *node_pubkey {
return Err(format!( return Err(format!(
"The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}", "The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}",
@@ -683,7 +572,7 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
let result = rpc_client let result = rpc_client
.get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?; .get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?;
if let Some(clock_account) = result.value { if let Some(clock_account) = result.value {
let clock: Clock = from_account(&clock_account).ok_or_else(|| { let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string()) CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?; })?;
let block_time = CliBlockTime { let block_time = CliBlockTime {
@@ -696,14 +585,9 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
} }
} }
pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult { pub fn process_cluster_version(rpc_client: &RpcClient) -> ProcessResult {
let remote_version = rpc_client.get_version()?; let remote_version = rpc_client.get_version()?;
Ok(remote_version.solana_core)
if config.verbose {
Ok(format!("{:?}", remote_version))
} else {
Ok(remote_version.to_string())
}
} }
pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult { pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
@@ -718,28 +602,9 @@ pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult
Ok(config.output_format.formatted_string(&fees)) Ok(config.output_format.formatted_string(&fees))
} }
pub fn process_first_available_block(rpc_client: &RpcClient) -> ProcessResult { pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
let first_available_block = rpc_client.get_first_available_block()?;
Ok(format!("{}", first_available_block))
}
pub fn parse_leader_schedule(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let epoch = value_of(matches, "epoch");
Ok(CliCommandInfo {
command: CliCommand::LeaderSchedule { epoch },
signers: vec![],
})
}
pub fn process_leader_schedule(rpc_client: &RpcClient, epoch: Option<Epoch>) -> ProcessResult {
let epoch_info = rpc_client.get_epoch_info()?; let epoch_info = rpc_client.get_epoch_info()?;
let epoch = epoch.unwrap_or(epoch_info.epoch); let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
if epoch > epoch_info.epoch {
return Err(format!("Epoch {} is in the future", epoch).into());
}
let epoch_schedule = rpc_client.get_epoch_schedule()?;
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
let leader_schedule = rpc_client.get_leader_schedule(Some(first_slot_in_epoch))?; let leader_schedule = rpc_client.get_leader_schedule(Some(first_slot_in_epoch))?;
if leader_schedule.is_none() { if leader_schedule.is_none() {
@@ -772,84 +637,6 @@ pub fn process_leader_schedule(rpc_client: &RpcClient, epoch: Option<Epoch>) ->
Ok("".to_string()) Ok("".to_string())
} }
pub fn process_get_block(
rpc_client: &RpcClient,
_config: &CliConfig,
slot: Option<Slot>,
) -> ProcessResult {
let slot = if let Some(slot) = slot {
slot
} else {
rpc_client.get_slot()?
};
let mut block =
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
println!("Slot: {}", slot);
println!("Parent Slot: {}", block.parent_slot);
println!("Blockhash: {}", block.blockhash);
println!("Previous Blockhash: {}", block.previous_blockhash);
if let Some(block_time) = block.block_time {
println!("Block Time: {:?}", Local.timestamp(block_time, 0));
}
if !block.rewards.is_empty() {
block.rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
let mut total_rewards = 0;
println!("Rewards:",);
println!(
" {:<44} {:^15} {:<15} {:<20} {:>14}",
"Address", "Type", "Amount", "New Balance", "Percent Change"
);
for reward in block.rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
total_rewards += reward.lamports;
println!(
" {:<44} {:^15} {:>15} {}",
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!(
"{}{:<14.9}",
sign,
lamports_to_sol(reward.lamports.abs() as u64)
),
if reward.post_balance == 0 {
" - -".to_string()
} else {
format!(
"{:<19.9} {:>13.9}%",
lamports_to_sol(reward.post_balance),
(reward.lamports.abs() as f64
/ (reward.post_balance as f64 - reward.lamports as f64))
* 100.0
)
}
);
}
let sign = if total_rewards < 0 { "-" } else { "" };
println!(
"Total Rewards: {}{:<12.9}",
sign,
lamports_to_sol(total_rewards.abs() as u64)
);
}
for (index, transaction_with_meta) in block.transactions.iter().enumerate() {
println!("Transaction {}:", index);
println_transaction(
&transaction_with_meta.transaction.decode().unwrap(),
&transaction_with_meta.meta,
" ",
);
}
Ok("".to_string())
}
pub fn process_get_block_time( pub fn process_get_block_time(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
@@ -911,7 +698,7 @@ pub fn process_show_block_production(
slot_limit: Option<u64>, slot_limit: Option<u64>,
) -> ProcessResult { ) -> ProcessResult {
let epoch_schedule = rpc_client.get_epoch_schedule()?; let epoch_schedule = rpc_client.get_epoch_schedule()?;
let epoch_info = rpc_client.get_epoch_info_with_commitment(CommitmentConfig::max())?; let epoch_info = rpc_client.get_epoch_info_with_commitment(CommitmentConfig::root())?;
let epoch = epoch.unwrap_or(epoch_info.epoch); let epoch = epoch.unwrap_or(epoch_info.epoch);
if epoch > epoch_info.epoch { if epoch > epoch_info.epoch {
@@ -1099,8 +886,6 @@ pub fn process_ping(
interval: &Duration, interval: &Duration,
count: &Option<u64>, count: &Option<u64>,
timeout: &Duration, timeout: &Duration,
fixed_blockhash: &Option<Hash>,
print_timestamp: bool,
) -> ProcessResult { ) -> ProcessResult {
println_name_value("Source Account:", &config.signers[0].pubkey().to_string()); println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
println!(); println!();
@@ -1118,21 +903,9 @@ pub fn process_ping(
let (mut blockhash, mut fee_calculator) = rpc_client.get_recent_blockhash()?; let (mut blockhash, mut fee_calculator) = rpc_client.get_recent_blockhash()?;
let mut blockhash_transaction_count = 0; let mut blockhash_transaction_count = 0;
let mut blockhash_acquired = Instant::now(); let mut blockhash_acquired = Instant::now();
if let Some(fixed_blockhash) = fixed_blockhash {
let blockhash_origin = if *fixed_blockhash != Hash::default() {
blockhash = *fixed_blockhash;
"supplied from cli arguments"
} else {
"fetched from cluster"
};
println!(
"Fixed blockhash is used: {} ({})",
blockhash, blockhash_origin
);
}
'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) { 'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) {
let now = Instant::now(); let now = Instant::now();
if fixed_blockhash.is_none() && now.duration_since(blockhash_acquired).as_secs() > 60 { if now.duration_since(blockhash_acquired).as_secs() > 60 {
// Fetch a new blockhash every minute // Fetch a new blockhash every minute
let (new_blockhash, new_fee_calculator) = rpc_client.get_new_blockhash(&blockhash)?; let (new_blockhash, new_fee_calculator) = rpc_client.get_new_blockhash(&blockhash)?;
blockhash = new_blockhash; blockhash = new_blockhash;
@@ -1163,18 +936,6 @@ pub fn process_ping(
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, blockhash)?; tx.try_sign(&config.signers, blockhash)?;
let timestamp = || {
let micros = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_micros();
if print_timestamp {
format!("[{}.{:06}] ", micros / 1_000_000, micros % 1_000_000)
} else {
format!("")
}
};
match rpc_client.send_transaction(&tx) { match rpc_client.send_transaction(&tx) {
Ok(signature) => { Ok(signature) => {
let transaction_sent = Instant::now(); let transaction_sent = Instant::now();
@@ -1188,20 +949,15 @@ pub fn process_ping(
let elapsed_time_millis = elapsed_time.as_millis() as u64; let elapsed_time_millis = elapsed_time.as_millis() as u64;
confirmation_time.push_back(elapsed_time_millis); confirmation_time.push_back(elapsed_time_millis);
println!( println!(
"{}{}{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}", "{}{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
timestamp(),
CHECK_MARK, lamports, seq, elapsed_time_millis, signature CHECK_MARK, lamports, seq, elapsed_time_millis, signature
); );
confirmed_count += 1; confirmed_count += 1;
} }
Err(err) => { Err(err) => {
println!( println!(
"{}{}Transaction failed: seq={:<3} error={:?} signature={}", "{}Transaction failed: seq={:<3} error={:?} signature={}",
timestamp(), CROSS_MARK, seq, err, signature
CROSS_MARK,
seq,
err,
signature
); );
} }
} }
@@ -1210,11 +966,8 @@ pub fn process_ping(
if elapsed_time >= *timeout { if elapsed_time >= *timeout {
println!( println!(
"{}{}Confirmation timeout: seq={:<3} signature={}", "{}Confirmation timeout: seq={:<3} signature={}",
timestamp(), CROSS_MARK, seq, signature
CROSS_MARK,
seq,
signature
); );
break; break;
} }
@@ -1232,11 +985,8 @@ pub fn process_ping(
} }
Err(err) => { Err(err) => {
println!( println!(
"{}{}Submit failed: seq={:<3} error={:?}", "{}Submit failed: seq={:<3} error={:?}",
timestamp(), CROSS_MARK, seq, err
CROSS_MARK,
seq,
err
); );
} }
} }
@@ -1271,83 +1021,24 @@ pub fn process_ping(
Ok("".to_string()) Ok("".to_string())
} }
pub fn parse_logs( pub fn process_live_slots(url: &str) -> ProcessResult {
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let address = pubkey_of_signer(matches, "address", wallet_manager)?;
let include_votes = matches.is_present("include_votes");
let filter = match address {
None => {
if include_votes {
RpcTransactionLogsFilter::AllWithVotes
} else {
RpcTransactionLogsFilter::All
}
}
Some(address) => RpcTransactionLogsFilter::Mentions(vec![address.to_string()]),
};
Ok(CliCommandInfo {
command: CliCommand::Logs { filter },
signers: vec![],
})
}
pub fn process_logs(config: &CliConfig, filter: &RpcTransactionLogsFilter) -> ProcessResult {
println!(
"Streaming transaction logs{}. {:?} commitment",
match filter {
RpcTransactionLogsFilter::All => "".into(),
RpcTransactionLogsFilter::AllWithVotes => " (including votes)".into(),
RpcTransactionLogsFilter::Mentions(addresses) =>
format!(" mentioning {}", addresses.join(",")),
},
config.commitment.commitment
);
let (_client, receiver) = PubsubClient::logs_subscribe(
&config.websocket_url,
filter.clone(),
RpcTransactionLogsConfig {
commitment: Some(config.commitment),
},
)?;
loop {
match receiver.recv() {
Ok(logs) => {
println!("Transaction executed in slot {}:", logs.context.slot);
println!(" Signature: {}", logs.value.signature);
println!(
" Status: {}",
logs.value
.err
.map(|err| err.to_string())
.unwrap_or_else(|| "Ok".to_string())
);
println!(" Log Messages:");
for log in logs.value.logs {
println!(" {}", log);
}
}
Err(err) => {
return Ok(format!("Disconnected: {}", err));
}
}
}
}
pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let mut current: Option<SlotInfo> = None; // Disable Ctrl+C handler as sometimes the PubsubClient shutdown can stall. Also it doesn't
// really matter that the shutdown is clean because the process is terminating.
/*
let exit_clone = exit.clone();
ctrlc::set_handler(move || {
exit_clone.store(true, Ordering::Relaxed);
})?;
*/
let mut current: Option<SlotInfoMessage> = None;
let mut message = "".to_string(); let mut message = "".to_string();
let slot_progress = new_spinner_progress_bar(); let slot_progress = new_spinner_progress_bar();
slot_progress.set_message("Connecting..."); slot_progress.set_message("Connecting...");
let (mut client, receiver) = PubsubClient::slot_subscribe(&config.websocket_url)?; let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
slot_progress.set_message("Connected."); slot_progress.set_message("Connected.");
let spacer = "|"; let spacer = "|";
@@ -1437,16 +1128,14 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
.into_iter() .into_iter()
.map(|node| { .map(|node| {
format!( format!(
"{:15} | {:44} | {:6} | {:5} | {:21} | {}", "{:15} | {:44} | {:6} | {:5} | {:5} | {}",
node.gossip node.gossip
.map(|addr| addr.ip().to_string()) .map(|addr| addr.ip().to_string())
.unwrap_or_else(|| "none".to_string()), .unwrap_or_else(|| "none".to_string()),
format_labeled_address(&node.pubkey, &config.address_labels), format_labeled_address(&node.pubkey, &config.address_labels),
format_port(node.gossip), format_port(node.gossip),
format_port(node.tpu), format_port(node.tpu),
node.rpc format_port(node.rpc),
.map(|addr| addr.to_string())
.unwrap_or_else(|| "none".to_string()),
node.version.unwrap_or_else(|| "unknown".to_string()), node.version.unwrap_or_else(|| "unknown".to_string()),
) )
}) })
@@ -1454,9 +1143,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
Ok(format!( Ok(format!(
"IP Address | Node identifier \ "IP Address | Node identifier \
| Gossip | TPU | RPC Address | Version\n\ | Gossip | TPU | RPC | Version\n\
----------------+----------------------------------------------+\ ----------------+----------------------------------------------+\
--------+-------+-----------------------+----------------\n\ --------+-------+-------+----------------\n\
{}\n\ {}\n\
Nodes: {}", Nodes: {}",
s.join("\n"), s.join("\n"),
@@ -1475,52 +1164,17 @@ pub fn process_show_stakes(
let progress_bar = new_spinner_progress_bar(); let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching stake accounts..."); progress_bar.set_message("Fetching stake accounts...");
let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
let mut program_accounts_config = RpcProgramAccountsConfig {
filters: None,
account_config: RpcAccountInfoConfig {
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
..RpcAccountInfoConfig::default()
},
};
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
// Use server-side filtering if only one vote account is provided
if vote_account_pubkeys.len() == 1 {
program_accounts_config.filters = Some(vec![
// Filter by `StakeState::Stake(_, _)`
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
offset: 0,
bytes: rpc_filter::MemcmpEncodedBytes::Binary(
bs58::encode([2, 0, 0, 0]).into_string(),
),
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
}),
// Filter by `Delegation::voter_pubkey`, which begins at byte offset 124
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
offset: 124,
bytes: rpc_filter::MemcmpEncodedBytes::Binary(
vote_account_pubkeys[0].to_string(),
),
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
}),
]);
}
}
let all_stake_accounts = rpc_client
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
let stake_history_account = rpc_client.get_account(&stake_history::id())?; 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(|| {
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?;
progress_bar.finish_and_clear(); progress_bar.finish_and_clear();
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
let stake_history = from_account(&stake_history_account).ok_or_else(|| { let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize stake history".to_string()) CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?; })?;
// At v1.6, this check can be removed and simply passed as `true` let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
let stake_program_v2_enabled = is_stake_program_v2_enabled(rpc_client); CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?;
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![]; let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
for (stake_pubkey, stake_account) in all_stake_accounts { for (stake_pubkey, stake_account) in all_stake_accounts {
@@ -1536,7 +1190,6 @@ pub fn process_show_stakes(
use_lamports_unit, use_lamports_unit,
&stake_history, &stake_history,
&clock, &clock,
stake_program_v2_enabled,
), ),
}); });
} }
@@ -1555,7 +1208,6 @@ pub fn process_show_stakes(
use_lamports_unit, use_lamports_unit,
&stake_history, &stake_history,
&clock, &clock,
stake_program_v2_enabled,
), ),
}); });
} }
@@ -1569,16 +1221,6 @@ pub fn process_show_stakes(
.formatted_string(&CliStakeVec::new(stake_accounts))) .formatted_string(&CliStakeVec::new(stake_accounts)))
} }
pub fn process_wait_for_max_stake(
rpc_client: &RpcClient,
config: &CliConfig,
max_stake_percent: f32,
) -> ProcessResult {
let now = std::time::Instant::now();
rpc_client.wait_for_max_stake(config.commitment, max_stake_percent)?;
Ok(format!("Done waiting, took: {}s", now.elapsed().as_secs()))
}
pub fn process_show_validators( pub fn process_show_validators(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
@@ -1605,12 +1247,12 @@ pub fn process_show_validators(
.map(|vote_account| vote_account.activated_stake) .map(|vote_account| vote_account.activated_stake)
.sum(); .sum();
let total_delinquent_stake = vote_accounts let total_deliquent_stake = vote_accounts
.delinquent .delinquent
.iter() .iter()
.map(|vote_account| vote_account.activated_stake) .map(|vote_account| vote_account.activated_stake)
.sum(); .sum();
let total_current_stake = total_active_stake - total_delinquent_stake; let total_current_stake = total_active_stake - total_deliquent_stake;
let mut current = vote_accounts.current; let mut current = vote_accounts.current;
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake)); current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
@@ -1664,7 +1306,7 @@ pub fn process_show_validators(
let cli_validators = CliValidators { let cli_validators = CliValidators {
total_active_stake, total_active_stake,
total_current_stake, total_current_stake,
total_delinquent_stake, total_deliquent_stake,
current_validators, current_validators,
delinquent_validators, delinquent_validators,
stake_by_version, stake_by_version,
@@ -1680,7 +1322,6 @@ pub fn process_transaction_history(
before: Option<Signature>, before: Option<Signature>,
until: Option<Signature>, until: Option<Signature>,
limit: usize, limit: usize,
show_transactions: bool,
) -> ProcessResult { ) -> ProcessResult {
let results = rpc_client.get_confirmed_signatures_for_address2_with_config( let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
address, address,
@@ -1708,28 +1349,6 @@ pub fn process_transaction_history(
} else { } else {
println!("{}", result.signature); println!("{}", result.signature);
} }
if show_transactions {
if let Ok(signature) = result.signature.parse::<Signature>() {
match rpc_client
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
{
Ok(confirmed_transaction) => {
println_transaction(
&confirmed_transaction
.transaction
.transaction
.decode()
.expect("Successful decode"),
&confirmed_transaction.transaction.meta,
" ",
);
}
Err(err) => println!(" Unable to get confirmed transaction details: {}", err),
}
}
println!();
}
} }
Ok(transactions_found) Ok(transactions_found)
} }
@@ -1739,7 +1358,6 @@ mod tests {
use super::*; use super::*;
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::signature::{write_keypair, Keypair}; use solana_sdk::signature::{write_keypair, Keypair};
use std::str::FromStr;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) { fn make_tmp_file() -> (String, NamedTempFile) {
@@ -1753,16 +1371,12 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner {
path: default_keypair_file,
arg_name: String::new(),
};
let test_cluster_version = test_commands let test_cluster_version = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "cluster-date"]); .get_matches_from(vec!["test", "cluster-date"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(), parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterDate, command: CliCommand::ClusterDate,
signers: vec![], signers: vec![],
@@ -1773,7 +1387,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "cluster-version"]); .get_matches_from(vec!["test", "cluster-version"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(), parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterVersion, command: CliCommand::ClusterVersion,
signers: vec![], signers: vec![],
@@ -1782,7 +1396,7 @@ mod tests {
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]); let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!( assert_eq!(
parse_command(&test_fees, &default_signer, &mut None).unwrap(), parse_command(&test_fees, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Fees, command: CliCommand::Fees,
signers: vec![], signers: vec![],
@@ -1795,7 +1409,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "block-time", &slot.to_string()]); .get_matches_from(vec!["test", "block-time", &slot.to_string()]);
assert_eq!( assert_eq!(
parse_command(&test_get_block_time, &default_signer, &mut None).unwrap(), parse_command(&test_get_block_time, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetBlockTime { slot: Some(slot) }, command: CliCommand::GetBlockTime { slot: Some(slot) },
signers: vec![], signers: vec![],
@@ -1806,7 +1420,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch"]); .get_matches_from(vec!["test", "epoch"]);
assert_eq!( assert_eq!(
parse_command(&test_get_epoch, &default_signer, &mut None).unwrap(), parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetEpoch, command: CliCommand::GetEpoch,
signers: vec![], signers: vec![],
@@ -1817,7 +1431,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch-info"]); .get_matches_from(vec!["test", "epoch-info"]);
assert_eq!( assert_eq!(
parse_command(&test_get_epoch_info, &default_signer, &mut None).unwrap(), parse_command(&test_get_epoch_info, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetEpochInfo, command: CliCommand::GetEpochInfo,
signers: vec![], signers: vec![],
@@ -1828,7 +1442,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "genesis-hash"]); .get_matches_from(vec!["test", "genesis-hash"]);
assert_eq!( assert_eq!(
parse_command(&test_get_genesis_hash, &default_signer, &mut None).unwrap(), parse_command(&test_get_genesis_hash, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetGenesisHash, command: CliCommand::GetGenesisHash,
signers: vec![], signers: vec![],
@@ -1837,7 +1451,7 @@ mod tests {
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]); let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
assert_eq!( assert_eq!(
parse_command(&test_get_slot, &default_signer, &mut None).unwrap(), parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetSlot, command: CliCommand::GetSlot,
signers: vec![], signers: vec![],
@@ -1848,7 +1462,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "total-supply"]); .get_matches_from(vec!["test", "total-supply"]);
assert_eq!( assert_eq!(
parse_command(&test_total_supply, &default_signer, &mut None).unwrap(), parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::TotalSupply, command: CliCommand::TotalSupply,
signers: vec![], signers: vec![],
@@ -1859,7 +1473,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "transaction-count"]); .get_matches_from(vec!["test", "transaction-count"]);
assert_eq!( assert_eq!(
parse_command(&test_transaction_count, &default_signer, &mut None).unwrap(), parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetTransactionCount, command: CliCommand::GetTransactionCount,
signers: vec![], signers: vec![],
@@ -1875,24 +1489,17 @@ mod tests {
"2", "2",
"-t", "-t",
"3", "3",
"-D",
"--commitment", "--commitment",
"max", "max",
"--blockhash",
"4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_ping, &default_signer, &mut None).unwrap(), parse_command(&test_ping, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Ping { command: CliCommand::Ping {
lamports: 1, lamports: 1,
interval: Duration::from_secs(1), interval: Duration::from_secs(1),
count: Some(2), count: Some(2),
timeout: Duration::from_secs(3), timeout: Duration::from_secs(3),
blockhash: Some(
Hash::from_str("4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX").unwrap()
),
print_timestamp: true,
}, },
signers: vec![default_keypair.into()], signers: vec![default_keypair.into()],
} }

View File

@@ -1,3 +1,4 @@
use crate::cli::SettingType;
use console::style; use console::style;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use solana_sdk::{ use solana_sdk::{
@@ -7,27 +8,9 @@ use solana_sdk::{
use solana_transaction_status::UiTransactionStatusMeta; use solana_transaction_status::UiTransactionStatusMeta;
use std::{collections::HashMap, fmt, io}; use std::{collections::HashMap, fmt, io};
pub fn build_balance_message(lamports: u64, use_lamports_unit: bool, show_unit: bool) -> String {
if use_lamports_unit {
let ess = if lamports == 1 { "" } else { "s" };
let unit = if show_unit {
format!(" lamport{}", ess)
} else {
"".to_string()
};
format!("{:?}{}", lamports, unit)
} else {
let sol = lamports_to_sol(lamports);
let sol_str = format!("{:.9}", sol);
let pretty_sol = sol_str.trim_end_matches('0').trim_end_matches('.');
let unit = if show_unit { " SOL" } else { "" };
format!("{}{}", pretty_sol, unit)
}
}
// Pretty print a "name value" // Pretty print a "name value"
pub fn println_name_value(name: &str, value: &str) { pub fn println_name_value(name: &str, value: &str) {
let styled_value = if value.is_empty() { let styled_value = if value == "" {
style("(not set)").italic() style("(not set)").italic()
} else { } else {
style(value) style(value)
@@ -36,7 +19,7 @@ pub fn println_name_value(name: &str, value: &str) {
} }
pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fmt::Result { pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fmt::Result {
let styled_value = if value.is_empty() { let styled_value = if value == "" {
style("(not set)").italic() style("(not set)").italic()
} else { } else {
style(value) style(value)
@@ -57,6 +40,21 @@ pub fn format_labeled_address(pubkey: &str, address_labels: &HashMap<String, Str
} }
} }
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
let description = match setting_type {
SettingType::Explicit => "",
SettingType::Computed => "(computed)",
SettingType::SystemDefault => "(default)",
};
println!(
"{} {} {}",
style(name).bold(),
style(value),
style(description).italic(),
);
}
pub fn println_signers( pub fn println_signers(
blockhash: &Hash, blockhash: &Hash,
signers: &[String], signers: &[String],
@@ -164,7 +162,7 @@ pub fn write_transaction<W: io::Write>(
)?; )?;
writeln!( writeln!(
w, w,
"{} Fee: {}", "{} Fee: {} SOL",
prefix, prefix,
lamports_to_sol(transaction_status.fee) lamports_to_sol(transaction_status.fee)
)?; )?;
@@ -181,7 +179,7 @@ pub fn write_transaction<W: io::Write>(
if pre == post { if pre == post {
writeln!( writeln!(
w, w,
"{} Account {} balance: {}", "{} Account {} balance: {} SOL",
prefix, prefix,
i, i,
lamports_to_sol(*pre) lamports_to_sol(*pre)
@@ -189,7 +187,7 @@ pub fn write_transaction<W: io::Write>(
} else { } else {
writeln!( writeln!(
w, w,
"{} Account {} balance: {} -> {}", "{} Account {} balance: {} SOL -> {} SOL",
prefix, prefix,
i, i,
lamports_to_sol(*pre), lamports_to_sol(*pre),
@@ -197,15 +195,6 @@ pub fn write_transaction<W: io::Write>(
)?; )?;
} }
} }
if let Some(log_messages) = &transaction_status.log_messages {
if !log_messages.is_empty() {
writeln!(w, "{}Log Messages:", prefix,)?;
for log_message in log_messages {
writeln!(w, "{} {}", prefix, log_message,)?;
}
}
}
} else { } else {
writeln!(w, "{}Status: Unavailable", prefix)?; writeln!(w, "{}Status: Unavailable", prefix)?;
} }

View File

@@ -1,379 +0,0 @@
use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
};
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use console::style;
use serde::{Deserialize, Serialize};
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::*};
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::{
clock::Slot,
feature::{self, Feature},
feature_set::FEATURE_NAMES,
message::Message,
pubkey::Pubkey,
transaction::Transaction,
};
use std::{collections::HashMap, fmt, sync::Arc};
#[derive(Debug, PartialEq)]
pub enum FeatureCliCommand {
Status { features: Vec<Pubkey> },
Activate { feature: Pubkey },
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "status", content = "sinceSlot")]
pub enum CliFeatureStatus {
Inactive,
Pending,
Active(Slot),
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliFeature {
pub id: String,
pub description: String,
#[serde(flatten)]
pub status: CliFeatureStatus,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliFeatures {
pub features: Vec<CliFeature>,
pub feature_activation_allowed: bool,
#[serde(skip)]
pub inactive: bool,
}
impl fmt::Display for CliFeatures {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.features.len() > 1 {
writeln!(
f,
"{}",
style(format!(
"{:<44} {:<40} {}",
"Feature", "Description", "Status"
))
.bold()
)?;
}
for feature in &self.features {
writeln!(
f,
"{:<44} {:<40} {}",
feature.id,
feature.description,
match feature.status {
CliFeatureStatus::Inactive => style("inactive".to_string()).red(),
CliFeatureStatus::Pending => style("activation pending".to_string()).yellow(),
CliFeatureStatus::Active(activation_slot) =>
style(format!("active since slot {}", activation_slot)).green(),
}
)?;
}
if self.inactive && !self.feature_activation_allowed {
writeln!(
f,
"{}",
style("\nFeature activation is not allowed at this time")
.bold()
.red()
)?;
}
Ok(())
}
}
impl QuietDisplay for CliFeatures {}
impl VerboseDisplay for CliFeatures {}
pub trait FeatureSubCommands {
fn feature_subcommands(self) -> Self;
}
impl FeatureSubCommands for App<'_, '_> {
fn feature_subcommands(self) -> Self {
self.subcommand(
SubCommand::with_name("feature")
.about("Runtime feature management")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("status")
.about("Query runtime feature status")
.arg(
Arg::with_name("features")
.value_name("ADDRESS")
.validator(is_valid_pubkey)
.index(1)
.multiple(true)
.help("Feature status to query [default: all known features]"),
),
)
.subcommand(
SubCommand::with_name("activate")
.about("Activate a runtime feature")
.arg(
Arg::with_name("feature")
.value_name("FEATURE_KEYPAIR")
.validator(is_valid_signer)
.index(1)
.required(true)
.help("The signer for the feature to activate"),
),
),
)
}
}
fn known_feature(feature: &Pubkey) -> Result<(), CliError> {
if FEATURE_NAMES.contains_key(feature) {
Ok(())
} else {
Err(CliError::BadParameter(format!(
"Unknown feature: {}",
feature
)))
}
}
pub fn parse_feature_subcommand(
matches: &ArgMatches<'_>,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let response = match matches.subcommand() {
("activate", Some(matches)) => {
let (feature_signer, feature) = signer_of(matches, "feature", wallet_manager)?;
let mut signers = vec![default_signer.signer_from_path(matches, wallet_manager)?];
signers.push(feature_signer.unwrap());
let feature = feature.unwrap();
known_feature(&feature)?;
CliCommandInfo {
command: CliCommand::Feature(FeatureCliCommand::Activate { feature }),
signers,
}
}
("status", Some(matches)) => {
let mut features = if let Some(features) = pubkeys_of(matches, "features") {
for feature in &features {
known_feature(feature)?;
}
features
} else {
FEATURE_NAMES.keys().cloned().collect()
};
features.sort();
CliCommandInfo {
command: CliCommand::Feature(FeatureCliCommand::Status { features }),
signers: vec![],
}
}
_ => unreachable!(),
};
Ok(response)
}
pub fn process_feature_subcommand(
rpc_client: &RpcClient,
config: &CliConfig,
feature_subcommand: &FeatureCliCommand,
) -> ProcessResult {
match feature_subcommand {
FeatureCliCommand::Status { features } => process_status(rpc_client, config, features),
FeatureCliCommand::Activate { feature } => process_activate(rpc_client, config, *feature),
}
}
fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, u64>, ClientError> {
// Validator identity -> feature set
let feature_set_map = rpc_client
.get_cluster_nodes()?
.into_iter()
.map(|contact_info| (contact_info.pubkey, contact_info.feature_set))
.collect::<HashMap<_, _>>();
let vote_accounts = rpc_client.get_vote_accounts()?;
let total_active_stake: u64 = vote_accounts
.current
.iter()
.chain(vote_accounts.delinquent.iter())
.map(|vote_account| vote_account.activated_stake)
.sum();
// Sum all active stake by feature set
let mut active_stake_by_feature_set = HashMap::new();
for vote_account in vote_accounts.current {
if let Some(Some(feature_set)) = feature_set_map.get(&vote_account.node_pubkey) {
*active_stake_by_feature_set.entry(*feature_set).or_default() +=
vote_account.activated_stake;
} else {
*active_stake_by_feature_set
.entry(0 /* "unknown" */)
.or_default() += vote_account.activated_stake;
}
}
// Convert active stake to a percentage so the caller doesn't need `total_active_stake`
for (_, val) in active_stake_by_feature_set.iter_mut() {
*val = *val * 100 / total_active_stake;
}
Ok(active_stake_by_feature_set)
}
// Feature activation is only allowed when 95% of the active stake is on the current feature set
fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<bool, ClientError> {
let my_feature_set = solana_version::Version::default().feature_set;
let active_stake_by_feature_set = active_stake_by_feature_set(rpc_client)?;
let feature_activation_allowed = active_stake_by_feature_set
.get(&my_feature_set)
.map(|percentage| *percentage >= 95)
.unwrap_or(false);
if !feature_activation_allowed && !quiet {
if active_stake_by_feature_set.get(&my_feature_set).is_none() {
println!(
"{}",
style("To activate features the tool and cluster feature sets must match, select a tool version that matches the cluster")
.bold());
} else {
println!(
"{}",
style("To activate features the stake must be >= 95%").bold()
);
}
println!(
"{}",
style(format!("Tool Feture Set: {}", my_feature_set)).bold()
);
println!("{}", style("Cluster Feature Sets and Stakes:").bold());
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
if *feature_set == 0 {
println!("unknown - {}%", percentage);
} else {
println!(
"{} - {}% {}",
feature_set,
percentage,
if *feature_set == my_feature_set {
" <-- me"
} else {
""
}
);
}
}
println!();
}
Ok(feature_activation_allowed)
}
fn process_status(
rpc_client: &RpcClient,
config: &CliConfig,
feature_ids: &[Pubkey],
) -> ProcessResult {
let mut features: Vec<CliFeature> = vec![];
let mut inactive = false;
for (i, account) in rpc_client
.get_multiple_accounts(feature_ids)?
.into_iter()
.enumerate()
{
let feature_id = &feature_ids[i];
let feature_name = FEATURE_NAMES.get(feature_id).unwrap();
if let Some(account) = account {
if let Some(feature) = feature::from_account(&account) {
let feature_status = match feature.activated_at {
None => CliFeatureStatus::Pending,
Some(activation_slot) => CliFeatureStatus::Active(activation_slot),
};
features.push(CliFeature {
id: feature_id.to_string(),
description: feature_name.to_string(),
status: feature_status,
});
continue;
}
}
inactive = true;
features.push(CliFeature {
id: feature_id.to_string(),
description: feature_name.to_string(),
status: CliFeatureStatus::Inactive,
});
}
let feature_activation_allowed = feature_activation_allowed(rpc_client, features.len() <= 1)?;
let feature_set = CliFeatures {
features,
feature_activation_allowed,
inactive,
};
Ok(config.output_format.formatted_string(&feature_set))
}
fn process_activate(
rpc_client: &RpcClient,
config: &CliConfig,
feature_id: Pubkey,
) -> ProcessResult {
let account = rpc_client
.get_multiple_accounts(&[feature_id])?
.into_iter()
.next()
.unwrap();
if let Some(account) = account {
if feature::from_account(&account).is_some() {
return Err(format!("{} has already been activated", feature_id).into());
}
}
if !feature_activation_allowed(rpc_client, false)? {
return Err("Feature activation is not allowed at this time".into());
}
let rent = rpc_client.get_minimum_balance_for_rent_exemption(Feature::size_of())?;
let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (message, _) = resolve_spend_tx_and_check_account_balance(
rpc_client,
false,
SpendAmount::Some(rent),
&fee_calculator,
&config.signers[0].pubkey(),
|lamports| {
Message::new(
&feature::activate_with_lamports(
&feature_id,
&config.signers[0].pubkey(),
lamports,
),
Some(&config.signers[0].pubkey()),
)
},
config.commitment,
)?;
let mut transaction = Transaction::new_unsigned(message);
transaction.try_sign(&config.signers, blockhash)?;
println!(
"Activating {} ({})",
FEATURE_NAMES.get(&feature_id).unwrap(),
feature_id
);
rpc_client.send_and_confirm_transaction_with_spinner(&transaction)?;
Ok("".to_string())
}

View File

@@ -1,89 +0,0 @@
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
use clap::{App, ArgMatches, SubCommand};
use console::style;
use solana_clap_utils::keypair::*;
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use std::sync::Arc;
#[derive(Debug, PartialEq)]
pub enum InflationCliCommand {
Show,
}
pub trait InflationSubCommands {
fn inflation_subcommands(self) -> Self;
}
impl InflationSubCommands for App<'_, '_> {
fn inflation_subcommands(self) -> Self {
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
}
}
pub fn parse_inflation_subcommand(
_matches: &ArgMatches<'_>,
_default_signer: &DefaultSigner,
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
Ok(CliCommandInfo {
command: CliCommand::Inflation(InflationCliCommand::Show),
signers: vec![],
})
}
pub fn process_inflation_subcommand(
rpc_client: &RpcClient,
_config: &CliConfig,
inflation_subcommand: &InflationCliCommand,
) -> ProcessResult {
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
let governor = rpc_client.get_inflation_governor()?;
let current_inflation_rate = rpc_client.get_inflation_rate()?;
println!("{}", style("Inflation Governor:").bold());
if (governor.initial - governor.terminal).abs() < f64::EPSILON {
println!(
"Fixed APR: {:>5.2}%",
governor.terminal * 100.
);
} else {
println!("Initial APR: {:>5.2}%", governor.initial * 100.);
println!(
"Terminal APR: {:>5.2}%",
governor.terminal * 100.
);
println!("Rate reduction per year: {:>5.2}%", governor.taper * 100.);
}
if governor.foundation_term > 0. {
println!("Foundation percentage: {:>5.2}%", governor.foundation);
println!(
"Foundation term: {:.1} years",
governor.foundation_term
);
}
println!(
"\n{}",
style(format!(
"Inflation for Epoch {}:",
current_inflation_rate.epoch
))
.bold()
);
println!(
"Total APR: {:>5.2}%",
current_inflation_rate.total * 100.
);
println!(
"Staking APR: {:>5.2}%",
current_inflation_rate.validator * 100.
);
println!(
"Foundation APR: {:>5.2}%",
current_inflation_rate.foundation * 100.
);
Ok("".to_string())
}

View File

@@ -18,16 +18,16 @@ macro_rules! pubkey {
}; };
} }
#[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub mod checks; pub mod checks;
pub mod cli; pub mod cli;
pub mod cli_output;
pub mod cluster_query; pub mod cluster_query;
pub mod feature; pub mod display;
pub mod inflation;
pub mod nonce; pub mod nonce;
pub mod program; pub mod offline;
pub mod send_tpu;
pub mod spend_utils; pub mod spend_utils;
pub mod stake; pub mod stake;
pub mod test_utils; pub mod test_utils;

View File

@@ -5,37 +5,22 @@ use clap::{
use console::style; use console::style;
use solana_clap_utils::{ use solana_clap_utils::{
commitment::COMMITMENT_ARG, commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url,
input_parsers::commitment_of, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
input_validators::is_url,
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
DisplayError,
}; };
use solana_cli::cli::{ use solana_cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType, cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners,
DEFAULT_RPC_TIMEOUT_SECONDS, DEFAULT_RPC_TIMEOUT_SECONDS,
},
cli_output::OutputFormat,
display::{println_name_value, println_name_value_or},
}; };
use solana_cli_config::{Config, CONFIG_FILE}; use solana_cli_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat};
use solana_client::rpc_config::RpcSendTransactionConfig; use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration}; use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration};
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
let description = match setting_type {
SettingType::Explicit => "",
SettingType::Computed => "(computed)",
SettingType::SystemDefault => "(default)",
};
println!(
"{} {} {}",
style(name).bold(),
style(value),
style(description).italic(),
);
}
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> { fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() { let parse_args = match matches.subcommand() {
("config", Some(matches)) => { ("config", Some(matches)) => {
@@ -154,21 +139,14 @@ pub fn parse_args<'a>(
matches.value_of("json_rpc_url").unwrap_or(""), matches.value_of("json_rpc_url").unwrap_or(""),
&config.json_rpc_url, &config.json_rpc_url,
); );
let default_signer_arg_name = "keypair".to_string();
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting( let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
matches.value_of(&default_signer_arg_name).unwrap_or(""), matches.value_of("keypair").unwrap_or(""),
&config.keypair_path, &config.keypair_path,
); );
let default_signer = DefaultSigner {
arg_name: default_signer_arg_name,
path: default_signer_path.clone(),
};
let CliCommandInfo { command, signers } = let CliCommandInfo { command, signers } =
parse_command(&matches, &default_signer, &mut wallet_manager)?; parse_command(&matches, &default_signer_path, &mut wallet_manager)?;
let verbose = matches.is_present("verbose");
let output_format = matches let output_format = matches
.value_of("output_format") .value_of("output_format")
.map(|value| match value { .map(|value| match value {
@@ -176,21 +154,12 @@ pub fn parse_args<'a>(
"json-compact" => OutputFormat::JsonCompact, "json-compact" => OutputFormat::JsonCompact,
_ => unreachable!(), _ => unreachable!(),
}) })
.unwrap_or(if verbose { .unwrap_or(OutputFormat::Display);
OutputFormat::DisplayVerbose
} else {
OutputFormat::Display
});
let commitment = { let commitment = matches
let mut sub_matches = matches; .subcommand_name()
while let Some(subcommand_name) = sub_matches.subcommand_name() { .and_then(|name| matches.subcommand_matches(name))
sub_matches = sub_matches .and_then(|sub_matches| commitment_of(sub_matches, COMMITMENT_ARG.long))
.subcommand_matches(subcommand_name)
.expect("subcommand_matches");
}
commitment_of(sub_matches, COMMITMENT_ARG.long)
}
.unwrap_or_default(); .unwrap_or_default();
let address_labels = if matches.is_present("no_address_labels") { let address_labels = if matches.is_present("no_address_labels") {
@@ -208,13 +177,10 @@ pub fn parse_args<'a>(
keypair_path: default_signer_path, keypair_path: default_signer_path,
rpc_client: None, rpc_client: None,
rpc_timeout, rpc_timeout,
verbose, verbose: matches.is_present("verbose"),
output_format, output_format,
commitment, commitment,
send_transaction_config: RpcSendTransactionConfig { send_transaction_config: RpcSendTransactionConfig::default(),
preflight_commitment: Some(commitment.commitment),
..RpcSendTransactionConfig::default()
},
address_labels, address_labels,
}, },
signers, signers,

View File

@@ -1,26 +1,29 @@
use crate::{ use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{ cli::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
ProcessResult, CliConfig, CliError, ProcessResult, SignerIndex,
}, },
cli_output::CliNonceAccount,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::*, input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
nonce::*,
}; };
use solana_cli_output::CliNonceAccount; use solana_client::rpc_client::RpcClient;
use solana_client::{nonce_utils::*, rpc_client::RpcClient};
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut,
commitment_config::CommitmentConfig,
hash::Hash, hash::Hash,
message::Message, message::Message,
nonce::{self, State}, nonce::{
self,
state::{Data, Versions},
State,
},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction::{ system_instruction::{
advance_nonce_account, authorize_nonce_account, create_nonce_account, advance_nonce_account, authorize_nonce_account, create_nonce_account,
@@ -30,11 +33,64 @@ use solana_sdk::{
transaction::Transaction, transaction::Transaction,
}; };
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error;
#[derive(Debug, Error, PartialEq)]
pub enum CliNonceError {
#[error("invalid account owner")]
InvalidAccountOwner,
#[error("invalid account data")]
InvalidAccountData,
#[error("unexpected account data size")]
UnexpectedDataSize,
#[error("query hash does not match stored hash")]
InvalidHash,
#[error("query authority does not match account authority")]
InvalidAuthority,
#[error("invalid state for requested operation")]
InvalidStateForOperation,
#[error("client error: {0}")]
Client(String),
}
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
name: "nonce",
long: "nonce",
help: "Provide the nonce account to use when creating a nonced \n\
transaction. Nonced transactions are useful when a transaction \n\
requires a lengthy signing process. Learn more about nonced \n\
transactions at https://docs.solana.com/offline-signing/durable-nonce",
};
pub const NONCE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant {
name: "nonce_authority",
long: "nonce-authority",
help: "Provide the nonce authority keypair to use when signing a nonced transaction",
};
pub trait NonceSubCommands { pub trait NonceSubCommands {
fn nonce_subcommands(self) -> Self; fn nonce_subcommands(self) -> Self;
} }
pub fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(NONCE_ARG.name)
.long(NONCE_ARG.long)
.takes_value(true)
.value_name("PUBKEY")
.requires(BLOCKHASH_ARG.name)
.validator(is_valid_pubkey)
.help(NONCE_ARG.help)
}
pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(NONCE_AUTHORITY_ARG.name)
.long(NONCE_AUTHORITY_ARG.long)
.takes_value(true)
.value_name("KEYPAIR")
.validator(is_valid_signer)
.help(NONCE_AUTHORITY_ARG.help)
}
impl NonceSubCommands for App<'_, '_> { impl NonceSubCommands for App<'_, '_> {
fn nonce_subcommands(self) -> Self { fn nonce_subcommands(self) -> Self {
self.subcommand( self.subcommand(
@@ -164,9 +220,64 @@ impl NonceSubCommands for App<'_, '_> {
} }
} }
pub fn get_account(
rpc_client: &RpcClient,
nonce_pubkey: &Pubkey,
) -> Result<Account, CliNonceError> {
get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default())
}
pub fn get_account_with_commitment(
rpc_client: &RpcClient,
nonce_pubkey: &Pubkey,
commitment: CommitmentConfig,
) -> Result<Account, CliNonceError> {
rpc_client
.get_account_with_commitment(nonce_pubkey, commitment)
.map_err(|e| CliNonceError::Client(format!("{}", e)))
.and_then(|result| {
result.value.ok_or_else(|| {
CliNonceError::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey))
})
})
.and_then(|a| match account_identity_ok(&a) {
Ok(()) => Ok(a),
Err(e) => Err(e),
})
}
pub fn account_identity_ok(account: &Account) -> Result<(), CliNonceError> {
if account.owner != system_program::id() {
Err(CliNonceError::InvalidAccountOwner)
} else if account.data.is_empty() {
Err(CliNonceError::UnexpectedDataSize)
} else {
Ok(())
}
}
pub fn state_from_account(account: &Account) -> Result<State, CliNonceError> {
account_identity_ok(account)?;
StateMut::<Versions>::state(account)
.map_err(|_| CliNonceError::InvalidAccountData)
.map(|v| v.convert_to_current())
}
pub fn data_from_account(account: &Account) -> Result<Data, CliNonceError> {
account_identity_ok(account)?;
state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
}
pub fn data_from_state(state: &State) -> Result<&Data, CliNonceError> {
match state {
State::Uninitialized => Err(CliNonceError::InvalidStateForOperation),
State::Initialized(data) => Ok(data),
}
}
pub fn parse_authorize_nonce_account( pub fn parse_authorize_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
@@ -175,9 +286,10 @@ pub fn parse_authorize_nonce_account(
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -193,7 +305,7 @@ pub fn parse_authorize_nonce_account(
pub fn parse_nonce_create_account( pub fn parse_nonce_create_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let (nonce_account, nonce_account_pubkey) = let (nonce_account, nonce_account_pubkey) =
@@ -203,9 +315,10 @@ pub fn parse_nonce_create_account(
let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_account], vec![payer_provided, nonce_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -235,7 +348,7 @@ pub fn parse_get_nonce(
pub fn parse_new_nonce( pub fn parse_new_nonce(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
@@ -243,9 +356,10 @@ pub fn parse_new_nonce(
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -277,7 +391,7 @@ pub fn parse_show_nonce_account(
pub fn parse_withdraw_from_nonce_account( pub fn parse_withdraw_from_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
@@ -288,9 +402,10 @@ pub fn parse_withdraw_from_nonce_account(
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -314,14 +429,14 @@ pub fn check_nonce_account(
match state_from_account(nonce_account)? { match state_from_account(nonce_account)? {
State::Initialized(ref data) => { State::Initialized(ref data) => {
if &data.blockhash != nonce_hash { if &data.blockhash != nonce_hash {
Err(Error::InvalidHash.into()) Err(CliNonceError::InvalidHash.into())
} else if nonce_authority != &data.authority { } else if nonce_authority != &data.authority {
Err(Error::InvalidAuthority.into()) Err(CliNonceError::InvalidAuthority.into())
} else { } else {
Ok(()) Ok(())
} }
} }
State::Uninitialized => Err(Error::InvalidStateForOperation.into()), State::Uninitialized => Err(CliNonceError::InvalidStateForOperation.into()),
} }
} }
@@ -576,11 +691,9 @@ mod tests {
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut,
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
hash::hash, hash::hash,
nonce::{self, state::Versions, State}, nonce::{self, State},
nonce_account,
signature::{read_keypair_file, write_keypair, Keypair, Signer}, signature::{read_keypair_file, write_keypair, Keypair, Signer},
system_program, system_program,
}; };
@@ -597,10 +710,6 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let (keypair_file, mut tmp_file) = make_tmp_file(); let (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new(); let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
@@ -619,7 +728,12 @@ mod tests {
&Pubkey::default().to_string(), &Pubkey::default().to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(), parse_command(
&test_authorize_nonce_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey, nonce_account: nonce_account_pubkey,
@@ -640,7 +754,12 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(), parse_command(
&test_authorize_nonce_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
@@ -662,7 +781,7 @@ mod tests {
"50", "50",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_nonce_account, &default_signer, &mut None).unwrap(), parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: 1, nonce_account: 1,
@@ -687,7 +806,7 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_nonce_account, &default_signer, &mut None).unwrap(), parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: 1, nonce_account: 1,
@@ -709,7 +828,7 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_get_nonce, &default_signer, &mut None).unwrap(), parse_command(&test_get_nonce, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()), command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
signers: vec![], signers: vec![],
@@ -723,7 +842,7 @@ mod tests {
.get_matches_from(vec!["test", "new-nonce", &keypair_file]); .get_matches_from(vec!["test", "new-nonce", &keypair_file]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(), parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
@@ -743,7 +862,7 @@ mod tests {
]); ]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(), parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
@@ -763,7 +882,7 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_show_nonce_account, &default_signer, &mut None).unwrap(), parse_command(&test_show_nonce_account, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ShowNonceAccount { command: CliCommand::ShowNonceAccount {
nonce_account_pubkey: nonce_account_keypair.pubkey(), nonce_account_pubkey: nonce_account_keypair.pubkey(),
@@ -784,7 +903,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_command( parse_command(
&test_withdraw_from_nonce_account, &test_withdraw_from_nonce_account,
&default_signer, &default_keypair_file,
&mut None &mut None
) )
.unwrap(), .unwrap(),
@@ -812,7 +931,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_command( parse_command(
&test_withdraw_from_nonce_account, &test_withdraw_from_nonce_account,
&default_signer, &default_keypair_file,
&mut None &mut None
) )
.unwrap(), .unwrap(),
@@ -834,7 +953,7 @@ mod tests {
#[test] #[test]
fn test_check_nonce_account() { fn test_check_nonce_account() {
let blockhash = Hash::default(); let blockhash = Hash::default();
let nonce_pubkey = solana_sdk::pubkey::new_rand(); let nonce_pubkey = Pubkey::new_rand();
let data = Versions::new_current(State::Initialized(nonce::state::Data { let data = Versions::new_current(State::Initialized(nonce::state::Data {
authority: nonce_pubkey, authority: nonce_pubkey,
blockhash, blockhash,
@@ -847,14 +966,14 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
{ {
assert_eq!(err, Error::InvalidAccountOwner,); assert_eq!(err, CliNonceError::InvalidAccountOwner,);
} }
let invalid_data = Account::new_data(1, &"invalid", &system_program::ID); let invalid_data = Account::new_data(1, &"invalid", &system_program::ID);
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
{ {
assert_eq!(err, Error::InvalidAccountData,); assert_eq!(err, CliNonceError::InvalidAccountData,);
} }
let data = Versions::new_current(State::Initialized(nonce::state::Data { let data = Versions::new_current(State::Initialized(nonce::state::Data {
@@ -866,11 +985,11 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
{ {
assert_eq!(err, Error::InvalidHash,); assert_eq!(err, CliNonceError::InvalidHash,);
} }
let data = Versions::new_current(State::Initialized(nonce::state::Data { let data = Versions::new_current(State::Initialized(nonce::state::Data {
authority: solana_sdk::pubkey::new_rand(), authority: Pubkey::new_rand(),
blockhash, blockhash,
fee_calculator: FeeCalculator::default(), fee_calculator: FeeCalculator::default(),
})); }));
@@ -878,7 +997,7 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
{ {
assert_eq!(err, Error::InvalidAuthority,); assert_eq!(err, CliNonceError::InvalidAuthority,);
} }
let data = Versions::new_current(State::Uninitialized); let data = Versions::new_current(State::Uninitialized);
@@ -886,32 +1005,32 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
{ {
assert_eq!(err, Error::InvalidStateForOperation,); assert_eq!(err, CliNonceError::InvalidStateForOperation,);
} }
} }
#[test] #[test]
fn test_account_identity_ok() { fn test_account_identity_ok() {
let nonce_account = nonce_account::create_account(1).into_inner(); let nonce_account = nonce::create_account(1).into_inner();
assert_eq!(account_identity_ok(&nonce_account), Ok(())); assert_eq!(account_identity_ok(&nonce_account), Ok(()));
let system_account = Account::new(1, 0, &system_program::id()); let system_account = Account::new(1, 0, &system_program::id());
assert_eq!( assert_eq!(
account_identity_ok(&system_account), account_identity_ok(&system_account),
Err(Error::UnexpectedDataSize), Err(CliNonceError::UnexpectedDataSize),
); );
let other_program = Pubkey::new(&[1u8; 32]); let other_program = Pubkey::new(&[1u8; 32]);
let other_account_no_data = Account::new(1, 0, &other_program); let other_account_no_data = Account::new(1, 0, &other_program);
assert_eq!( assert_eq!(
account_identity_ok(&other_account_no_data), account_identity_ok(&other_account_no_data),
Err(Error::InvalidAccountOwner), Err(CliNonceError::InvalidAccountOwner),
); );
} }
#[test] #[test]
fn test_state_from_account() { fn test_state_from_account() {
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account = nonce::create_account(1).into_inner();
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized)); assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
let data = nonce::state::Data { let data = nonce::state::Data {
@@ -930,21 +1049,21 @@ mod tests {
let wrong_data_size_account = Account::new(1, 1, &system_program::id()); let wrong_data_size_account = Account::new(1, 1, &system_program::id());
assert_eq!( assert_eq!(
state_from_account(&wrong_data_size_account), state_from_account(&wrong_data_size_account),
Err(Error::InvalidAccountData), Err(CliNonceError::InvalidAccountData),
); );
} }
#[test] #[test]
fn test_data_from_helpers() { fn test_data_from_helpers() {
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account = nonce::create_account(1).into_inner();
let state = state_from_account(&nonce_account).unwrap(); let state = state_from_account(&nonce_account).unwrap();
assert_eq!( assert_eq!(
data_from_state(&state), data_from_state(&state),
Err(Error::InvalidStateForOperation) Err(CliNonceError::InvalidStateForOperation)
); );
assert_eq!( assert_eq!(
data_from_account(&nonce_account), data_from_account(&nonce_account),
Err(Error::InvalidStateForOperation) Err(CliNonceError::InvalidStateForOperation)
); );
let data = nonce::state::Data { let data = nonce::state::Data {

View File

@@ -1,13 +1,5 @@
use crate::{nonce_utils, rpc_client::RpcClient}; use super::*;
use clap::ArgMatches; use solana_sdk::commitment_config::CommitmentConfig;
use solana_clap_utils::{
input_parsers::{pubkey_of, value_of},
nonce::*,
offline::*,
};
use solana_sdk::{
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey,
};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Source { pub enum Source {
@@ -29,8 +21,8 @@ impl Source {
Ok((res.0, res.1)) Ok((res.0, res.1))
} }
Self::NonceAccount(ref pubkey) => { Self::NonceAccount(ref pubkey) => {
let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) let data = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)
.and_then(|ref a| nonce_utils::data_from_account(a))?; .and_then(|ref a| nonce::data_from_account(a))?;
Ok((data.blockhash, data.fee_calculator)) Ok((data.blockhash, data.fee_calculator))
} }
} }
@@ -50,8 +42,8 @@ impl Source {
Ok(res) Ok(res)
} }
Self::NonceAccount(ref pubkey) => { Self::NonceAccount(ref pubkey) => {
let res = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)?; let res = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)?;
let res = nonce_utils::data_from_account(&res)?; let res = nonce::data_from_account(&res)?;
Ok(Some(res) Ok(Some(res)
.filter(|d| d.blockhash == *blockhash) .filter(|d| d.blockhash == *blockhash)
.map(|d| d.fee_calculator)) .map(|d| d.fee_calculator))
@@ -83,7 +75,7 @@ impl BlockhashQuery {
pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self { pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self {
let blockhash = value_of(matches, BLOCKHASH_ARG.name); let blockhash = value_of(matches, BLOCKHASH_ARG.name);
let sign_only = matches.is_present(SIGN_ONLY_ARG.name); let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
let nonce_account = pubkey_of(matches, NONCE_ARG.name); let nonce_account = pubkey_of(matches, nonce::NONCE_ARG.name);
BlockhashQuery::new(blockhash, sign_only, nonce_account) BlockhashQuery::new(blockhash, sign_only, nonce_account)
} }
@@ -116,15 +108,17 @@ impl Default for BlockhashQuery {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
blockhash_query,
rpc_request::RpcRequest,
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
};
use clap::App; use clap::App;
use serde_json::{self, json, Value}; use serde_json::{self, json, Value};
use solana_account_decoder::{UiAccount, UiAccountEncoding}; use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_sdk::{account::Account, hash::hash, nonce, system_program}; use solana_client::{
rpc_request::RpcRequest,
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
};
use solana_sdk::{
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program,
};
use std::collections::HashMap; use std::collections::HashMap;
#[test] #[test]
@@ -178,7 +172,7 @@ mod tests {
#[test] #[test]
fn test_blockhash_query_new_from_matches_ok() { fn test_blockhash_query_new_from_matches_ok() {
let test_commands = App::new("blockhash_query_test") let test_commands = App::new("blockhash_query_test")
.nonce_args(false) .arg(nonce_arg())
.offline_args(); .offline_args();
let blockhash = hash(&[1u8]); let blockhash = hash(&[1u8]);
let blockhash_string = blockhash.to_string(); let blockhash_string = blockhash.to_string();

129
cli/src/offline/mod.rs Normal file
View File

@@ -0,0 +1,129 @@
pub mod blockhash_query;
use crate::nonce;
use clap::{App, Arg, ArgMatches};
use serde_json::Value;
use solana_clap_utils::{
input_parsers::{pubkey_of, value_of},
input_validators::{is_hash, is_pubkey_sig},
keypair::presigner_from_pubkey_sigs,
offline::{BLOCKHASH_ARG, SIGNER_ARG, SIGN_ONLY_ARG},
};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
signature::{Presigner, Signature},
};
use std::str::FromStr;
fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(BLOCKHASH_ARG.name)
.long(BLOCKHASH_ARG.long)
.takes_value(true)
.value_name("BLOCKHASH")
.validator(is_hash)
.help(BLOCKHASH_ARG.help)
}
fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(SIGN_ONLY_ARG.name)
.long(SIGN_ONLY_ARG.long)
.takes_value(false)
.requires(BLOCKHASH_ARG.name)
.help(SIGN_ONLY_ARG.help)
}
fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(SIGNER_ARG.name)
.long(SIGNER_ARG.long)
.takes_value(true)
.value_name("PUBKEY=SIGNATURE")
.validator(is_pubkey_sig)
.requires(BLOCKHASH_ARG.name)
.multiple(true)
.help(SIGNER_ARG.help)
}
pub trait OfflineArgs {
fn offline_args(self) -> Self;
}
impl OfflineArgs for App<'_, '_> {
fn offline_args(self) -> Self {
self.arg(blockhash_arg())
.arg(sign_only_arg())
.arg(signer_arg())
}
}
pub struct SignOnly {
pub blockhash: Hash,
pub present_signers: Vec<(Pubkey, Signature)>,
pub absent_signers: Vec<Pubkey>,
pub bad_signers: Vec<Pubkey>,
}
impl SignOnly {
pub fn has_all_signers(&self) -> bool {
self.absent_signers.is_empty() && self.bad_signers.is_empty()
}
pub fn presigner_of(&self, pubkey: &Pubkey) -> Option<Presigner> {
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
}
}
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
let object: Value = serde_json::from_str(&reply).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let blockhash = blockhash_str.parse::<Hash>().unwrap();
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
let signer_strings = object.get("signers");
if let Some(sig_strings) = signer_strings {
present_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
}
let mut absent_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("absent");
if let Some(sig_strings) = signer_strings {
absent_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
let mut bad_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("badSig");
if let Some(sig_strings) = signer_strings {
bad_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
SignOnly {
blockhash,
present_signers,
absent_signers,
bad_signers,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
use log::*;
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
use std::net::{SocketAddr, UdpSocket};
pub fn get_leader_tpu(
slot_index: u64,
leader_schedule: Option<&RpcLeaderSchedule>,
cluster_nodes: Option<&Vec<RpcContactInfo>>,
) -> Option<SocketAddr> {
leader_schedule?
.iter()
.find(|(_pubkey, slots)| slots.iter().any(|slot| *slot as u64 == slot_index))
.and_then(|(pubkey, _)| {
cluster_nodes?
.iter()
.find(|contact_info| contact_info.pubkey == *pubkey)
.and_then(|contact_info| contact_info.tpu)
})
}
pub fn send_transaction_tpu(
send_socket: &UdpSocket,
tpu_address: &SocketAddr,
wire_transaction: &[u8],
) {
if let Err(err) = send_socket.send_to(wire_transaction, tpu_address) {
warn!("Failed to send transaction to {}: {:?}", tpu_address, err);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
use crate::{ use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::{CliValidatorInfo, CliValidatorInfoVec},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use bincode::deserialize; use bincode::deserialize;
@@ -12,9 +13,8 @@ use solana_account_decoder::validator_info::{
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::pubkey_of, input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url}, input_validators::{is_pubkey, is_url},
keypair::DefaultSigner, keypair::signer_from_path,
}; };
use solana_cli_output::{CliValidatorInfo, CliValidatorInfoVec};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState}; use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@@ -212,7 +212,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
pub fn parse_validator_info_command( pub fn parse_validator_info_command(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey"); let info_pubkey = pubkey_of(matches, "info_pubkey");
@@ -224,7 +224,12 @@ pub fn parse_validator_info_command(
force_keybase: matches.is_present("force"), force_keybase: matches.is_present("force"),
info_pubkey, info_pubkey,
}, },
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}) })
} }
@@ -266,12 +271,10 @@ pub fn process_set_validator_info(
let all_config = rpc_client.get_program_accounts(&solana_config_program::id())?; let all_config = rpc_client.get_program_accounts(&solana_config_program::id())?;
let existing_account = all_config let existing_account = all_config
.iter() .iter()
.filter( .filter(|(_, account)| {
|(_, account)| match deserialize::<ConfigKeys>(&account.data) { let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)), key_list.keys.contains(&(validator_info::id(), false))
Err(_) => false, })
},
)
.find(|(pubkey, account)| { .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() validator_pubkey == config.signers[0].pubkey()
@@ -382,10 +385,10 @@ pub fn process_get_validator_info(
all_config all_config
.into_iter() .into_iter()
.filter(|(_, validator_info_account)| { .filter(|(_, validator_info_account)| {
match deserialize::<ConfigKeys>(&validator_info_account.data) { let key_list: ConfigKeys = deserialize(&validator_info_account.data)
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)), .map_err(|_| false)
Err(_) => false, .unwrap();
} key_list.keys.contains(&(validator_info::id(), false))
}) })
.collect() .collect()
}; };
@@ -486,7 +489,7 @@ mod tests {
#[test] #[test]
fn test_parse_validator_info() { fn test_parse_validator_info() {
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let keys = vec![(validator_info::id(), false), (pubkey, true)]; let keys = vec![(validator_info::id(), false), (pubkey, true)];
let config = ConfigKeys { keys }; let config = ConfigKeys { keys };

View File

@@ -1,19 +1,14 @@
use crate::{ use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{ cli::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
ProcessResult, CliConfig, CliError, ProcessResult, SignerIndex,
}, },
cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand}; use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{commitment::commitment_arg, input_parsers::*, input_validators::*};
commitment::commitment_arg,
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
};
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
@@ -251,7 +246,7 @@ impl VoteSubCommands for App<'_, '_> {
pub fn parse_create_vote_account( pub fn parse_create_vote_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?; let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?;
@@ -263,9 +258,10 @@ pub fn parse_create_vote_account(
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?; let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, vote_account, identity_account], vec![payer_provided, vote_account, identity_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -284,7 +280,7 @@ pub fn parse_create_vote_account(
pub fn parse_vote_authorize( pub fn parse_vote_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
@@ -295,9 +291,10 @@ pub fn parse_vote_authorize(
let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?; let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, authorized], vec![payer_provided, authorized],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -313,7 +310,7 @@ pub fn parse_vote_authorize(
pub fn parse_vote_update_validator( pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@@ -324,9 +321,10 @@ pub fn parse_vote_update_validator(
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, authorized_withdrawer, new_identity_account], vec![payer_provided, authorized_withdrawer, new_identity_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -342,7 +340,7 @@ pub fn parse_vote_update_validator(
pub fn parse_vote_update_commission( pub fn parse_vote_update_commission(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@@ -352,9 +350,10 @@ pub fn parse_vote_update_commission(
let commission = value_t_or_exit!(matches, "commission", u8); let commission = value_t_or_exit!(matches, "commission", u8);
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, authorized_withdrawer], vec![payer_provided, authorized_withdrawer],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -386,7 +385,7 @@ pub fn parse_vote_get_account_command(
pub fn parse_withdraw_from_vote_account( pub fn parse_withdraw_from_vote_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@@ -399,9 +398,10 @@ pub fn parse_withdraw_from_vote_account(
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, withdraw_authority], vec![payer_provided, withdraw_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -671,11 +671,11 @@ fn get_vote_account(
pub fn process_show_vote_account( pub fn process_show_vote_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
vote_account_address: &Pubkey, vote_account_pubkey: &Pubkey,
use_lamports_unit: bool, use_lamports_unit: bool,
) -> ProcessResult { ) -> ProcessResult {
let (vote_account, vote_state) = let (vote_account, vote_state) =
get_vote_account(rpc_client, vote_account_address, config.commitment)?; get_vote_account(rpc_client, vote_account_pubkey, config.commitment)?;
let epoch_schedule = rpc_client.get_epoch_schedule()?; let epoch_schedule = rpc_client.get_epoch_schedule()?;
@@ -685,28 +685,17 @@ pub fn process_show_vote_account(
for vote in &vote_state.votes { for vote in &vote_state.votes {
votes.push(vote.into()); votes.push(vote.into());
} }
for (epoch, credits, prev_credits) in vote_state.epoch_credits().iter().copied() { for (epoch, credits, prev_credits) in vote_state.epoch_credits() {
let credits_earned = credits - prev_credits; let credits_earned = credits - prev_credits;
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch); let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
epoch_voting_history.push(CliEpochVotingHistory { epoch_voting_history.push(CliEpochVotingHistory {
epoch, epoch: *epoch,
slots_in_epoch, slots_in_epoch,
credits_earned, credits_earned,
credits,
prev_credits,
}); });
} }
} }
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 { let vote_account_data = CliVoteAccount {
account_balance: vote_account.lamports, account_balance: vote_account.lamports,
validator_identity: vote_state.node_pubkey.to_string(), validator_identity: vote_state.node_pubkey.to_string(),
@@ -719,7 +708,6 @@ pub fn process_show_vote_account(
votes, votes,
epoch_voting_history, epoch_voting_history,
use_lamports_unit, use_lamports_unit,
epoch_rewards,
}; };
Ok(config.output_format.formatted_string(&vote_account_data)) Ok(config.output_format.formatted_string(&vote_account_data))
@@ -806,10 +794,6 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let test_authorize_voter = test_commands.clone().get_matches_from(vec![ let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test", "test",
@@ -819,7 +803,7 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(), parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -842,7 +826,7 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(), parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -872,7 +856,7 @@ mod tests {
"10", "10",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account, &default_signer, &mut None).unwrap(), parse_command(&test_create_vote_account, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -901,7 +885,7 @@ mod tests {
&identity_keypair_file, &identity_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account2, &default_signer, &mut None).unwrap(), parse_command(&test_create_vote_account2, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -920,7 +904,7 @@ mod tests {
); );
// test init with an authed voter // test init with an authed voter
let authed = solana_sdk::pubkey::new_rand(); let authed = Pubkey::new_rand();
let (keypair_file, mut tmp_file) = make_tmp_file(); let (keypair_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new(); let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
@@ -934,7 +918,7 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account3, &default_signer, &mut None).unwrap(), parse_command(&test_create_vote_account3, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -965,7 +949,7 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_create_vote_account4, &default_signer, &mut None).unwrap(), parse_command(&test_create_vote_account4, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -991,7 +975,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_validator, &default_signer, &mut None).unwrap(), parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateValidator { command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -1014,7 +998,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_commission, &default_signer, &mut None).unwrap(), parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateCommission { command: CliCommand::VoteUpdateCommission {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -1037,7 +1021,12 @@ mod tests {
"42", "42",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(), parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@@ -1058,7 +1047,12 @@ mod tests {
"ALL", "ALL",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(), parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@@ -1084,7 +1078,12 @@ mod tests {
&withdraw_authority_file, &withdraw_authority_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(), parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),

118
cli/tests/deploy.rs Normal file
View File

@@ -0,0 +1,118 @@
use serde_json::Value;
use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
bpf_loader,
commitment_config::CommitmentConfig,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use std::{
fs::{remove_dir_all, File},
io::Read,
path::PathBuf,
str::FromStr,
sync::mpsc::channel,
};
#[test]
fn test_cli_deploy_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 TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
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 minimum_balance_for_rent_exemption = rpc_client
.get_minimum_balance_for_rent_exemption(program_data.len())
.unwrap();
let mut config = CliConfig::recent_for_tests();
let keypair = Keypair::new();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 3 * minimum_balance_for_rent_exemption, // min balance for rent exemption for two programs + leftover for tx processing
};
config.signers = vec![&keypair];
process_command(&config).unwrap();
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: None,
use_deprecated_loader: false,
};
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let account0 = rpc_client
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account0.owner, bpf_loader::id());
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();
assert_eq!(account0.data, elf);
// Test custom address
let custom_address_keypair = Keypair::new();
config.signers = vec![&keypair, &custom_address_keypair];
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
};
process_command(&config).unwrap();
let account1 = rpc_client
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account1.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account1.owner, bpf_loader::id());
assert_eq!(account1.executable, true);
assert_eq!(account0.data, account1.data);
// Attempt to redeploy to the same address
process_command(&config).unwrap_err();
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}

View File

@@ -1,15 +1,17 @@
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
parse_sign_only_reply_string,
},
spend_utils::SpendAmount, spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance}, test_utils::{check_ready, check_recent_balance},
}; };
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; use solana_client::rpc_client::RpcClient;
use solana_client::{ use solana_core::contact_info::ContactInfo;
blockhash_query::{self, BlockhashQuery}, use solana_core::validator::{TestValidator, TestValidatorOptions};
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
@@ -18,53 +20,69 @@ use solana_sdk::{
signature::{keypair_from_seed, Keypair, Signer}, signature::{keypair_from_seed, Keypair, Signer},
system_program, system_program,
}; };
use std::sync::mpsc::channel; use std::{fs::remove_dir_all, sync::mpsc::channel};
#[test] #[test]
fn test_nonce() { fn test_nonce() {
let mint_keypair = Keypair::new(); solana_logger::setup();
full_battery_tests( let TestValidator {
TestValidator::with_no_fees(mint_keypair.pubkey()), server,
mint_keypair, leader_data,
None, alice,
false, ledger_path,
); ..
} = TestValidator::run();
full_battery_tests(leader_data, alice, None, false);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
} }
#[test] #[test]
fn test_nonce_with_seed() { fn test_nonce_with_seed() {
let mint_keypair = Keypair::new(); let TestValidator {
full_battery_tests( server,
TestValidator::with_no_fees(mint_keypair.pubkey()), leader_data,
mint_keypair, alice,
Some(String::from("seed")), ledger_path,
false, ..
); } = TestValidator::run();
full_battery_tests(leader_data, alice, Some(String::from("seed")), false);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
} }
#[test] #[test]
fn test_nonce_with_authority() { fn test_nonce_with_authority() {
let mint_keypair = Keypair::new(); let TestValidator {
full_battery_tests( server,
TestValidator::with_no_fees(mint_keypair.pubkey()), leader_data,
mint_keypair, alice,
None, ledger_path,
true, ..
); } = TestValidator::run();
full_battery_tests(leader_data, alice, None, true);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
} }
fn full_battery_tests( fn full_battery_tests(
test_validator: TestValidator, leader_data: ContactInfo,
mint_keypair: Keypair, alice: Keypair,
seed: Option<String>, seed: Option<String>,
use_nonce_authority: bool, use_nonce_authority: bool,
) { ) {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url()); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let json_rpc_url = test_validator.rpc_url(); let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::recent_for_tests(); let mut config_payer = CliConfig::recent_for_tests();
config_payer.json_rpc_url = json_rpc_url.clone(); config_payer.json_rpc_url = json_rpc_url.clone();
@@ -155,7 +173,7 @@ fn full_battery_tests(
assert_ne!(first_nonce, third_nonce); assert_ne!(first_nonce, third_nonce);
// Withdraw from nonce account // Withdraw from nonce account
let payee_pubkey = solana_sdk::pubkey::new_rand(); let payee_pubkey = Pubkey::new_rand();
config_payer.signers = authorized_signers; config_payer.signers = authorized_signers;
config_payer.command = CliCommand::WithdrawFromNonceAccount { config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account, nonce_account,
@@ -214,9 +232,17 @@ fn full_battery_tests(
#[test] #[test]
fn test_create_account_with_seed() { fn test_create_account_with_seed() {
solana_logger::setup(); let TestValidator {
let mint_keypair = Keypair::new(); server,
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1); leader_data,
alice: mint_keypair,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None); run_local_faucet(mint_keypair, sender, None);
@@ -228,7 +254,7 @@ fn test_create_account_with_seed() {
let config = CliConfig::recent_for_tests(); let config = CliConfig::recent_for_tests();
// Setup accounts // Setup accounts
let rpc_client = RpcClient::new(test_validator.rpc_url()); let rpc_client = RpcClient::new_socket(leader_data.rpc);
request_and_confirm_airdrop( request_and_confirm_airdrop(
&rpc_client, &rpc_client,
&faucet_addr, &faucet_addr,
@@ -260,7 +286,8 @@ fn test_create_account_with_seed() {
check_recent_balance(0, &rpc_client, &nonce_address); check_recent_balance(0, &rpc_client, &nonce_address);
let mut creator_config = CliConfig::recent_for_tests(); let mut creator_config = CliConfig::recent_for_tests();
creator_config.json_rpc_url = test_validator.rpc_url(); creator_config.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
creator_config.signers = vec![&online_nonce_creator_signer]; creator_config.signers = vec![&online_nonce_creator_signer];
creator_config.command = CliCommand::CreateNonceAccount { creator_config.command = CliCommand::CreateNonceAccount {
nonce_account: 0, nonce_account: 0,
@@ -275,12 +302,9 @@ fn test_create_account_with_seed() {
check_recent_balance(0, &rpc_client, &to_address); check_recent_balance(0, &rpc_client, &to_address);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash =
&rpc_client, nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent())
&nonce_address, .and_then(|ref a| nonce::data_from_account(a))
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -310,7 +334,8 @@ fn test_create_account_with_seed() {
// And submit it // And submit it
let mut submit_config = CliConfig::recent_for_tests(); let mut submit_config = CliConfig::recent_for_tests();
submit_config.json_rpc_url = test_validator.rpc_url(); submit_config.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
submit_config.signers = vec![&authority_presigner]; submit_config.signers = vec![&authority_presigner];
submit_config.command = CliCommand::Transfer { submit_config.command = CliCommand::Transfer {
amount: SpendAmount::Some(10), amount: SpendAmount::Some(10),
@@ -331,4 +356,7 @@ fn test_create_account_with_seed() {
check_recent_balance(31, &rpc_client, &offline_nonce_authority_signer.pubkey()); check_recent_balance(31, &rpc_client, &offline_nonce_authority_signer.pubkey());
check_recent_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey()); check_recent_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
check_recent_balance(10, &rpc_client, &to_address); check_recent_balance(10, &rpc_client, &to_address);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
} }

453
cli/tests/pay.rs Normal file
View File

@@ -0,0 +1,453 @@
use chrono::prelude::*;
use serde_json::Value;
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
parse_sign_only_reply_string,
},
spend_utils::SpendAmount,
test_utils::check_recent_balance,
};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
commitment_config::CommitmentConfig,
nonce::State as NonceState,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use std::{fs::remove_dir_all, sync::mpsc::channel};
#[test]
fn test_cli_timestamp_tx() {
let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer0 = Keypair::new();
let default_signer1 = Keypair::new();
let mut config_payer = CliConfig::recent_for_tests();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_payer.signers = vec![&default_signer0];
let mut config_witness = CliConfig::recent_for_tests();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!(
config_payer.signers[0].pubkey(),
config_witness.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.signers[0].pubkey(),
50,
&config_witness,
)
.unwrap();
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_witness.signers[0].pubkey(),
1,
&config_witness,
)
.unwrap();
// Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness
let date_string = "\"2018-09-19T17:30:59Z\"";
let dt: DateTime<Utc> = serde_json::from_str(&date_string).unwrap();
config_payer.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
to: bob_pubkey,
timestamp: Some(dt),
timestamp_pubkey: Some(config_witness.signers[0].pubkey()),
..PayCommand::default()
});
let sig_response = process_command(&config_payer);
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
let process_id_vec = bs58::decode(process_id_str)
.into_vec()
.expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec);
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_recent_balance(10, &rpc_client, &process_id); // contract balance
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
// Sign transaction by config_witness
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
process_command(&config_witness).unwrap();
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_recent_balance(0, &rpc_client, &process_id); // contract balance
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_cli_witness_tx() {
let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer0 = Keypair::new();
let default_signer1 = Keypair::new();
let mut config_payer = CliConfig::recent_for_tests();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_payer.signers = vec![&default_signer0];
let mut config_witness = CliConfig::recent_for_tests();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!(
config_payer.signers[0].pubkey(),
config_witness.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.signers[0].pubkey(),
50,
&config_witness,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_witness.signers[0].pubkey(),
1,
&config_witness,
)
.unwrap();
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
config_payer.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
to: bob_pubkey,
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
..PayCommand::default()
});
let sig_response = process_command(&config_payer);
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
let process_id_vec = bs58::decode(process_id_str)
.into_vec()
.expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec);
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_recent_balance(10, &rpc_client, &process_id); // contract balance
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
// Sign transaction by config_witness
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
process_command(&config_witness).unwrap();
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_recent_balance(0, &rpc_client, &process_id); // contract balance
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_cli_cancel_tx() {
let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer0 = Keypair::new();
let default_signer1 = Keypair::new();
let mut config_payer = CliConfig::recent_for_tests();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_payer.signers = vec![&default_signer0];
let mut config_witness = CliConfig::recent_for_tests();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!(
config_payer.signers[0].pubkey(),
config_witness.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.signers[0].pubkey(),
50,
&config_witness,
)
.unwrap();
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
config_payer.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
to: bob_pubkey,
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
cancelable: true,
..PayCommand::default()
});
let sig_response = process_command(&config_payer).unwrap();
let object: Value = serde_json::from_str(&sig_response).unwrap();
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
let process_id_vec = bs58::decode(process_id_str)
.into_vec()
.expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec);
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_recent_balance(10, &rpc_client, &process_id); // contract balance
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
// Sign transaction by config_witness
config_payer.command = CliCommand::Cancel(process_id);
process_command(&config_payer).unwrap();
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_recent_balance(0, &rpc_client, &process_id); // contract balance
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_offline_pay_tx() {
let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer = Keypair::new();
let default_offline_signer = Keypair::new();
let mut config_offline = CliConfig::recent_for_tests();
config_offline.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_offline.signers = vec![&default_offline_signer];
let mut config_online = CliConfig::recent_for_tests();
config_online.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config_online.signers = vec![&default_signer];
assert_ne!(
config_offline.signers[0].pubkey(),
config_online.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_offline.signers[0].pubkey(),
50,
&config_offline,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_online.signers[0].pubkey(),
50,
&config_offline,
)
.unwrap();
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
config_offline.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
to: bob_pubkey,
blockhash_query: BlockhashQuery::None(blockhash),
sign_only: true,
..PayCommand::default()
});
config_offline.output_format = OutputFormat::JsonCompact;
let sig_response = process_command(&config_offline).unwrap();
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
check_recent_balance(0, &rpc_client, &bob_pubkey);
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only
.presigner_of(&config_offline.signers[0].pubkey())
.unwrap();
let online_pubkey = config_online.signers[0].pubkey();
config_online.signers = vec![&offline_presigner];
config_online.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
to: bob_pubkey,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
..PayCommand::default()
});
process_command(&config_online).unwrap();
check_recent_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
check_recent_balance(50, &rpc_client, &online_pubkey);
check_recent_balance(10, &rpc_client, &bob_pubkey);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_nonced_pay_tx() {
solana_logger::setup();
let TestValidator {
server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer = Keypair::new();
let mut config = CliConfig::recent_for_tests();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.signers = vec![&default_signer];
let minimum_nonce_balance = rpc_client
.get_minimum_balance_for_rent_exemption(NonceState::size())
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config.signers[0].pubkey(),
50 + minimum_nonce_balance,
&config,
)
.unwrap();
check_recent_balance(
50 + minimum_nonce_balance,
&rpc_client,
&config.signers[0].pubkey(),
);
// Create nonce account
let nonce_account = Keypair::new();
config.command = CliCommand::CreateNonceAccount {
nonce_account: 1,
seed: None,
nonce_authority: Some(config.signers[0].pubkey()),
amount: SpendAmount::Some(minimum_nonce_balance),
};
config.signers.push(&nonce_account);
process_command(&config).unwrap();
check_recent_balance(50, &rpc_client, &config.signers[0].pubkey());
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.unwrap()
.blockhash;
let bob_pubkey = Pubkey::new_rand();
config.signers = vec![&default_signer];
config.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
to: bob_pubkey,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
nonce_hash,
),
nonce_account: Some(nonce_account.pubkey()),
..PayCommand::default()
});
process_command(&config).expect("failed to process pay command");
check_recent_balance(40, &rpc_client, &config.signers[0].pubkey());
check_recent_balance(10, &rpc_client, &bob_pubkey);
// Verify that nonce has been used
let nonce_hash2 = nonce::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.unwrap()
.blockhash;
assert_ne!(nonce_hash, nonce_hash2);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}

View File

@@ -1,525 +0,0 @@
use serde_json::Value;
use solana_cli::{
cli::{process_command, CliCommand, CliConfig},
program::ProgramCliCommand,
};
use solana_client::rpc_client::RpcClient;
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
bpf_loader,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
commitment_config::CommitmentConfig,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use std::{fs::File, io::Read, path::PathBuf, str::FromStr, sync::mpsc::channel};
#[test]
fn test_cli_program_deploy_non_upgradeable() {
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 test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url());
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 minimum_balance_for_rent_exemption = rpc_client
.get_minimum_balance_for_rent_exemption(program_data.len())
.unwrap();
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 {
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
};
process_command(&config).unwrap();
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
};
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let account0 = rpc_client
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account0.owner, bpf_loader::id());
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();
assert_eq!(account0.data, elf);
// Test custom address
let custom_address_keypair = Keypair::new();
config.signers = vec![&keypair, &custom_address_keypair];
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
};
process_command(&config).unwrap();
let account1 = rpc_client
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account1.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account1.owner, bpf_loader::id());
assert_eq!(account1.executable, true);
assert_eq!(account1.data, account0.data);
// Attempt to redeploy to the same address
process_command(&config).unwrap_err();
// Attempt to deploy to account with excess balance
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
};
process_command(&config).unwrap();
config.signers = vec![&keypair, &custom_address_keypair];
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
};
process_command(&config).unwrap_err();
// Use forcing parameter to deploy to account with excess balance
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: true,
};
process_command(&config).unwrap();
let account2 = rpc_client
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
assert_eq!(account2.owner, bpf_loader::id());
assert_eq!(account2.executable, true);
assert_eq!(account2.data, account0.data);
}
#[test]
fn test_cli_program_deploy_no_authority() {
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 test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url());
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.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
};
config.signers = vec![&keypair];
process_command(&config).unwrap();
// Deploy a program with no authority
config.signers = vec![&keypair];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: None,
program_pubkey: None,
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: None,
upgrade_authority_pubkey: None,
max_len: None,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
// Attempt to upgrade the program
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: None,
program_pubkey: Some(program_id),
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: Some(1),
upgrade_authority_pubkey: Some(upgrade_authority.pubkey()),
max_len: None,
});
process_command(&config).unwrap_err();
}
#[test]
fn test_cli_program_deploy_with_authority() {
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 test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url());
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 {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
};
process_command(&config).unwrap();
// Deploy the upgradeable program with specified program_id
let program_keypair = Keypair::new();
config.signers = vec![&keypair, &upgrade_authority, &program_keypair];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: Some(2),
program_pubkey: Some(program_keypair.pubkey()),
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: Some(1),
upgrade_authority_pubkey: Some(upgrade_authority.pubkey()),
max_len: Some(max_len),
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
assert_eq!(
program_keypair.pubkey(),
Pubkey::from_str(&program_id_str).unwrap()
);
let program_account = rpc_client
.get_account_with_commitment(&program_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) = Pubkey::find_program_address(
&[program_keypair.pubkey().as_ref()],
&bpf_loader_upgradeable::id(),
);
let programdata_account = rpc_client
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(
programdata_account.lamports,
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
);
// Deploy the upgradeable program
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: None,
program_pubkey: None,
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: Some(1),
upgrade_authority_pubkey: Some(upgrade_authority.pubkey()),
max_len: Some(max_len),
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let program_account = rpc_client
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(
programdata_account.lamports,
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
);
// Upgrade the program
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: None,
program_pubkey: Some(program_id),
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: Some(1),
upgrade_authority_pubkey: Some(upgrade_authority.pubkey()),
max_len: Some(max_len),
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let program_account = rpc_client
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(
programdata_account.lamports,
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
);
// Set a new authority
let new_upgrade_authority = Keypair::new();
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
program: program_id,
upgrade_authority_index: Some(1),
new_upgrade_authority: Some(new_upgrade_authority.pubkey()),
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let new_upgrade_authority_str = json
.as_object()
.unwrap()
.get("UpgradeAuthority")
.unwrap()
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(&new_upgrade_authority_str).unwrap(),
new_upgrade_authority.pubkey()
);
// Upgrade with new authority
config.signers = vec![&keypair, &new_upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: None,
program_pubkey: Some(program_id),
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: Some(1),
upgrade_authority_pubkey: Some(new_upgrade_authority.pubkey()),
max_len: None,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_id_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let program_account = rpc_client
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(
programdata_account.lamports,
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
);
// Set no authority
config.signers = vec![&keypair, &new_upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
program: program_id,
upgrade_authority_index: Some(1),
new_upgrade_authority: None,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let new_upgrade_authority_str = json
.as_object()
.unwrap()
.get("UpgradeAuthority")
.unwrap()
.as_str()
.unwrap();
assert_eq!(new_upgrade_authority_str, "None");
// Upgrade with no authority
config.signers = vec![&keypair, &new_upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
program_signer_index: None,
program_pubkey: Some(program_id),
buffer_signer_index: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: Some(1),
upgrade_authority_pubkey: Some(new_upgrade_authority.pubkey()),
max_len: None,
});
process_command(&config).unwrap_err();
}

View File

@@ -1,24 +1,25 @@
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::test_validator::TestValidator; use solana_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
commitment_config::CommitmentConfig, use std::{fs::remove_dir_all, sync::mpsc::channel};
signature::{Keypair, Signer},
};
use std::sync::mpsc::channel;
#[test] #[test]
fn test_cli_request_airdrop() { fn test_cli_request_airdrop() {
let mint_keypair = Keypair::new(); let TestValidator {
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey()); server,
leader_data,
alice,
ledger_path,
..
} = TestValidator::run();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None); run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap(); let faucet_addr = receiver.recv().unwrap();
let mut bob_config = CliConfig::recent_for_tests(); let mut bob_config = CliConfig::recent_for_tests();
bob_config.json_rpc_url = test_validator.rpc_url(); bob_config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
bob_config.command = CliCommand::Airdrop { bob_config.command = CliCommand::Airdrop {
faucet_host: None, faucet_host: None,
faucet_port: faucet_addr.port(), faucet_port: faucet_addr.port(),
@@ -31,11 +32,14 @@ fn test_cli_request_airdrop() {
let sig_response = process_command(&bob_config); let sig_response = process_command(&bob_config);
sig_response.unwrap(); sig_response.unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url()); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let balance = rpc_client let balance = rpc_client
.get_balance_with_commitment(&bob_config.signers[0].pubkey(), CommitmentConfig::recent()) .get_balance_with_commitment(&bob_config.signers[0].pubkey(), CommitmentConfig::recent())
.unwrap() .unwrap()
.value; .value;
assert_eq!(balance, 50); assert_eq!(balance, 50);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
} }

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