Compare commits

...

180 Commits

Author SHA1 Message Date
d0de1c913a Use fs::rename which is much faster than move_items (#8579) (#8594)
automerge
2020-03-03 10:54:36 -08:00
4398628265 Add Ledger wallet installation instructions (#8581) (#8591)
automerge
2020-03-03 08:28:52 -08:00
86f76dbb2d Do periodic inbound cleaning for rooted slots (#8436) (#8582)
automerge
2020-03-03 01:57:26 -08:00
d59d231209 Hack to skip cleanup_dead_slots upon snapshot load (bp #8561) (#8585)
automerge
2020-03-03 00:49:23 -08:00
b286296064 Remove needless last_root for better reclaims (#8148) (#8584)
automerge
2020-03-02 23:21:44 -08:00
1d2c3fc81a Don't advertise the snapshot that the node was loaded from
snapshot_packager_service will remove this snapshot hash from gossip
when it starts
2020-03-02 19:16:25 -07:00
3e92cf9563 Docs: Update CLI offline cmds (#8548) (#8557)
automerge
2020-03-02 16:18:35 -08:00
a679751766 Remove granularity from genesis (bp #8514) (#8517)
automerge
2020-03-02 15:26:27 -08:00
922d677534 Allow stake lockup fields to be updated independently (#8568) (#8573)
automerge
2020-03-02 13:59:16 -08:00
db419193ba Only gossip packaged snapshots
(cherry picked from commit 42c5c59800)
2020-03-02 14:20:59 -07:00
df1874a7e1 --wait-for-supermajority now requires a SLOT
(cherry picked from commit 13551885c2)
2020-03-02 13:41:20 -07:00
b74e3ce725 Add ---no-untrusted-rpc flag
(cherry picked from commit d677e83ed4)
2020-03-02 11:53:11 -07:00
dcb35d287a Bump version to 0.23.9 2020-02-29 12:27:25 -07:00
db949b4059 Reduce max snapshot hashes to stay under MTU
(cherry picked from commit 2a5605db24)
2020-02-29 09:25:58 -07:00
2bc735bc85 Log RPC node root slot
(cherry picked from commit b4362cc18b)
2020-02-29 09:25:58 -07:00
dca0a864b6 Fix skipping own leader slots (#8533) (#8539)
automerge
2020-02-29 01:12:24 -08:00
f2f6828691 rpc: GET for /snapshot.tar.bz2 now redirects to the latest snapshot (bp #8536) (#8537)
automerge
2020-02-28 23:16:23 -08:00
797e4b11e2 Upgrade to Rust 1.41.1 (#8529)
automerge
2020-02-28 10:07:39 -08:00
80206decf8 Ensure the validator's identity pubkey is not provided as a --trusted-validator (#8525) (#8526)
automerge
2020-02-27 21:51:18 -08:00
eb4d6912bd Bump version to 0.23.8 2020-02-27 22:00:44 -07:00
febc7c7ac0 Use legit solana message in verify (#8513) (#8522)
automerge
2020-02-27 19:16:30 -08:00
f8add53a71 Ledger messaging cleanup (#8506) (#8507)
automerge
2020-02-27 12:11:41 -08:00
18b55bbfe1 Remove loop (#8495) 2020-02-27 02:42:42 -08:00
85637e8f63 Rename snapshot.tar.bz2 to snapshot-<slot>-<hash>.tar.bz2 (bp #8482) (#8500)
automerge
2020-02-27 00:04:14 -08:00
eb3b5d7382 Update voting simulation (#8460) (#8488)
automerge
2020-02-26 21:00:38 -08:00
1a4de4d3c4 v0.23: backport cli refactoring and remote-wallet signing integration (#8487)
* CLI: dynamic signing reboot (#8384)

* Add keypair_util_from_path helper

* Cli: impl config.keypair as a trait object

* SDK: Add Debug and PartialEq for dyn Signer

* ClapUtils: Arg parsing from pubkey+signers to Presigner

* Impl Signers for &dyn Signer collections

* CLI: Add helper for getting signers from args

* CLI: Replace SigningAuthority with Signer trait-objs

* CLI: Drop disused signers command field

* CLI: Drop redundant tests

* Add clap validator that handles all current signer types

* clap_utils: Factor Presigner resolution to helper

* SDK: `From` for boxing Signer implementors to trait objects

* SDK: Derive `Clone` for `Presigner`

* Remove panic

* Cli: dedup signers in transfer for remote-wallet ergonomics

* Update docs vis-a-vis ASK changes

* Cli: update transaction types to use new dynamic-signer methods

* CLI: Fix tests No. 1

what to do about write_keypair outstanding

* Work around `CliConfig`'s signer not necessarily being a `Keypair`

* CLI: Fix tests No. 2

* Remove unused arg

* Remove unused methods

* Move offline arg constants upstream

* Make cli signing fallible

Co-authored-by: Trent Nelson <trent.a.b.nelson@gmail.com>

* Reinstate `create-stale-account` w/ seed test (#8401)

automerge

* CLI: collect and deduplicate signers (#8398)

* Rename (keypair util is not a thing)

* Add method to generate_unique_signers

* Cli: refactor signer handling and remote-wallet init

* Fixup unit tests

* Fixup intergation tests

* Update keypair path print statement

* Remove &None

* Use deterministic key in test

* Retain storage-account as index

* Make signer index-handling less brittle

* Cache pubkey on RemoteKeypair::new

* Make signer_of consistent + return pubkey

* Remove &matches double references

* Nonce authorities need special handling

* Make solana root key accessible on Ledger (#8421)

* Use 44/501 key as ledger id

* Add error codes

* Ledger key path rework (#8453)

automerge

* Ledger hardware wallet docs (#8472)

* Update protocol documentation

* Correct app-version command const

* Rough initial Ledger docs

* Add more docs

* Cleanup

* Add remote-wallet to docs TOC

Co-authored-by: Greg Fitzgerald <greg@solana.com>

* Add flag to confirm key on device

Co-authored-by: Trent Nelson <trent.a.b.nelson@gmail.com>
Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-02-26 17:59:41 -07:00
100a11f061 Limit leader schedule search space (#8468) (#8485)
automerge
2020-02-26 15:49:31 -08:00
6d431b8210 Refactor select_fork() to avoid clones and for clarity (#8081) (#8483)
automerge
2020-02-26 15:45:15 -08:00
242afa7e6b Validate the genesis config downloaded over RPC before accepting it (bp #8474) (#8480)
automerge
2020-02-26 14:54:01 -08:00
536e01121f Tower tests (bp #7974) (#8463)
automerge
2020-02-26 13:25:08 -08:00
93bcb65d8b Promote dangerous cond. from just warning to panic (#8439) (#8448)
automerge
2020-02-25 19:12:07 -08:00
0743334486 Allow withdrawer to change the authorized stake key (#8456) (#8461)
automerge
2020-02-25 19:11:20 -08:00
93027fa067 Add --no-check-vote-account argument (#8430) (#8434)
automerge
2020-02-25 00:13:18 -08:00
8548c83e48 validator: snapshot fetching cleanup (bp #8413) (#8415)
automerge
2020-02-24 14:37:24 -08:00
80dd5db628 Add support for large transactions with Ledger Wallet (#8394) (#8395)
automerge
2020-02-22 09:39:48 -08:00
2d0ee7e3dc Add --trusted-validator support for snapshot hash validation (#8390) (#8393)
automerge
2020-02-21 19:46:43 -08:00
df2b4d31b7 Bump version to 0.23.7 2020-02-21 20:01:54 -07:00
45612bc988 \ 2020-02-21 18:10:11 -07:00
51ce98badd Add --enable-warmup-epochs flag
(cherry picked from commit ea5b00364f)
2020-02-21 17:03:35 -07:00
075e784bef Promote some datapoints to info to fix dashboard (#8381)
automerge

(cherry picked from commit aa80f69171)
2020-02-21 17:00:42 -07:00
c1b587c6e4 4x DEFAULT_MAX_LEDGER_SLOTS to give nodes 3 hours of slots to repair from (#8388)
automerge

(cherry picked from commit fb98df76b7)
2020-02-21 17:00:35 -07:00
94e2d0b5c2 Update unlocks (bp #8363) (#8382)
automerge
2020-02-21 11:40:27 -08:00
c2be9fdf0e Add handling for fallible signers (#8367) (#8374)
automerge
2020-02-21 01:45:13 -08:00
c2b17c7d3f Correct missing entry handling to avoid bad warns (bp #8339) (#8378)
automerge
2020-02-20 23:35:21 -08:00
31544f2a82 If the node was loaded from a snapshot, advertise it in gossip (#8364) (#8373)
automerge
2020-02-20 19:57:43 -08:00
3a88190e4e Enable BPF in TdS epoch 17 (#8370)
automerge
2020-02-20 18:11:58 -08:00
3f30354d1a Add non-bz2 snapshot for faster creation for dev. (#8350)
* Add non-bz2 snapshot for faster creation for dev.

* Fix tests..

* Revert and always just use snapshot.tar.bz2

(cherry picked from commit 5ef06a9d36)
2020-02-20 18:51:16 -07:00
11f15c0708 Flip Stable and Preview enum values
(cherry picked from commit 2d665da3e1)
2020-02-20 18:29:04 -07:00
a83bf85bb3 Search for the validator with the highest snapshot (#8368)
automerge
2020-02-20 17:24:23 -08:00
02877814fa Rename KeypairUtil to Signer (#8360) (#8366)
automerge
2020-02-20 16:30:43 -08:00
29cdfd6bc9 Book: Add instructions for verifying a paper wallet keypair (#8357) (#8365)
automerge
2020-02-20 14:33:18 -08:00
9dffc3abe4 Support transaction signing by heterogenous lists of keypairs (bp #8342) (#8362)
automerge
2020-02-20 14:02:14 -08:00
b4eb81546e Snapshot hash gossip changes (#8358) (#8359)
automerge
2020-02-20 12:52:04 -08:00
489fd3058f Do not compress small incomplete slot list (#8355) (#8356)
automerge
2020-02-20 10:48:47 -08:00
e5872ef1c1 Bitwise compress incomplete epoch slots (#8341) 2020-02-20 05:49:17 -08:00
cb9d18316a Update epoch slots to include all missing slots (#8276)
* Update epoch slots to include all missing slots

* new test for compress/decompress

* address review comments

* limit cache based on size, instead of comparing roots
2020-02-20 05:49:17 -08:00
c3ac85828b Process Gossip in parallel and add an upper limit (#8328) (#8345)
automerge
2020-02-19 21:54:52 -08:00
5fbddd5894 Add Preview operating mode, rename SoftLaunch operating mode to Stable (#8331) (#8340)
automerge
2020-02-19 18:13:41 -08:00
90af35737d Use correct static IP address 2020-02-19 18:15:54 -07:00
58cb21402b Remove validators from genesis (#8330) (#8333)
automerge
2020-02-19 15:42:25 -08:00
824b894977 More testnet->devnet 2020-02-19 16:15:58 -07:00
2295a5e512 Update README.md 2020-02-19 15:53:17 -07:00
83a322a211 rename testnet.solana.com to devnet.solana.com
(cherry picked from commit e3cebcf82d)

# Conflicts:
#	README.md
2020-02-19 15:53:17 -07:00
a008748d9d grooming: use cleanup, remove some dead code (bp #8324) (#8326)
automerge
2020-02-18 21:06:56 -08:00
72cb0b7c9e Add --fee-burn-percentage (#8323)
automerge
2020-02-18 19:03:41 -08:00
ede3781f91 Just define BnakSlotDelta type alias (bp #8186) (#8320)
automerge
2020-02-18 16:31:38 -08:00
e3ac6fac1e Factor out creating genesis with vote accounts into a utility function (bp #8315) (#8317)
automerge
2020-02-18 13:12:53 -08:00
e30561f8a0 CLI: Add optional airdrop recipient (#8291) (#8310)
automerge
2020-02-16 11:32:06 -08:00
8d59bef561 Cli: Remove units from various subcommands (#8301) (#8306)
automerge
2020-02-15 12:45:02 -08:00
897e1fc5d6 Bump version to 0.23.6 2020-02-14 22:38:14 -07:00
cb84099b2e Add storage rewards pools in development mode only (#8300)
automerge
2020-02-14 21:15:11 -08:00
c89b35545c install: support vX.Y.Z in addition to X.Y.Z (#8297)
automerge

(cherry picked from commit 335675c51c)
2020-02-14 21:30:18 -07:00
370716edd3 ledger-tool: Add print-accounts command
(cherry picked from commit 1bf2285fa2)
2020-02-14 21:29:24 -07:00
1dbcd5c298 Presigner KeypairUtil implementer (#8269) (#8271)
automerge
2020-02-14 12:50:00 -08:00
ca770d5e74 Datapoints overwhelm the metrics queue and blow up ram usage. (#8272) (#8283)
automerge
2020-02-14 12:42:45 -08:00
51a8d0356f Make generate_remote_keypair more generic for potential other remote-wallets (#8274) (#8275)
automerge
2020-02-14 09:37:39 -08:00
79e340c499 Enable remote-wallet signing in solana-keygen (#8267) (#8268)
automerge
2020-02-13 13:57:11 -08:00
00f92f520f get_confirmed_block: expect() less (#8266)
automerge
2020-02-13 10:43:55 -08:00
b90049aafb Retain signature subscriptions that haven't been notified (#8261) (#8265)
automerge
2020-02-13 09:58:16 -08:00
c1d66b46fa Retry to curl to codecov.io unfortunately (#8263) (#8264)
automerge
2020-02-13 09:15:55 -08:00
c377d1cbbd CLI: Offline-ify remaining stake ops (#8257) (#8259)
automerge
2020-02-12 23:00:16 -08:00
bbdb4129cf Add CliCommand::StakeSetLockup (#8248) (#8254)
automerge
2020-02-12 17:23:53 -08:00
0ecf823986 Cargo.lock 2020-02-12 17:53:27 -07:00
43ac961637 Simplify remote wallet (#8249) (#8251)
automerge
2020-02-12 16:19:14 -08:00
57e6213528 Plumb --enable-rpc-get-confirmed-block flag 2020-02-12 17:09:03 -07:00
acafb89ff2 Wrap ed25519_dalek::Keypair (#8247) (#8250)
automerge
2020-02-12 16:01:30 -08:00
ec319a6043 Bump version to 0.23.5 2020-02-12 13:58:37 -07:00
f389d434f8 Bump version to 0.23.4 2020-02-12 13:56:59 -07:00
1b600a7f37 Update cluster_info.rs 2020-02-12 12:50:45 -07:00
798b457b27 Avoid assigning the serve repair port to the storage port
(cherry picked from commit d0a4686990)

# Conflicts:
#	core/src/cluster_info.rs
2020-02-12 12:50:45 -07:00
132d012842 Quash 'repair listener error: Err(RecvTimeoutError(Timeout))' log spam (#8238)
automerge
2020-02-12 10:29:26 -08:00
e16f9ad961 Fix accounts_db store counts in purging accounts logic (#8218) (#8236)
automerge
2020-02-12 09:45:06 -08:00
66f006108c CLI: Don't hide errors when fees are disabled (#8204)
automerge

(cherry picked from commit ed87229cec)
2020-02-11 23:33:49 -07:00
47f887bda0 The getConfirmedBlock RPC API is now disabled by default (#8230)
automerge
2020-02-11 22:18:22 -08:00
bb64c73aa2 set_read_timeout() can fail, don't expect() it not to
(cherry picked from commit 36c0cb052b)
2020-02-11 21:08:07 -07:00
1f30d1e77a solana-install init edge when "edge" is not currently installed now works
(cherry picked from commit ed58bcda4c)
2020-02-11 21:08:07 -07:00
04dab9b274 Fix RPC pub sub unsubscribe (#8208) (#8228)
automerge
2020-02-11 18:40:21 -08:00
fb4e102670 Report validator rewards in getConfirmedBlock JSON RPC (#8226)
automerge
2020-02-11 18:20:16 -08:00
67e0ba0356 Add method to sign raw data, enabling easier device app testing (#8221) (#8225)
automerge
2020-02-11 17:59:08 -08:00
22bb4e6462 Factor repair from gossip (#8044) (#8220)
automerge
2020-02-11 14:18:45 -08:00
79035bdbed Upgrade to rust 1.41.0 (bp #8202) (#8219)
automerge
2020-02-11 13:56:58 -08:00
70089a5258 Fixup sign_transaction; pass derivation_path by reference (#8194) (#8217)
automerge
2020-02-11 12:38:31 -08:00
34238d5f1e Reliably track proc macro & build.rs code coverage (#8210) (#8213)
automerge
2020-02-11 09:13:21 -08:00
cab6917cbd Fix nightly clippy warnings (#8199) (#8212)
automerge
2020-02-11 08:43:12 -08:00
2951ee5b1d Channel installs no longer re-download the same release. (#8211)
automerge
2020-02-11 08:24:17 -08:00
fb16a15900 CLI: Add fee-payer parame to stake-split subcommand (#8201) (#8205)
automerge
2020-02-11 01:32:07 -08:00
76b52f4c5d CLI: transfer fix checks pubkeys (#8198) (#8203)
automerge
2020-02-11 00:26:56 -08:00
21a2e643c2 CLI: Harden offline signing and tests (#8052) (#8197)
automerge
2020-02-10 19:23:22 -08:00
733d9cb026 Remove repairman as its spamming cluster with unwanted repairs (#8193) (#8195)
automerge
2020-02-10 17:56:45 -08:00
2f54f57b7a Fix larger than necessary allocations in streamer (#8187) (#8192)
automerge
2020-02-10 13:06:28 -08:00
7bd95019ef Minor logging improvements (bp #8140) (#8190)
automerge
2020-02-10 11:22:26 -08:00
33557c3271 Check for AVX512 at runtime to avoid invalid opcode trap (#8166)
automerge

(cherry picked from commit ef5fb6fa46)
2020-02-07 17:07:10 -07:00
c65b9cd88d Filter old CrdsValues received via Pull Responses in Gossip (#8150) (#8171)
automerge
2020-02-07 14:11:48 -08:00
038db8167f CLI: Implement transfer command (#8108) (#8170)
automerge
2020-02-07 13:18:35 -08:00
030498ced5 Ledger hardware wallet integration (#8068) (#8169)
automerge
2020-02-07 12:14:41 -08:00
28eb8b662a Remove unwanted println 2020-02-07 12:59:44 -07:00
de752eaf80 Lock snapshot version to 0.23.2 (#8167)
automerge
2020-02-07 11:35:54 -08:00
9c5ef19d80 Surface shred version more in tools (#8163) (#8165)
automerge
2020-02-07 10:10:00 -08:00
235bd0a46b CLI: Support offline fee payers (#8009) (#8164)
automerge
2020-02-07 09:41:35 -08:00
465d71a3a3 De-replicode Tower constructors (#8153) (#8154)
automerge
2020-02-06 19:38:47 -08:00
14e6029fae Add libudev-dev to docker image to build remote-wallet (#8149) (#8152)
automerge
2020-02-06 16:51:44 -08:00
75434158ee Ignore flaky test_exchange_local_cluster (#8146) (#8147)
automerge
2020-02-06 12:24:42 -08:00
1cae9fd893 Better surface bank hash verification failures (#8134)
automerge
2020-02-05 12:04:34 -08:00
bea34a812c CLI cosmetic: make config get and verbose prints consistent (#8119) (#8133)
automerge
2020-02-05 11:31:29 -08:00
41a28d7322 Bump version to 0.23.3 2020-02-03 21:10:30 -07:00
235158d2bc CLI: Expose sign-only reply parsing helper (#8107) (#8110)
automerge
2020-02-03 19:55:45 -08:00
521238f7d7 Delete uptime command, report total credits in solana validators instead
(cherry picked from commit 4c0420b884)
2020-02-03 17:15:09 -07:00
384f52a607 Fix consensus threshold when new root is created (#8093)
When a new root is created, the oldest slot is popped off
but when the logic checks for identical slots, it assumes
that any difference means a slot was popped off the front.
2020-02-03 16:54:48 -07:00
49f2d912ab Add split-stake command (#8092)
automerge
2020-02-03 11:04:21 -08:00
8652fe30ce Update book release version 2020-02-03 11:36:19 -07:00
899a14ba51 Disable windows update as windows build artifacts are turned off 2020-02-01 22:25:47 -07:00
466c7dafb3 Bump version to v0.23.2 2020-02-01 21:46:34 -07:00
293bb63ed8 Reduce rpc client pre-flight requests by setting max-age header (#8082) (#8083)
automerge
2020-02-01 08:48:40 -08:00
8f8fb720af CLI: Fix stake-account auth withdrawer output (#8071)
automerge

(cherry picked from commit 9739be9ecf)
2020-02-01 08:58:13 -07:00
19f414d843 Use solana-cli config keypair in solana-keygen (bp #8074) (#8080)
* Use solana-cli config keypair in solana-keygen (#8074)

* Use solana-cli config keypair in solana-keygen

* s/infile/keypair for consistency across modules and more generality across access methods

* Move config into separate crate

(cherry picked from commit fab8ef379f)

# Conflicts:
#	Cargo.lock
#	cli/Cargo.toml
#	keygen/Cargo.toml

* Fixup version numbers for backport

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-01-31 23:08:08 -07:00
eaca1c3170 Add new colo test cases using reduced node count (#8078) (#8079)
automerge
2020-01-31 19:06:36 -08:00
9fc75925f9 CLI: De-replicode SigningAuthority instatiation (#8076) (#8077)
automerge
2020-01-31 17:42:15 -08:00
b5098ac87c Filter repairman peers based on shred_version (#8069) (#8073)
automerge
2020-01-31 15:29:30 -08:00
e23aec9728 Update key (#8062) (#8066)
automerge
2020-01-31 12:55:49 -08:00
57d490c84f Minor cli fixes (bp #8061) (#8065)
automerge
2020-01-31 12:36:35 -08:00
aa8c9f6a98 Remove asteroids and pacman from QA/dev testnet availability (#8050) (#8063)
automerge
2020-01-31 11:28:33 -08:00
57772dc73d s/mint/faucet 2020-01-31 12:15:20 -07:00
21706108e8 Don't exit early if add. validators not found during gce.sh config
(cherry picked from commit 9adf0d4ee0)
2020-01-31 08:36:03 -07:00
50d0caf00f Remove support for 0.22.3 snapshots (#8058)
automerge
2020-01-31 00:15:44 -08:00
2739332306 Fix stale gossip entrypoint (#8053) (#8057)
automerge
2020-01-30 23:13:26 -08:00
c85c4699aa validator: add --private-rpc flag (bp #8037) (#8054)
automerge
2020-01-30 20:44:53 -08:00
81add4d6bf Make tds slots-per-epoch configurable 2020-01-30 21:38:39 -07:00
8e31eeb696 Dial testnet down to a single node 2020-01-30 21:17:38 -07:00
e1ce8b37ff Minor --expected-shred fix, clean up shred-related gossip log messages (#8041) (#8045)
automerge
2020-01-30 14:41:21 -08:00
3f831c05f5 Add different shred test to test_tvu_peers_and_stakes
(cherry picked from commit 0c55b37976)
2020-01-30 11:28:45 -07:00
f0d7ce6bb6 CLI: Disallow blockhash/fee-calc lookups when offline (#7981)
* CLI: Add BlockhashSpec to tighten control over --blockhash

* Use BlockhashSpec

* Add a matches-free constructor

* More descriptive naming

(cherry picked from commit 966d077431)
2020-01-30 09:39:04 -07:00
6ba95b2545 Ignore slow archiver tests (#8032)
automerge

(cherry picked from commit 400412d76c)
2020-01-30 09:38:49 -07:00
6818e68542 Add shred version filters to Crds Accessors (#8027)
* Add shred version filters to Crds Accessors

* Adopt entrypoint shred_version if one isn't provided

(cherry picked from commit 64c42e28dc)
2020-01-30 08:58:36 -07:00
43659d7deb Remove support for stake redelegation (#7995) (#8024)
automerge
2020-01-29 23:46:42 -08:00
f24d8e7d2d Add set_lockup to stake (#7997)
(cherry picked from commit 0d6c233747)
2020-01-29 23:22:04 -07:00
e10fe5e125 Update and fix transaction error documentation (#7998)
(cherry picked from commit fed3817ed3)
2020-01-29 23:20:32 -07:00
0f8c9ab1c4 Various fixes/improvements resulting from SLP 1.1 restart debug (bp #8019) (#8026)
automerge
2020-01-29 20:11:23 -08:00
8a9a9cb991 Log solana-validator args on startup to aid debugging
(cherry picked from commit effe6e3ff3)
2020-01-29 09:40:33 -07:00
44208ffa67 refactored 2020-01-28 20:29:56 -07:00
5df0478fa3 refactored the thread loop
a thread will break if the atomic bool is true
2020-01-28 20:29:56 -07:00
d52567933e refactored grind_parse_args and grind_print_info 2020-01-28 20:29:56 -07:00
a32cdb9f4d updated to slice 2020-01-28 20:29:56 -07:00
eacd8d986c put some logic into functions 2020-01-28 20:29:56 -07:00
1d32603b49 taking care of errors from ./test-check.sh 2020-01-28 20:29:56 -07:00
8c6f7ee5a4 ran cargo fmt 2020-01-28 20:29:56 -07:00
be482eed3f removed whitespace 2020-01-28 20:29:56 -07:00
6e1c53cb0f simplified messaging and if blocks 2020-01-28 20:29:56 -07:00
af92f205cf simplified messaging 2020-01-28 20:29:56 -07:00
87047b08c8 removed found and changed count to AtomicU64 2020-01-28 20:29:56 -07:00
e282161872 updated bs58 decode check 2020-01-28 20:29:56 -07:00
01b1e287ed fixed prefix typo 2020-01-28 20:29:56 -07:00
d7fd1fa467 added informative print statements 2020-01-28 20:29:56 -07:00
bfa34cd494 it works
need to add print out to inform user
2020-01-28 20:29:56 -07:00
915835e224 this command works but wont exit right when the 6th key is found
cargo run grind --starts-with hj:2 --ends-with jk:2 --starts-and-ends-with nⓂ️2
2020-01-28 20:29:56 -07:00
659332e7ac progress on storing parameters 2020-01-28 20:29:56 -07:00
272986c6ac validator methods work 2020-01-28 20:29:56 -07:00
4d8ab45c56 removed includes
added ends-with and starts-and-ends-with
updated help messages
added expected number of values
updated .value_name for each option
2020-01-28 20:29:56 -07:00
932ae86d47 CLI: Fix tests. sign_only requires a blockhash (#8005) (#8007)
automerge
2020-01-28 19:07:47 -08:00
756e6334b0 Add lock to make sure slot-based locktree calls are safe (#7993) (#7999)
automerge
2020-01-28 14:57:37 -08:00
4e6eca9748 Update cargo files to 0.23.1 (#7994)
automerge
2020-01-27 20:44:44 -08:00
305 changed files with 15313 additions and 7965 deletions

829
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ members = [
"chacha",
"chacha-cuda",
"chacha-sys",
"cli-config",
"client",
"core",
"faucet",
@ -42,6 +43,7 @@ members = [
"archiver",
"archiver-lib",
"archiver-utils",
"remote-wallet",
"runtime",
"sdk",
"sdk-c",

View File

@ -126,10 +126,7 @@ Remote Testnets
---
We maintain several testnets:
* `testnet` - public stable testnet accessible via testnet.solana.com. Runs 24/7
* `testnet-beta` - public beta channel testnet accessible via beta.testnet.solana.com. Runs 24/7
* `testnet-edge` - public edge channel testnet accessible via edge.testnet.solana.com. Runs 24/7
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
## Deploy process

View File

@ -140,9 +140,9 @@ TODO: Documentation update procedure is WIP as we move to gitbook
Document the new recommended version by updating `book/src/running-archiver.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
### Update software on testnet.solana.com
### Update software on devnet.solana.com
The testnet running on testnet.solana.com is set to use a fixed release tag
The testnet running on devnet.solana.com is set to use a fixed release tag
which is set in the Buildkite testnet-management pipeline.
This tag needs to be updated and the testnet restarted after a new release
tag is created.
@ -182,4 +182,4 @@ TESTNET_OP=create-and-start
### Alert the community
Notify Discord users on #validator-support that a new release for
testnet.solana.com is available
devnet.solana.com is available

View File

@ -1,6 +1,6 @@
[package]
name = "solana-archiver-lib"
version = "0.23.0"
version = "0.23.9"
description = "Solana Archiver Library"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -15,22 +15,22 @@ ed25519-dalek = "=1.0.0-pre.1"
log = "0.4.8"
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-client = { path = "../client", version = "0.23.0" }
solana-storage-program = { path = "../programs/storage", version = "0.23.0" }
solana-client = { path = "../client", version = "0.23.9" }
solana-storage-program = { path = "../programs/storage", version = "0.23.9" }
thiserror = "1.0"
serde = "1.0.104"
serde_json = "1.0.44"
serde_derive = "1.0.103"
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-chacha = { path = "../chacha", version = "0.23.0" }
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.0" }
solana-ledger = { path = "../ledger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-perf = { path = "../perf", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.0" }
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.0" }
solana-metrics = { path = "../metrics", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-chacha = { path = "../chacha", version = "0.23.9" }
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.9" }
solana-ledger = { path = "../ledger", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-perf = { path = "../perf", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
solana-core = { path = "../core", version = "0.23.9" }
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.9" }
solana-metrics = { path = "../metrics", version = "0.23.9" }
[dev-dependencies]
hex = "0.4.0"

View File

@ -1,6 +1,5 @@
use crate::result::ArchiverError;
use crossbeam_channel::unbounded;
use ed25519_dalek;
use rand::{thread_rng, Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use solana_archiver_utils::sample_file;
@ -16,6 +15,7 @@ use solana_core::{
packet::{limited_deserialize, PACKET_DATA_SIZE},
repair_service,
repair_service::{RepairService, RepairSlotRange, RepairStrategy},
serve_repair::ServeRepair,
shred_fetch_stage::ShredFetchStage,
sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
storage_stage::NUM_STORAGE_SAMPLES,
@ -36,7 +36,7 @@ use solana_sdk::{
commitment_config::CommitmentConfig,
hash::Hash,
message::Message,
signature::{Keypair, KeypairUtil, Signature},
signature::{Keypair, Signature, Signer},
timing::timestamp,
transaction::Transaction,
transport::TransportError,
@ -87,11 +87,11 @@ struct ArchiverMeta {
}
fn get_slot_from_signature(
signature: &ed25519_dalek::Signature,
signature: &Signature,
storage_turn: u64,
slots_per_segment: u64,
) -> u64 {
let signature_vec = signature.to_bytes();
let signature_vec = signature.as_ref();
let mut segment_index = u64::from(signature_vec[0])
| (u64::from(signature_vec[1]) << 8)
| (u64::from(signature_vec[1]) << 16)
@ -195,13 +195,7 @@ impl Archiver {
Blockstore::open(ledger_path).expect("Expected to be able to open database ledger"),
);
let gossip_service = GossipService::new(
&cluster_info,
Some(blockstore.clone()),
None,
node.sockets.gossip,
&exit,
);
let gossip_service = GossipService::new(&cluster_info, None, node.sockets.gossip, &exit);
info!("Connecting to the cluster via {:?}", cluster_entrypoint);
let (nodes, _) =
@ -390,7 +384,7 @@ impl Archiver {
);
let message =
Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey()));
if let Err(e) = client.send_message(&[&archiver_keypair], message) {
if let Err(e) = client.send_message(&[archiver_keypair.as_ref()], message) {
error!("unable to redeem reward, tx failed: {:?}", e);
} else {
info!(
@ -443,13 +437,13 @@ impl Archiver {
return Err(e);
}
};
let signature = storage_keypair.sign(segment_blockhash.as_ref());
let signature = storage_keypair.sign_message(segment_blockhash.as_ref());
let slot = get_slot_from_signature(&signature, segment_slot, slots_per_segment);
info!("replicating slot: {}", slot);
slot_sender.send(slot)?;
meta.slot = slot;
meta.slots_per_segment = slots_per_segment;
meta.signature = Signature::new(&signature.to_bytes());
meta.signature = signature;
meta.blockhash = segment_blockhash;
let mut repair_slot_range = RepairSlotRange::default();
@ -522,6 +516,8 @@ impl Archiver {
let mut contact_info = node_info.clone();
contact_info.tvu = "0.0.0.0:0".parse().unwrap();
contact_info.wallclock = timestamp();
// copy over the adopted shred_version from the entrypoint
contact_info.shred_version = cluster_info.read().unwrap().my_data().shred_version;
{
let mut cluster_info_w = cluster_info.write().unwrap();
cluster_info_w.insert_self(contact_info);
@ -675,7 +671,7 @@ impl Archiver {
blockhash,
);
if let Err(err) = client.send_and_confirm_transaction(
&[&archiver_keypair, &storage_keypair],
&[archiver_keypair.as_ref(), storage_keypair.as_ref()],
&mut transaction,
10,
0,
@ -701,7 +697,7 @@ impl Archiver {
) -> Result<u64> {
let rpc_peers = {
let cluster_info = cluster_info.read().unwrap();
cluster_info.rpc_peers()
cluster_info.all_rpc_peers()
};
debug!("rpc peers: {:?}", rpc_peers);
if !rpc_peers.is_empty() {
@ -757,7 +753,7 @@ impl Archiver {
loop {
let rpc_peers = {
let cluster_info = cluster_info.read().unwrap();
cluster_info.rpc_peers()
cluster_info.all_rpc_peers()
};
debug!("rpc peers: {:?}", rpc_peers);
if !rpc_peers.is_empty() {
@ -812,7 +808,7 @@ impl Archiver {
/// It is recommended to use a temporary blockstore for this since the download will not verify
/// shreds received and might impact the chaining of shreds across slots
pub fn download_from_archiver(
cluster_info: &Arc<RwLock<ClusterInfo>>,
serve_repair: &ServeRepair,
archiver_info: &ContactInfo,
blockstore: &Arc<Blockstore>,
slots_per_segment: u64,
@ -832,10 +828,10 @@ impl Archiver {
Recycler::default(),
"archiver_reeciver",
);
let id = cluster_info.read().unwrap().id();
let id = serve_repair.keypair().pubkey();
info!(
"Sending repair requests from: {} to: {}",
cluster_info.read().unwrap().my_data().id,
serve_repair.my_info().id,
archiver_info.gossip
);
let repair_slot_range = RepairSlotRange {
@ -855,9 +851,7 @@ impl Archiver {
let reqs: Vec<_> = repairs
.into_iter()
.filter_map(|repair_request| {
cluster_info
.read()
.unwrap()
serve_repair
.map_repair_request(&repair_request)
.map(|result| ((archiver_info.gossip, result), repair_request))
.ok()

View File

@ -1,6 +1,6 @@
[package]
name = "solana-archiver-utils"
version = "0.23.0"
version = "0.23.9"
description = "Solana Archiver Utils"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -12,12 +12,12 @@ edition = "2018"
log = "0.4.8"
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-chacha = { path = "../chacha", version = "0.23.0" }
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.0" }
solana-ledger = { path = "../ledger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-perf = { path = "../perf", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-chacha = { path = "../chacha", version = "0.23.9" }
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.9" }
solana-ledger = { path = "../ledger", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-perf = { path = "../perf", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
[dev-dependencies]
hex = "0.4.0"

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-archiver"
version = "0.23.0"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -10,11 +10,11 @@ homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
console = "0.9.1"
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-metrics = { path = "../metrics", version = "0.23.0" }
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.9" }
solana-core = { path = "../core", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-metrics = { path = "../metrics", version = "0.23.9" }
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.9" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }

View File

@ -12,7 +12,7 @@ use solana_core::{
cluster_info::{Node, VALIDATOR_PORT_RANGE},
contact_info::ContactInfo,
};
use solana_sdk::{commitment_config::CommitmentConfig, signature::KeypairUtil};
use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer};
use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc};
fn main() {

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-banking-bench"
version = "0.23.0"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -10,11 +10,11 @@ homepage = "https://solana.com/"
[dependencies]
log = "0.4.6"
rayon = "1.2.0"
solana-core = { path = "../core", version = "0.23.0" }
solana-ledger = { path = "../ledger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-runtime = { path = "../runtime", version = "0.23.0" }
solana-measure = { path = "../measure", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.9" }
solana-ledger = { path = "../ledger", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-runtime = { path = "../runtime", version = "0.23.9" }
solana-measure = { path = "../measure", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
rand = "0.6.5"
crossbeam-channel = "0.3"

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-exchange"
version = "0.23.0"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -23,19 +23,19 @@ serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.44"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.0" }
solana-genesis = { path = "../genesis", version = "0.23.0" }
solana-client = { path = "../client", version = "0.23.0" }
solana-faucet = { path = "../faucet", version = "0.23.0" }
solana-exchange-program = { path = "../programs/exchange", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-metrics = { path = "../metrics", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-runtime = { path = "../runtime", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.9" }
solana-core = { path = "../core", version = "0.23.9" }
solana-genesis = { path = "../genesis", version = "0.23.9" }
solana-client = { path = "../client", version = "0.23.9" }
solana-faucet = { path = "../faucet", version = "0.23.9" }
solana-exchange-program = { path = "../programs/exchange", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-metrics = { path = "../metrics", version = "0.23.9" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-runtime = { path = "../runtime", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
untrusted = "0.7.0"
ws = "0.9.1"
[dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "0.23.0" }
solana-local-cluster = { path = "../local-cluster", version = "0.23.9" }

View File

@ -15,7 +15,7 @@ use solana_sdk::{
client::{Client, SyncClient},
commitment_config::CommitmentConfig,
pubkey::Pubkey,
signature::{Keypair, KeypairUtil},
signature::{Keypair, Signer},
timing::{duration_as_ms, duration_as_s},
transaction::Transaction,
{system_instruction, system_program},
@ -701,7 +701,7 @@ fn verify_funding_transfer<T: SyncClient + ?Sized>(
false
}
pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
let total = lamports * (dests.len() as u64 + 1);
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
@ -824,7 +824,11 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
}
}
pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], accounts: &[Keypair]) {
pub fn create_token_accounts<T: Client>(
client: &T,
signers: &[Arc<Keypair>],
accounts: &[Keypair],
) {
let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
while !notfunded.is_empty() {
@ -968,7 +972,12 @@ fn generate_keypairs(num: u64) -> Vec<Keypair> {
rnd.gen_n_keypairs(num)
}
pub fn airdrop_lamports(client: &dyn Client, faucet_addr: &SocketAddr, id: &Keypair, amount: u64) {
pub fn airdrop_lamports<T: Client>(
client: &T,
faucet_addr: &SocketAddr,
id: &Keypair,
amount: u64,
) {
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
let balance = balance.unwrap_or(0);
if balance >= amount {

View File

@ -1,7 +1,7 @@
use clap::{crate_description, crate_name, value_t, App, Arg, ArgMatches};
use solana_core::gen_keys::GenKeys;
use solana_faucet::faucet::FAUCET_PORT;
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, Keypair};
use std::net::SocketAddr;
use std::process::exit;
use std::time::Duration;

View File

@ -5,7 +5,7 @@ pub mod order_book;
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
use log::*;
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana_sdk::signature::KeypairUtil;
use solana_sdk::signature::Signer;
fn main() {
solana_logger::setup();

View File

@ -10,12 +10,13 @@ use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_sdk::genesis_config::create_genesis_config;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use std::process::exit;
use std::sync::mpsc::channel;
use std::time::Duration;
#[test]
#[ignore]
fn test_exchange_local_cluster() {
solana_logger::setup();

View File

@ -2,14 +2,14 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-streamer"
version = "0.23.0"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.9" }
solana-core = { path = "../core", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-tps"
version = "0.23.0"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -16,24 +16,24 @@ serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.44"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.0" }
solana-genesis = { path = "../genesis", version = "0.23.0" }
solana-client = { path = "../client", version = "0.23.0" }
solana-faucet = { path = "../faucet", version = "0.23.0" }
solana-librapay = { path = "../programs/librapay", version = "0.23.0", optional = true }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-metrics = { path = "../metrics", version = "0.23.0" }
solana-measure = { path = "../measure", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-runtime = { path = "../runtime", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.0", optional = true }
solana-clap-utils = { path = "../clap-utils", version = "0.23.9" }
solana-core = { path = "../core", version = "0.23.9" }
solana-genesis = { path = "../genesis", version = "0.23.9" }
solana-client = { path = "../client", version = "0.23.9" }
solana-faucet = { path = "../faucet", version = "0.23.9" }
solana-librapay = { path = "../programs/librapay", version = "0.23.9", optional = true }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-metrics = { path = "../metrics", version = "0.23.9" }
solana-measure = { path = "../measure", version = "0.23.9" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-runtime = { path = "../runtime", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.9", optional = true }
[dev-dependencies]
serial_test = "0.3.2"
serial_test_derive = "0.3.1"
solana-local-cluster = { path = "../local-cluster", version = "0.23.0" }
solana-local-cluster = { path = "../local-cluster", version = "0.23.9" }
[features]
move = ["solana-librapay", "solana-move-loader-program"]

View File

@ -7,7 +7,7 @@ use solana_faucet::faucet::request_airdrop_transaction;
#[cfg(feature = "move")]
use solana_librapay::{create_genesis, upload_mint_script, upload_payment_script};
use solana_measure::measure::Measure;
use solana_metrics::{self, datapoint_debug};
use solana_metrics::{self, datapoint_info};
use solana_sdk::{
client::Client,
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
@ -15,7 +15,7 @@ use solana_sdk::{
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
signature::{Keypair, KeypairUtil},
signature::{Keypair, Signer},
system_instruction, system_transaction,
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
transaction::Transaction,
@ -244,7 +244,7 @@ where
fn metrics_submit_lamport_balance(lamport_balance: u64) {
info!("Token balance: {}", lamport_balance);
datapoint_debug!(
datapoint_info!(
"bench-tps-lamport_balance",
("balance", lamport_balance, i64)
);
@ -375,7 +375,7 @@ fn generate_txs(
duration_as_ms(&duration),
blockhash,
);
datapoint_debug!(
datapoint_info!(
"bench-tps-generate_txs",
("duration", duration_as_us(&duration), i64)
);
@ -481,7 +481,7 @@ fn do_tx_transfers<T: Client>(
duration_as_ms(&transfer_start.elapsed()),
tx_len as f32 / duration_as_s(&transfer_start.elapsed()),
);
datapoint_debug!(
datapoint_info!(
"bench-tps-do_tx_transfers",
("duration", duration_as_us(&transfer_start.elapsed()), i64),
("count", tx_len, i64)

View File

@ -1,7 +1,7 @@
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
use solana_faucet::faucet::FAUCET_PORT;
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, Keypair};
use std::{net::SocketAddr, process::exit, time::Duration};
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;

View File

@ -4,7 +4,7 @@ use solana_bench_tps::cli;
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
use solana_genesis::Base64Account;
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_program;
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};

View File

@ -8,7 +8,7 @@ use solana_faucet::faucet::run_local_faucet;
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
#[cfg(feature = "move")]
use solana_sdk::move_loader::solana_move_loader_program;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use std::sync::{mpsc::channel, Arc};
use std::time::Duration;

11
book/offline-cmd-md-links.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
CLI_USAGE_RELPATH="../cli/usage.md"
SED_OMIT_NONMATCHING=$'\nt\nd'
SED_CMD="s:^#### solana-(.*):* [\`\\1\`](${CLI_USAGE_RELPATH}#solana-\\1):${SED_OMIT_NONMATCHING}"
OFFLINE_CMDS=$(grep -E '#### solana-|--signer ' src/cli/usage.md | grep -B1 -- --signer | sed -Ee "$SED_CMD")
# Omit deprecated
grep -vE '\b(pay)\b' <<<"$OFFLINE_CMDS"

View File

@ -3,6 +3,8 @@
* [Introduction](introduction.md)
* [Using Solana from the Command-line](cli/README.md)
* [Command-line Usage](cli/usage.md)
* [Remote Wallet](remote-wallet/README.md)
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
* [Paper Wallet](paper-wallet/README.md)
* [Installation](paper-wallet/installation.md)
* [Paper Wallet Usage](paper-wallet/usage.md)

View File

@ -303,6 +303,9 @@ The result field will be an object with the following fields:
* `fee: <u64>` - fee this transaction was charged, as u64 integer
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
* `rewards: <array>` - an array of JSON objects containing:
* `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
* `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
#### Example:
@ -827,7 +830,7 @@ The result field will be a JSON object with the following fields:
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"solana-core": "0.23.0"},"id":1}
{"jsonrpc":"2.0","result":{"solana-core": "0.23.9"},"id":1}
```
### getVoteAccounts

View File

@ -154,7 +154,7 @@ The stream will output a series of JSON objects:
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
```bash
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint testnet.solana.com:8001 --faucet testnet.solana.com:9900 --duration 60 --tx_count 50
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint devnet.solana.com:8001 --faucet devnet.solana.com:9900 --duration 60 --tx_count 50
```
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)

View File

@ -22,12 +22,6 @@ $ solana airdrop 2
// Return
"2.00000000 SOL"
// Command
$ solana airdrop 123 --lamports
// Return
"123 lamports"
```
### Get Balance

File diff suppressed because it is too large Load Diff

View File

@ -94,12 +94,13 @@ The Stakes and the RewardsPool are accounts that are owned by the same `Stake` p
### StakeInstruction::DelegateStake
The Stake account is moved from Ininitialized to StakeState::Stake form. This is how stakers choose their initial delegate validator node and activate their stake account lamports. The transaction must be signed by the stake's `authorized_staker`. If the stake account is already StakeState::Stake \(i.e. already activated\), the stake is re-delegated. Stakes may be re-delegated at any time, and updated stakes are reflected immediately, but only one re-delegation is permitted per epoch.
The Stake account is moved from Initialized to StakeState::Stake form, or from a deactivated (i.e. fully cooled-down) StakeState::Stake to activated StakeState::Stake. This is how stakers choose the vote account and validator node to which their stake account lamports are delegated. The transaction must be signed by the stake's `authorized_staker`.
* `account[0]` - RW - The StakeState::Stake instance. `StakeState::Stake::credits_observed` is initialized to `VoteState::credits`, `StakeState::Stake::voter_pubkey` is initialized to `account[1]`. If this is the initial delegation of stake, `StakeState::Stake::stake` is initialized to the account's balance in lamports, `StakeState::Stake::activated` is initialized to the current Bank epoch, and `StakeState::Stake::deactivated` is initialized to std::u64::MAX
* `account[1]` - R - The VoteState instance.
* `account[2]` - R - sysvar::clock account, carries information about current Bank epoch
* `account[3]` - R - stake::Config accoount, carries warmup, cooldown, and slashing configuration
* `account[3]` - R - sysvar::stakehistory account, carries information about stake history
* `account[4]` - R - stake::Config accoount, carries warmup, cooldown, and slashing configuration
### StakeInstruction::Authorize\(Pubkey, StakeAuthorize\)

View File

@ -154,7 +154,7 @@ FLAGS:
OPTIONS:
-d, --data_dir <PATH> Directory to store install data [default: .../Library/Application Support/solana]
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://testnet.solana.com:8899]
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://devnet.solana.com:8899]
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
```

View File

@ -14,15 +14,21 @@ transaction.
## Commands Supporting Offline Signing
At present, the following commands support offline signing:
* [`delegate-stake`](../api-reference/cli.md#solana-delegate-stake)
* [`deactivate-stake`](../api-reference/cli.md#solana-deactivate-stake)
* [`pay`](../api-reference/cli.md#solana-pay)
* [`create-stake-account`](../cli/usage.md#solana-create-stake-account)
* [`deactivate-stake`](../cli/usage.md#solana-deactivate-stake)
* [`delegate-stake`](../cli/usage.md#solana-delegate-stake)
* [`split-stake`](../cli/usage.md#solana-split-stake)
* [`stake-authorize-staker`](../cli/usage.md#solana-stake-authorize-staker)
* [`stake-authorize-withdrawer`](../cli/usage.md#solana-stake-authorize-withdrawer)
* [`stake-set-lockup`](../cli/usage.md#solana-stake-set-lockup)
* [`transfer`](../cli/usage.md#solana-transfer)
* [`withdraw-stake`](../cli/usage.md#solana-withdraw-stake)
## Signing Transactions Offline
To sign a transaction offline, pass the following arguments on the command line
1) `--sign-only`, prevents the client from submitting the signed transaction
to the network. Instead, the pubkey/signature pairs are printed to stdout.
to the network. Instead, the pubkey/signature pairs are printed to stdout.
2) `--blockhash BASE58_HASH`, allows the caller to specify the value used to
fill the transaction's `recent_blockhash` field. This serves a number of
purposes, namely:
@ -37,7 +43,7 @@ Command
```bash
solana@offline$ solana pay --sign-only --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
recipient-keypair.json 1 SOL
recipient-keypair.json 1
```
Output
@ -67,7 +73,7 @@ Command
```bash
solana@online$ solana pay --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
--signer FhtzLVsmcV7S5XqGD79ErgoseCLhZYmEZnz9kQg1Rp7j=4vC38p4bz7XyiXrk6HtaooUqwxTWKocf45cstASGtmrD398biNJnmTcUCVEojE7wVQvgdYbjHJqRFZPpzfCQpmUN
recipient-keypair.json 1 SOL
recipient-keypair.json 1
```
Output

View File

@ -36,7 +36,7 @@ A nonce account is created by first generating a new keypair, then create the ac
```bash
solana-keygen new -o nonce-keypair.json
solana create-nonce-account nonce-keypair.json 1 SOL
solana create-nonce-account nonce-keypair.json 1
```
- Output
@ -64,7 +64,7 @@ presently stored nonce value with
- Command
```bash
solana nonce nonce-keypair.json
solana nonce nonce-keypair.json
```
- Output
@ -105,7 +105,7 @@ Inspect a nonce account in a more human friendly format with
- Command
```bash
solana nonce-account nonce-keypair.json
solana nonce-account nonce-keypair.json
```
- Output
@ -127,7 +127,7 @@ Withdraw funds from a nonce account with
- Command
```bash
solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5 SOL
solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5
```
- Output
@ -151,7 +151,7 @@ Reassign the authority of a nonce account after creation with
- Command
```bash
solana authorize-nonce-account nonce-keypair.json nonce-authority.json
solana authorize-nonce-account nonce-keypair.json nonce-authority.json
```
- Output
@ -197,7 +197,7 @@ Alice will need some funds to create a nonce account and send to Bob. Airdrop
her some SOL
```bash
$ solana airdrop -k alice.json 10 SOL
$ solana airdrop -k alice.json 10
10 SOL
```
@ -211,7 +211,7 @@ has full authority over the nonce account
{% endhint %}
```bash
$ solana create-nonce-account -k alice.json nonce.json 1 SOL
$ solana create-nonce-account -k alice.json nonce.json 1
3KPZr96BTsL3hqera9up82KAU462Gz31xjqJ6eHUAjF935Yf8i1kmfEbo6SVbNaACKE5z6gySrNjVRvmS8DcPuwV
```
@ -221,7 +221,7 @@ Alice attempts to pay Bob, but takes too long to sign. The specified blockhash
expires and the transaction fails
```bash
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1 SOL
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1
[2020-01-02T18:48:28.462911000Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
Error: Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
```
@ -236,13 +236,13 @@ Remember, `alice.json` is the [nonce authority](#nonce-authority) in this exampl
{% endhint %}
```bash
$ solana nonce-account nonce.json
$ solana nonce-account nonce.json
balance: 1 SOL
minimum balance required: 0.00136416 SOL
nonce: F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7
```
```bash
$ solana pay -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 1 SOL
$ solana pay -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 1
HR1368UKHVZyenmH7yVz5sBAijV6XAPeWbEiXEGVYQorRMcoijeNAbzZqEZiH8cDB8tk65ckqeegFjK8dHwNFgQ
```
@ -256,7 +256,7 @@ $ solana balance -k bob.json
1 SOL
```
```bash
$ solana nonce-account nonce.json
$ solana nonce-account nonce.json
balance: 1 SOL
minimum balance required: 0.00136416 SOL
nonce: 6bjroqDcZgTv6Vavhqf81oBHTv3aMnX19UTB51YhAZnN

View File

@ -2,7 +2,7 @@
Follow this guide to setup Solana's key generation tool called `solana-keygen`
{% hint style="warn" %}
After installation, ensure your version is `0.21.1` or higher by running `solana-keygen -V`
After installation, ensure your version is `0.23.1` or higher by running `solana-keygen -V`
{% endhint %}
## Download

View File

@ -90,6 +90,91 @@ For full usage details run:
```bash
solana-keygen pubkey --help
```
## Verifying the Keypair
A keypair can be verified by following a variation on the
[offline signing](../offline-signing/README.md) procedure with a dummy transaction.
### Create and Sign a Dummy Transaction
Use offline signing to acquire the signature of a dummy transaction that can
be verified in the next step. A 0 Lamport [transfer](../cli/usage.md#solana-transfer)
is used to prevent inadvertent loss of funds. Additionally, an improbable _blockhash_
value is specified, as well as using the address of the _system program_ for the `TO`
argument, to ensure the transaction would be rejected by the _cluster_ should
it be submitted in error.
Command
```text
solana transfer 11111111111111111111111111111111 0 --sign-only \
--ask-seed-phrase keypair --blockhash 11111111111111111111111111111111
```
Prompt for seed phrase
```text
[keypair] seed phrase:
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
Recovered pubkey `AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi`. Continue? (y/n): y
```
Output
```text
Blockhash: 11111111111111111111111111111111
Signers (Pubkey=Signature):
AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA
{"blockhash":"11111111111111111111111111111111","signers":["AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA"]}
```
### Verify the Signature
Using the _Signers_ output from the [previous step](#create-and-sign-a-dummy-transaction)
to reconstruct the transaction, this time specifying the _pubkey_ and _signature_
as in the submission step of [offline signing](../offline-signing/README.md). That is, the `--from` and
`--fee-payer` are explicitly set to the _pubkey_ rather than being taken from
the keypair (which is not queried this time).
Command
```text
solana transfer 11111111111111111111111111111111 0 --sign-only --from AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi \
--signer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA \
--blockhash 11111111111111111111111111111111 --fee-payer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi
```
Output
```text
Blockhash: 11111111111111111111111111111111
Signers (Pubkey=Signature):
AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA
{"blockhash":"11111111111111111111111111111111","signers":["AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA"]}
```
### An Example of Failure
To simulate an error the [verification step](#verify-the-signature) is repeated,
but with a corrupted _signature_ (the last letter is changed from "A" to "B").
Command
```text
solana transfer 11111111111111111111111111111111 0 --sign-only --from AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi \
--signer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWB \
--blockhash 11111111111111111111111111111111 --fee-payer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi
```
Output (Error)
```text
Error: BadParameter("Transaction construction failed, incorrect signature or public key provided")
```
## Checking Account Balance
All that is needed to check an account balance is the public key of an account.
@ -102,7 +187,7 @@ networked machine.
Next, configure the `solana` CLI tool to connect to a particular cluster:
```bash
solana config set --url <CLUSTER URL> # (i.e. http://testnet.solana.com:8899)
solana config set --url <CLUSTER URL> # (i.e. http://devnet.solana.com:8899)
```
Finally, to check the balance, run the following command:
@ -162,10 +247,10 @@ Refer to the following page for a comprehensive guide on running a validator:
Solana CLI tooling supports secure keypair input for stake delegation. To do so,
first create a stake account with some SOL. Use the special `ASK` keyword to
trigger a seed phrase input prompt for the stake account and use
`--ask-seed-phrase keypair` to securely input the funding keypair.
`--keypair ASK` to securely input the funding keypair.
```bash
solana create-stake-account ASK 1 SOL --ask-seed-phrase keypair
solana create-stake-account ASK 1 --keypair ASK
[stake_account] seed phrase: 🔒
[stake_account] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
@ -173,11 +258,11 @@ solana create-stake-account ASK 1 SOL --ask-seed-phrase keypair
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
```
Then, to delegate that stake to a validator, use `--ask-seed-phrase keypair` to
Then, to delegate that stake to a validator, use `--keypair ASK` to
securely input the funding keypair.
```bash
solana delegate-stake --ask-seed-phrase keypair <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
solana delegate-stake --keypair ASK <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
[keypair] seed phrase: 🔒
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:

View File

@ -22,7 +22,7 @@ Each CTF test starts with an opaque entry point and a funded keypair. The test s
```text
use crate::contact_info::ContactInfo;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
pub fn test_this_behavior(
entry_point_info: &ContactInfo,
funding_keypair: &Keypair,

View File

@ -0,0 +1,12 @@
# Remote Wallet
This document describes how to use a remote wallet with the Solana CLI
tools. Currently, Solana supports:
- Ledger hardware wallet (Nano S)
## Overview
Solana's remote-wallet integration provides Solana CLI methods to query a
device's BIP32 public keys and send messages to the device for secure signing.
{% page-ref page="ledger.md" %}

View File

@ -0,0 +1,152 @@
# Ledger Hardware Wallet
The Ledger Nano S hardware wallet offers secure storage of your Solana private
keys. The Solana Ledger app enables derivation of essentially infinite keys, and
secure transaction signing.
## Before You Begin
- [Initialize your Ledger Nano S](https://support.ledger.com/hc/en-us/articles/360000613793)
- [Install the latest device firmware](https://support.ledgerwallet.com/hc/en-us/articles/360002731113-Update-Ledger-Nano-S-firmware)
- [Install Ledger Live](https://support.ledger.com/hc/en-us/articles/360006395553/) software on your computer
## Install the Solana App on Ledger Nano S
The Solana Ledger app is not yet available on Ledger Live. Until it is, you
can install a development version of the app from the command-line. Note that
because the app is not installed via Ledger Live, you will need to approve
installation from an "unsafe" manager, as well as see the message, "This app
is not genuine" each time you open the app. Once the app is available on
Ledger Live, you can reinstall the app from there, and the message will no
longer be displayed.
1. Connect your Ledger device via USB and enter your pin to unlock it
2. Download and run the Solana Ledger app installer:
```text
curl -sSLf https://github.com/solana-labs/ledger-app-solana/releases/download/v0.1.1/install.sh | sh
```
3. When prompted, approve the "unsafe" manager on your device
4. When prompted, approve the installation on your device
5. An installation window appears and your device will display Processing…
6. The app installation is confirmed
### Future: Installation once the Solana app is on Ledger Live
1. Open the Manager in Ledger Live
1. Connect your Ledger device via USB and enter your pin to unlock it
3. When prompted, approve the manager on your device
4. Find Solana in the app catalog and click Install
5. An installation window appears and your device will display Processing…
6. The app installation is confirmed
## Use Ledger Device with Solana CLI
1. Plug your Ledger device into your computer's USB port
2. Enter your pin and start the Solana app on the Ledger device
3. On your computer, run:
```text
solana address --keypair usb://ledger
```
This confirms your Ledger device is connected properly and in the correct state
to interact with the Solana CLI. The command returns your Ledger's unique
*wallet key*. When you have multiple Nano S devices connected to the same
computer, you can use your wallet key to specify which Ledger hardware wallet
you want to use. Run the same command again, but this time, with its fully
qualified URL:
```text
solana address --keypair usb://ledger/nano-s/<WALLET_KEY>
```
Confirm it prints the same key as when you entered just `usb://ledger`.
### Ledger Device URLs
Solana defines a format for the URL protocol "usb://" to uniquely locate any Solana key on
any remote wallet connected to your computer.
The URL has the form, where square brackets denote optional fields:
```text
usb://ledger[/<LEDGER_TYPE>[/<WALLET_KEY>]][?key=<DERIVATION_PATH>]
```
`LEDGER_TYPE` is optional and defaults to the value "nano-s". If the value is provided,
it must be "nano-s" without quotes, the only supported Ledger device at this time.
`WALLET_KEY` is used to disambiguate multiple Nano S devices. Every Ledger has
a unique master key and from that key derives a separate unique key per app.
`DERVIATION_PATH` is used to navigate to Solana keys within your Ledger hardware
wallet. The path has the form `<ACCOUNT>[/<CHANGE>]`, where each `ACCOUNT` and
`CHANGE` are postive integers.
All derivation paths implicitly include the prefix `44'/501'`, which indicates
the path follows the [BIP44 specifications](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
and that any dervied keys are Solana keys (Coin type 501). The single quote
indicates a "hardened" derivation. Because Solana uses Ed25519 keypairs, all
derivations are hardened and therefore adding the quote is optional and
unnecessary.
For example, a complete Ledger device path might be:
```text
usb://ledger/nano-s/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
```
### Set CLI Configuration
If you want to set a Ledger key as the default signer for CLI commands, use the
[CLI configuration settings](../cli/usage.md#solana-config):
```text
solana config set --keypair <LEDGER_URL>
```
For example:
```text
solana config set --keypair usb://ledger?key=0
```
### Check Account Balance
```text
solana balance --keypair usb://ledger?key=12345
```
Or with the default signer:
```text
solana balance
```
### Send SOL via Ledger Device
```text
solana transfer <RECIPIENT> <AMOUNT> --from <LEDGER_URL>
```
Or with the default signer:
```text
solana transfer <RECIPIENT> <AMOUNT>
```
### Delegate Stake with Ledger Device
```text
solana delegate-stake <STAKE_ACCOUNT> <VOTER_ID> --keypair <LEDGER_URL>
```
Or with the default signer:
```text
solana delegate-stake <STAKE_ACCOUNT> <VOTER_ID>
```
## Support
Email maintainers@solana.com

View File

@ -8,9 +8,9 @@ Please note some of the information and instructions described here may change i
Archivers are specialized light clients. They download a part of the ledger \(a.k.a Segment\) and store it. They earn rewards for storing segments.
The testnet features a validator running at testnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
The testnet features a validator running at devnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
Additionally there is a blockexplorer available at [http://testnet.solana.com/](http://testnet.solana.com/).
Additionally there is a blockexplorer available at [http://devnet.solana.com/](http://devnet.solana.com/).
The testnet is configured to reset the ledger daily, or sooner should the hourly automated cluster sanity test fail.
@ -29,10 +29,10 @@ Before starting an archiver node, sanity check that the cluster is accessible to
Fetch the current transaction count over JSON RPC:
```bash
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://testnet.solana.com:8899
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://devnet.solana.com:8899
```
Inspect the blockexplorer at [http://testnet.solana.com/](http://testnet.solana.com/) for activity.
Inspect the blockexplorer at [http://devnet.solana.com/](http://devnet.solana.com/) for activity.
View the [metrics dashboard](https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?var-testnet=testnet) for more detail on cluster activity.
@ -95,7 +95,7 @@ Download the binaries by navigating to [https://github.com/solana-labs/solana/re
Try running following command to join the gossip network and view all the other nodes in the cluster:
```bash
solana-gossip spy --entrypoint testnet.solana.com:8001
solana-gossip spy --entrypoint devnet.solana.com:8001
# Press ^C to exit
```
@ -129,7 +129,7 @@ Use solana-keygen to show the public keys for each of the keypairs, they will be
```text
Then set up the storage accounts for your archiver by running:
```bash
solana --keypair archiver-keypair.json airdrop 100000 lamports
solana --keypair archiver-keypair.json airdrop .0001
solana --keypair archiver-keypair.json create-archiver-storage-account $ARCHIVER_IDENTITY $STORAGE_IDENTITY
```
@ -138,7 +138,7 @@ Note: Every time the testnet restarts, run the steps to setup the archiver accou
To start the archiver:
```bash
solana-archiver --entrypoint testnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
solana-archiver --entrypoint devnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
```
## Verify Archiver Setup
@ -146,7 +146,7 @@ solana-archiver --entrypoint testnet.solana.com:8001 --identity-keypair archiver
From another console, confirm the IP address and **identity pubkey** of your archiver is visible in the gossip network by running:
```bash
solana-gossip spy --entrypoint testnet.solana.com:8001
solana-gossip spy --entrypoint devnet.solana.com:8001
```
Provide the **storage account pubkey** to the `solana storage-account` command to view the recent mining activity from your archiver:

View File

@ -13,9 +13,7 @@ serve as the entrypoint to the cluster for your validator.
Current testnet entrypoints:
* Stable, testnet.solana.com
* Beta, beta.testnet.solana.com
* Edge, edge.testnet.solana.com
* Developer testnet, devnet.solana.com
Solana may launch special testnets for validator participation; we will provide
you with a specific entrypoint URL to use.

View File

@ -6,7 +6,7 @@ Confirm the IP address and **identity pubkey** of your validator is visible in
the gossip network by running:
```bash
solana-gossip spy --entrypoint testnet.solana.com:8001
solana-gossip spy --entrypoint devnet.solana.com:8001
```
## Check Your Balance
@ -35,13 +35,13 @@ cluster, as well as the health of the cluster:
```bash
# Similar to solana-gossip, you should see your validator in the list of cluster nodes
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://testnet.solana.com:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://devnet.solana.com:8899
# If your validator is properly voting, it should appear in the list of `current` vote accounts. If staked, `stake` should be > 0
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://testnet.solana.com:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://devnet.solana.com:8899
# Returns the current leader schedule
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://testnet.solana.com:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://devnet.solana.com:8899
# Returns info about the current epoch. slotIndex should progress on subsequent calls.
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://testnet.solana.com:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://devnet.solana.com:8899
```

View File

@ -1,14 +1,14 @@
# Installing the Validator Software
Install the Solana release
[v0.21.0](https://github.com/solana-labs/solana/releases/tag/v0.21.0) on your
[v0.23.1](https://github.com/solana-labs/solana/releases/tag/v0.23.1) on your
machine by running:
```bash
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.21.0/install/solana-install-init.sh | sh -s - 0.21.0
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v0.23.1/install/solana-install-init.sh | sh -s - 0.23.1
```
If you are connecting to a different testnet, you can replace `0.21.0` with the
If you are connecting to a different testnet, you can replace `0.23.1` with the
release tag matching the software version of your desired testnet, or replace it
with the named channel `stable`, `beta`, or `edge`.
@ -16,11 +16,11 @@ The following output indicates a successful update:
```text
looking for latest release
downloading v0.21.0 installer
downloading v0.23.1 installer
Configuration: /home/solana/.config/solana/install/config.yml
Active release directory: /home/solana/.local/share/solana/install/active_release
* Release version: 0.21.0
* Release URL: https://github.com/solana-labs/solana/releases/download/v0.21.0/solana-release-x86_64-unknown-linux-gnu.tar.bz2
* Release version: 0.23.1
* Release URL: https://github.com/solana-labs/solana/releases/download/v0.23.1/solana-release-x86_64-unknown-linux-gnu.tar.bz2
Update successful
```

View File

@ -35,7 +35,7 @@ solana-keygen new -o ~/validator-stake-keypair.json
Now delegate 1 SOL to your validator by first creating your stake account:
```bash
solana create-stake-account ~/validator-stake-keypair.json 1 SOL
solana create-stake-account ~/validator-stake-keypair.json 1
```
and then delegating that stake to your validator:
@ -83,7 +83,6 @@ To monitor your validator during its warmup period:
* View your vote account:`solana vote-account ~/validator-vote-keypair.json` This displays the current state of all the votes the validator has submitted to the network.
* View your stake account, the delegation preference and details of your stake:`solana stake-account ~/validator-stake-keypair.json`
* `solana uptime ~/validator-vote-keypair.json` will display the voting history \(aka, uptime\) of your validator over recent Epochs
* `solana validators` displays the current active stake of all validators, including yours
* `solana stake-history ` shows the history of stake warming up and cooling down over recent epochs
* Look for log messages on your validator indicating your next leader slot: `[2019-09-27T20:16:00.319721164Z INFO solana_core::replay_stage] <VALIDATOR_IDENTITY_PUBKEY> voted and reset PoH at tick height ####. My next leader slot is ####`

View File

@ -6,11 +6,11 @@ The solana cli includes `get` and `set` configuration commands to automatically
set the `--url` argument for cli commands. For example:
```bash
solana config set --url http://testnet.solana.com:8899
solana config set --url http://devnet.solana.com:8899
```
\(You can always override the set configuration by explicitly passing the
`--url` argument with a command, eg: `solana --url http://beta.testnet.solana.com:8899 balance`\)
`--url` argument with a command, eg: `solana --url http://beta.devnet.solana.com:8899 balance`\)
## Confirm The Testnet Is Reachable
@ -33,7 +33,7 @@ Try running following command to join the gossip network and view all the other
nodes in the cluster:
```bash
solana-gossip spy --entrypoint testnet.solana.com:8001
solana-gossip spy --entrypoint devnet.solana.com:8001
# Press ^C to exit
```
@ -107,7 +107,7 @@ You should see the following output:
```text
Wallet Config Updated: /home/solana/.config/solana/wallet/config.yml
* url: http://testnet.solana.com:8899
* url: http://devnet.solana.com:8899
* keypair: /home/solana/validator-keypair.json
```
@ -155,7 +155,7 @@ Connect to a testnet cluster by running:
```bash
solana-validator --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json \
--ledger ~/validator-ledger --rpc-port 8899 --entrypoint testnet.solana.com:8001 \
--ledger ~/validator-ledger --rpc-port 8899 --entrypoint devnet.solana.com:8001 \
--limit-ledger-size
```
@ -166,7 +166,7 @@ Confirm your validator connected to the network by opening a new terminal and
running:
```bash
solana-gossip spy --entrypoint testnet.solana.com:8001
solana-gossip spy --entrypoint devnet.solana.com:8001
```
If your validator is connected, its public key and IP address will appear in the list.

View File

@ -5,8 +5,7 @@ that serves as an entrypoint to the cluster.
Current testnet entrypoints:
* Stable: testnet.solana.com
* Beta: beta.testnet.solana.com
* Stable: devnet.solana.com
Application developers should target the Stable testnet. Key differences
between the Stable testnet and what will be mainnet:
@ -28,13 +27,13 @@ You can submit a JSON-RPC request to see the specific software version of the
cluster. Use this to specify [the software version to install](validator-software.md).
```bash
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' testnet.solana.com:8899
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' devnet.solana.com:8899
```
Example result:
`{"jsonrpc":"2.0","result":{"solana-core":"0.21.0"},"id":1}`
## Using a Different Testnet
This guide is written in the context of testnet.solana.com, our most stable
This guide is written in the context of devnet.solana.com, our most stable
cluster. To participate in another testnet, modify the commands in the following
pages, replacing `testnet.solana.com` with your desired testnet.
pages, replacing `devnet.solana.com` with your desired testnet.

View File

@ -1,6 +1,6 @@
[package]
name = "solana-chacha-cuda"
version = "0.23.0"
version = "0.23.9"
description = "Solana Chacha Cuda APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -10,12 +10,12 @@ edition = "2018"
[dependencies]
log = "0.4.8"
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.0" }
solana-chacha = { path = "../chacha", version = "0.23.0" }
solana-ledger = { path = "../ledger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-perf = { path = "../perf", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.9" }
solana-chacha = { path = "../chacha", version = "0.23.9" }
solana-ledger = { path = "../ledger", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-perf = { path = "../perf", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
[dev-dependencies]
hex-literal = "0.2.1"

View File

@ -118,7 +118,7 @@ mod tests {
use solana_ledger::entry::create_ticks;
use solana_ledger::get_tmp_ledger_path;
use solana_sdk::clock::DEFAULT_SLOTS_PER_SEGMENT;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::Keypair;
use std::fs::{remove_dir_all, remove_file};
use std::path::Path;

View File

@ -1,6 +1,6 @@
[package]
name = "solana-chacha-sys"
version = "0.23.0"
version = "0.23.9"
description = "Solana chacha-sys"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"

View File

@ -1,6 +1,6 @@
[package]
name = "solana-chacha"
version = "0.23.0"
version = "0.23.9"
description = "Solana Chacha APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -12,11 +12,11 @@ edition = "2018"
log = "0.4.8"
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.0" }
solana-ledger = { path = "../ledger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-perf = { path = "../perf", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.9" }
solana-ledger = { path = "../ledger", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-perf = { path = "../perf", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
[dev-dependencies]
hex-literal = "0.2.1"

View File

@ -81,7 +81,7 @@ mod tests {
use solana_ledger::get_tmp_ledger_path;
use solana_sdk::hash::{hash, Hash, Hasher};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use std::fs::remove_file;
use std::fs::File;

View File

@ -78,7 +78,9 @@ ARGS+=(
# Also propagate environment variables needed for codecov
# https://docs.codecov.io/docs/testing-with-docker#section-codecov-inside-docker
# We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite...
CODECOV_ENVS=$(CI=true bash <(curl -s https://codecov.io/env))
# Unfortunately, codecov.io fails sometimes:
# curl: (7) Failed to connect to codecov.io port 443: Connection timed out
CODECOV_ENVS=$(CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused https://codecov.io/env; do sleep 10; done))
if $INTERACTIVE; then
if [[ -n $1 ]]; then

View File

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

View File

@ -1,6 +1,6 @@
# Note: when the rust version is changed also modify
# ci/rust-version.sh to pick up the new image tag
FROM rust:1.40.0
FROM rust:1.41.1
# Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0
@ -17,6 +17,7 @@ RUN set -x \
clang-7 \
cmake \
lcov \
libudev-dev \
libclang-common-7-dev \
mscgen \
net-tools \

View File

@ -16,13 +16,13 @@
if [[ -n $RUST_STABLE_VERSION ]]; then
stable_version="$RUST_STABLE_VERSION"
else
stable_version=1.40.0
stable_version=1.41.1
fi
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
nightly_version="$RUST_NIGHTLY_VERSION"
else
nightly_version=2019-12-19
nightly_version=2020-02-27
fi

View File

@ -42,7 +42,9 @@ if [[ -z "$CODECOV_TOKEN" ]]; then
echo CODECOV_TOKEN undefined, codecov.io upload skipped
else
# We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite...
CI=true bash <(curl -s https://codecov.io/bash) -X gcov -f target/cov/lcov.info
# Unfortunately, codecov.io fails sometimes:
# curl: (7) Failed to connect to codecov.io port 443: Connection timed out
CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused https://codecov.io/bash; do sleep 10; done) -Z -X gcov -f target/cov/lcov.info
annotate --style success --context codecov.io \
"CodeCov report: https://codecov.io/github/solana-labs/solana/commit/${CI_COMMIT:0:9}"

View File

@ -234,7 +234,7 @@ sanity() {
(
set -x
NO_INSTALL_CHECK=1 \
ci/testnet-sanity.sh edge-testnet-solana-com gce -P us-west1-b
ci/testnet-sanity.sh edge-devnet-solana-com gce -P us-west1-b
maybe_deploy_software
)
;;
@ -249,7 +249,7 @@ sanity() {
(
set -x
NO_INSTALL_CHECK=1 \
ci/testnet-sanity.sh beta-testnet-solana-com gce -P us-west1-b
ci/testnet-sanity.sh beta-devnet-solana-com gce -P us-west1-b
maybe_deploy_software --deploy-if-newer
)
;;
@ -263,7 +263,7 @@ sanity() {
testnet)
(
set -x
ci/testnet-sanity.sh testnet-solana-com gce -P us-west1-b
ci/testnet-sanity.sh devnet-solana-com gce -P us-west1-b
)
;;
testnet-perf)
@ -327,9 +327,9 @@ deploy() {
testnet-edge)
(
set -x
ci/testnet-deploy.sh -p edge-testnet-solana-com -C gce -z us-west1-b \
ci/testnet-deploy.sh -p edge-devnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
-a edge-testnet-solana-com --letsencrypt edge.testnet.solana.com \
-a edge-devnet-solana-com --letsencrypt edge.devnet.solana.com \
--limit-ledger-size \
${skipCreate:+-e} \
${skipStart:+-s} \
@ -352,9 +352,9 @@ deploy() {
testnet-beta)
(
set -x
ci/testnet-deploy.sh -p beta-testnet-solana-com -C gce -z us-west1-b \
ci/testnet-deploy.sh -p beta-devnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
-a beta-testnet-solana-com --letsencrypt beta.testnet.solana.com \
-a beta-devnet-solana-com --letsencrypt beta.devnet.solana.com \
--limit-ledger-size \
${skipCreate:+-e} \
${skipStart:+-s} \
@ -377,9 +377,9 @@ deploy() {
testnet)
(
set -x
ci/testnet-deploy.sh -p testnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 1 -c 0 -u -P \
-a testnet-solana-com --letsencrypt testnet.solana.com \
ci/testnet-deploy.sh -p devnet-solana-com -C gce -z us-west1-b \
-t "$CHANNEL_OR_TAG" -n 0 -c 0 -u -P \
-a testnet-solana-com --letsencrypt devnet.solana.com \
--limit-ledger-size \
${skipCreate:+-e} \
${skipStart:+-s} \
@ -389,7 +389,7 @@ deploy() {
(
echo "--- net.sh update"
set -x
time net/net.sh update -t "$CHANNEL_OR_TAG" --platform linux --platform osx --platform windows
time net/net.sh update -t "$CHANNEL_OR_TAG" --platform linux --platform osx #--platform windows
)
;;
testnet-perf)
@ -455,6 +455,10 @@ deploy() {
TDS_CLIENT_COUNT="1"
fi
if [[ -n $TDS_SLOTS_PER_EPOCH ]]; then
maybeSlotsPerEpoch=(--slots-per-epoch "$TDS_SLOTS_PER_EPOCH")
fi
if [[ -z $ENABLE_GPU ]]; then
maybeGpu=(-G "--machine-type n1-standard-16 --accelerator count=2,type=nvidia-tesla-v100")
elif [[ $ENABLE_GPU == skip ]]; then
@ -540,7 +544,7 @@ deploy() {
${maybeInternalNodesLamports} \
${maybeExternalAccountsFile} \
--target-lamports-per-signature 0 \
--slots-per-epoch 4096 \
"${maybeSlotsPerEpoch[@]}" \
${maybeAdditionalDisk}
)
;;

View File

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

View File

@ -1,13 +1,16 @@
use crate::keypair::{keypair_from_seed_phrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG};
use crate::keypair::{
keypair_from_seed_phrase, signer_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
};
use chrono::DateTime;
use clap::ArgMatches;
use solana_remote_wallet::remote_wallet::{DerivationPath, RemoteWalletManager};
use solana_sdk::{
clock::UnixTimestamp,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, KeypairUtil, Signature},
signature::{read_keypair_file, Keypair, Signature, Signer},
};
use std::str::FromStr;
use std::{str::FromStr, sync::Arc};
// Return parsed values from matches at `name`
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
@ -92,14 +95,36 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
})
}
pub fn amount_of(matches: &ArgMatches<'_>, name: &str, unit: &str) -> Option<u64> {
if matches.value_of(unit) == Some("lamports") {
value_of(matches, name)
// Return a signer from matches at `name`
#[allow(clippy::type_complexity)]
pub fn signer_of(
matches: &ArgMatches<'_>,
name: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<(Option<Box<dyn Signer>>, Option<Pubkey>), Box<dyn std::error::Error>> {
if let Some(location) = matches.value_of(name) {
let signer = signer_from_path(matches, location, name, wallet_manager)?;
let signer_pubkey = signer.pubkey();
Ok((Some(signer), Some(signer_pubkey)))
} else {
value_of(matches, name).map(sol_to_lamports)
Ok((None, None))
}
}
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
value_of(matches, name).map(sol_to_lamports)
}
pub fn derivation_of(matches: &ArgMatches<'_>, name: &str) -> Option<DerivationPath> {
matches.value_of(name).map(|derivation_str| {
let derivation_str = derivation_str.replace("'", "");
let mut parts = derivation_str.split('/');
let account = parts.next().map(|account| account.parse::<u32>().unwrap());
let change = parts.next().map(|change| change.parse::<u32>().unwrap());
DerivationPath { account, change }
})
}
#[cfg(test)]
mod tests {
use super::*;
@ -258,23 +283,56 @@ mod tests {
}
#[test]
fn test_amount_of() {
fn test_lamports_of_sol() {
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "50", "--unit", "lamports"]);
assert_eq!(amount_of(&matches, "single", "unit"), Some(50));
assert_eq!(amount_of(&matches, "multiple", "unit"), None);
.get_matches_from(vec!["test", "--single", "50"]);
assert_eq!(lamports_of_sol(&matches, "single"), Some(50000000000));
assert_eq!(lamports_of_sol(&matches, "multiple"), None);
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "50", "--unit", "SOL"]);
assert_eq!(amount_of(&matches, "single", "unit"), Some(50000000000));
.get_matches_from(vec!["test", "--single", "1.5"]);
assert_eq!(lamports_of_sol(&matches, "single"), Some(1500000000));
assert_eq!(lamports_of_sol(&matches, "multiple"), None);
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "SOL"]);
assert_eq!(amount_of(&matches, "single", "unit"), Some(1500000000));
.get_matches_from(vec!["test", "--single", "0.03"]);
assert_eq!(lamports_of_sol(&matches, "single"), Some(30000000));
}
#[test]
fn test_derivation_of() {
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "lamports"]);
assert_eq!(amount_of(&matches, "single", "unit"), None);
.get_matches_from(vec!["test", "--single", "2/3"]);
assert_eq!(
derivation_of(&matches, "single"),
Some(DerivationPath {
account: Some(2),
change: Some(3)
})
);
assert_eq!(derivation_of(&matches, "another"), None);
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "2"]);
assert_eq!(
derivation_of(&matches, "single"),
Some(DerivationPath {
account: Some(2),
change: None
})
);
assert_eq!(derivation_of(&matches, "another"), None);
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "2'/3'"]);
assert_eq!(
derivation_of(&matches, "single"),
Some(DerivationPath {
account: Some(2),
change: Some(3)
})
);
}
}

View File

@ -1,8 +1,11 @@
use crate::keypair::ASK_KEYWORD;
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
use chrono::DateTime;
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{read_keypair_file, Signature};
use solana_sdk::{
clock::Slot,
hash::Hash,
pubkey::Pubkey,
signature::{read_keypair_file, Signature},
};
use std::str::FromStr;
// Return an error if a pubkey cannot be parsed.
@ -48,6 +51,13 @@ pub fn is_pubkey_or_keypair_or_ask_keyword(string: String) -> Result<(), String>
is_pubkey(string.clone()).or_else(|_| is_keypair_or_ask_keyword(string))
}
pub fn is_valid_signer(string: String) -> Result<(), String> {
match parse_keypair_path(&string) {
KeypairUrl::Filepath(path) => is_keypair(path),
_ => Ok(()),
}
}
// Return an error if string cannot be parsed as pubkey=signature string
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
let mut signer = string.split('=');
@ -84,18 +94,10 @@ pub fn is_url(string: String) -> Result<(), String> {
}
}
pub fn is_semver(semver: &str) -> Result<(), String> {
match semver::Version::parse(&semver) {
Ok(_) => Ok(()),
Err(err) => Err(format!("{:?}", err)),
}
}
pub fn is_release_channel(channel: &str) -> Result<(), String> {
match channel {
"edge" | "beta" | "stable" => Ok(()),
_ => Err(format!("Invalid release channel {}", channel)),
}
pub fn is_slot(slot: String) -> Result<(), String> {
slot.parse::<Slot>()
.map(|_| ())
.map_err(|e| format!("{:?}", e))
}
pub fn is_port(port: String) -> Result<(), String> {
@ -141,3 +143,48 @@ pub fn is_rfc3339_datetime(value: String) -> Result<(), String> {
.map(|_| ())
.map_err(|e| format!("{:?}", e))
}
pub fn is_derivation(value: String) -> Result<(), String> {
let value = value.replace("'", "");
let mut parts = value.split('/');
let account = parts.next().unwrap();
account
.parse::<u32>()
.map_err(|e| {
format!(
"Unable to parse derivation, provided: {}, err: {:?}",
account, e
)
})
.and_then(|_| {
if let Some(change) = parts.next() {
change.parse::<u32>().map_err(|e| {
format!(
"Unable to parse derivation, provided: {}, err: {:?}",
change, e
)
})
} else {
Ok(0)
}
})
.map(|_| ())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_derivation() {
assert_eq!(is_derivation("2".to_string()), Ok(()));
assert_eq!(is_derivation("0".to_string()), Ok(()));
assert_eq!(is_derivation("65537".to_string()), Ok(()));
assert_eq!(is_derivation("0/2".to_string()), Ok(()));
assert_eq!(is_derivation("0'/2'".to_string()), Ok(()));
assert!(is_derivation("a".to_string()).is_err());
assert!(is_derivation("4294967296".to_string()).is_err());
assert!(is_derivation("a/b".to_string()).is_err());
assert!(is_derivation("0/4294967296".to_string()).is_err());
}
}

View File

@ -1,20 +1,114 @@
use crate::ArgConstant;
use crate::{
input_parsers::{derivation_of, pubkeys_sigs_of},
offline::SIGNER_ARG,
ArgConstant,
};
use bip39::{Language, Mnemonic, Seed};
use clap::values_t;
use clap::{values_t, ArgMatches, Error, ErrorKind};
use rpassword::prompt_password_stderr;
use solana_remote_wallet::{
remote_keypair::generate_remote_keypair,
remote_wallet::{RemoteWalletError, RemoteWalletManager},
};
use solana_sdk::{
pubkey::Pubkey,
signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
KeypairUtil,
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
read_keypair_file, Keypair, Presigner, Signature, Signer,
},
};
use std::{
error,
io::{stdin, stdout, Write},
process::exit,
str::FromStr,
sync::Arc,
};
pub enum KeypairUrl {
Ask,
Filepath(String),
Usb(String),
Stdin,
Pubkey(Pubkey),
}
pub fn parse_keypair_path(path: &str) -> KeypairUrl {
if path == "-" {
KeypairUrl::Stdin
} else if path == ASK_KEYWORD {
KeypairUrl::Ask
} else if path.starts_with("usb://") {
KeypairUrl::Usb(path.to_string())
} else if let Ok(pubkey) = Pubkey::from_str(path) {
KeypairUrl::Pubkey(pubkey)
} else {
KeypairUrl::Filepath(path.to_string())
}
}
pub fn presigner_from_pubkey_sigs(
pubkey: &Pubkey,
signers: &[(Pubkey, Signature)],
) -> Option<Presigner> {
signers.iter().find_map(|(signer, sig)| {
if *signer == *pubkey {
Some(Presigner::new(signer, sig))
} else {
None
}
})
}
pub fn signer_from_path(
matches: &ArgMatches,
path: &str,
keypair_name: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
Ok(Box::new(keypair_from_seed_phrase(
keypair_name,
skip_validation,
false,
)?))
}
KeypairUrl::Filepath(path) => Ok(Box::new(read_keypair_file(&path)?)),
KeypairUrl::Stdin => {
let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?))
}
KeypairUrl::Usb(path) => {
if let Some(wallet_manager) = wallet_manager {
Ok(Box::new(generate_remote_keypair(
path,
derivation_of(matches, "derivation_path"),
wallet_manager,
matches.is_present("confirm_key"),
)?))
} else {
Err(RemoteWalletError::NoDeviceFound.into())
}
}
KeypairUrl::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
.as_ref()
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
if let Some(presigner) = presigner {
Ok(Box::new(presigner))
} else {
Err(Error::with_description(
"Missing signature for supplied pubkey",
ErrorKind::MissingRequiredArgument,
)
.into())
}
}
}
}
// Keyword used to indicate that the user should be asked for a keypair seed phrase
pub const ASK_KEYWORD: &str = "ASK";
@ -32,8 +126,8 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
#[derive(Debug, PartialEq)]
pub enum Source {
File,
Generated,
Path,
SeedPhrase,
}
@ -87,7 +181,7 @@ pub fn keypair_from_seed_phrase(
};
if confirm_pubkey {
let pubkey = Pubkey::new(keypair.public.as_ref());
let pubkey = keypair.pubkey();
print!("Recovered pubkey `{:?}`. Continue? (y/n): ", pubkey);
let _ignored = stdout().flush();
let mut input = String::new();
@ -131,7 +225,12 @@ pub fn keypair_input(
keypair_from_seed_phrase(keypair_name, skip_validation, true)
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
read_keypair_file(keypair_file).map(|keypair| KeypairWithSource::new(keypair, Source::File))
if keypair_file.starts_with("usb://") {
Ok(KeypairWithSource::new(Keypair::new(), Source::Path))
} else {
read_keypair_file(keypair_file)
.map(|keypair| KeypairWithSource::new(keypair, Source::Path))
}
} else {
Ok(KeypairWithSource::new(Keypair::new(), Source::Generated))
}

View File

@ -26,3 +26,4 @@ pub struct ArgConstant<'a> {
pub mod input_parsers;
pub mod input_validators;
pub mod keypair;
pub mod offline;

19
clap-utils/src/offline.rs Normal file
View File

@ -0,0 +1,19 @@
use crate::ArgConstant;
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash",
long: "blockhash",
help: "Use the supplied blockhash",
};
pub const SIGN_ONLY_ARG: ArgConstant<'static> = ArgConstant {
name: "sign_only",
long: "sign-only",
help: "Sign the transaction offline",
};
pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
name: "signer",
long: "signer",
help: "Provide a public-key/signature pair for the transaction",
};

16
cli-config/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
dirs = "2.0.2"
lazy_static = "1.4.0"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_yaml = "0.8.11"

View File

@ -1,8 +1,10 @@
// Wallet settings that can be configured for long-term use
use serde_derive::{Deserialize, Serialize};
use std::fs::{create_dir_all, File};
use std::io::{self, Write};
use std::path::Path;
use std::{
fs::{create_dir_all, File},
io::{self, Write},
path::Path,
};
lazy_static! {
pub static ref CONFIG_FILE: Option<String> = {

4
cli-config/src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
#[macro_use]
extern crate lazy_static;
pub mod config;

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "0.23.0"
version = "0.23.9"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -17,7 +17,6 @@ criterion-stats = "0.3.0"
ctrlc = { version = "3.1.3", features = ["termination"] }
console = "0.9.1"
dirs = "2.0.2"
lazy_static = "1.4.0"
log = "0.4.8"
indicatif = "0.13.0"
humantime = "2.0.0"
@ -27,25 +26,27 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.44"
serde_yaml = "0.8.11"
solana-budget-program = { path = "../programs/budget", version = "0.23.0" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
solana-client = { path = "../client", version = "0.23.0" }
solana-config-program = { path = "../programs/config", version = "0.23.0" }
solana-faucet = { path = "../faucet", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-runtime = { path = "../runtime", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-stake-program = { path = "../programs/stake", version = "0.23.0" }
solana-storage-program = { path = "../programs/storage", version = "0.23.0" }
solana-vote-program = { path = "../programs/vote", version = "0.23.0" }
solana-vote-signer = { path = "../vote-signer", version = "0.23.0" }
solana-budget-program = { path = "../programs/budget", version = "0.23.9" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.9" }
solana-cli-config = { path = "../cli-config", version = "0.23.9" }
solana-client = { path = "../client", version = "0.23.9" }
solana-config-program = { path = "../programs/config", version = "0.23.9" }
solana-faucet = { path = "../faucet", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.9" }
solana-runtime = { path = "../runtime", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
solana-stake-program = { path = "../programs/stake", version = "0.23.9" }
solana-storage-program = { path = "../programs/storage", version = "0.23.9" }
solana-vote-program = { path = "../programs/vote", version = "0.23.9" }
solana-vote-signer = { path = "../vote-signer", version = "0.23.9" }
titlecase = "1.1.0"
url = "2.1.1"
[dev-dependencies]
solana-core = { path = "../core", version = "0.23.0" }
solana-budget-program = { path = "../programs/budget", version = "0.23.0" }
solana-core = { path = "../core", version = "0.23.9" }
solana-budget-program = { path = "../programs/budget", version = "0.23.9" }
tempfile = "3.1.0"
[[bin]]

File diff suppressed because it is too large Load Diff

View File

@ -8,21 +8,25 @@ use crate::{
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use console::{style, Emoji};
use indicatif::{ProgressBar, ProgressStyle};
use solana_clap_utils::{input_parsers::*, input_validators::*};
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
use solana_client::{rpc_client::RpcClient, rpc_response::RpcVoteAccountInfo};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account_utils::StateMut,
clock::{self, Slot},
commitment_config::CommitmentConfig,
epoch_schedule::{Epoch, EpochSchedule},
epoch_schedule::Epoch,
hash::Hash,
message::Message,
pubkey::Pubkey,
signature::{Keypair, KeypairUtil},
system_transaction,
signature::{Keypair, Signer},
system_instruction,
transaction::Transaction,
};
use std::{
collections::{HashMap, VecDeque},
net::SocketAddr,
sync::Arc,
thread::sleep,
time::{Duration, Instant},
};
@ -67,6 +71,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.help("Slot number of the block to query")
)
)
.subcommand(SubCommand::with_name("leader-schedule").about("Display leader schedule"))
.subcommand(
SubCommand::with_name("epoch-info")
.about("Get information about the current epoch")
@ -215,11 +220,15 @@ pub fn parse_catchup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErro
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
Ok(CliCommandInfo {
command: CliCommand::Catchup { node_pubkey },
require_keypair: false,
signers: vec![],
})
}
pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pub fn parse_cluster_ping(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let lamports = value_t_or_exit!(matches, "lamports", u64);
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
let count = if matches.is_present("count") {
@ -241,7 +250,12 @@ pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cl
timeout,
commitment_config,
},
require_keypair: true,
signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
})
}
@ -249,7 +263,7 @@ pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
let slot = value_t_or_exit!(matches, "slot", u64);
Ok(CliCommandInfo {
command: CliCommand::GetBlockTime { slot },
require_keypair: false,
signers: vec![],
})
}
@ -261,7 +275,7 @@ pub fn parse_get_epoch_info(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
};
Ok(CliCommandInfo {
command: CliCommand::GetEpochInfo { commitment_config },
require_keypair: false,
signers: vec![],
})
}
@ -273,7 +287,7 @@ pub fn parse_get_slot(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErr
};
Ok(CliCommandInfo {
command: CliCommand::GetSlot { commitment_config },
require_keypair: false,
signers: vec![],
})
}
@ -285,7 +299,7 @@ pub fn parse_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliComman
};
Ok(CliCommandInfo {
command: CliCommand::GetTransactionCount { commitment_config },
require_keypair: false,
signers: vec![],
})
}
@ -298,7 +312,7 @@ pub fn parse_show_stakes(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cli
use_lamports_unit,
vote_account_pubkeys,
},
require_keypair: false,
signers: vec![],
})
}
@ -307,7 +321,7 @@ pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
Ok(CliCommandInfo {
command: CliCommand::ShowValidators { use_lamports_unit },
require_keypair: false,
signers: vec![],
})
}
@ -320,20 +334,6 @@ fn new_spinner_progress_bar() -> ProgressBar {
progress_bar
}
/// Aggregate epoch credit stats and return (total credits, total slots, total epochs)
pub fn aggregate_epoch_credits(
epoch_credits: &[(Epoch, u64, u64)],
epoch_schedule: &EpochSchedule,
) -> (u64, u64, u64) {
epoch_credits
.iter()
.fold((0, 0, 0), |acc, (epoch, credits, prev_credits)| {
let credits_earned = credits - prev_credits;
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
(acc.0 + credits_earned, acc.1 + slots_in_epoch, acc.2 + 1)
})
}
pub fn process_catchup(rpc_client: &RpcClient, node_pubkey: &Pubkey) -> ProcessResult {
let cluster_nodes = rpc_client.get_cluster_nodes()?;
@ -406,6 +406,41 @@ pub fn process_fees(rpc_client: &RpcClient) -> ProcessResult {
))
}
pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
let epoch_info = rpc_client.get_epoch_info()?;
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
let leader_schedule = rpc_client.get_leader_schedule(Some(first_slot_in_epoch))?;
if leader_schedule.is_none() {
return Err(format!(
"Unable to fetch leader schedule for slot {}",
first_slot_in_epoch
)
.into());
}
let leader_schedule = leader_schedule.unwrap();
let mut leader_per_slot_index = Vec::new();
for (pubkey, leader_slots) in leader_schedule.iter() {
for slot_index in leader_slots.iter() {
if *slot_index >= leader_per_slot_index.len() {
leader_per_slot_index.resize(*slot_index + 1, "?");
}
leader_per_slot_index[*slot_index] = pubkey;
}
}
for (slot_index, leader) in leader_per_slot_index.iter().enumerate() {
println!(
" {:<15} {:<44}",
first_slot_in_epoch + slot_index as u64,
leader
);
}
Ok("".to_string())
}
pub fn process_get_block_time(rpc_client: &RpcClient, slot: Slot) -> ProcessResult {
let timestamp = rpc_client.get_block_time(slot)?;
Ok(timestamp.to_string())
@ -429,11 +464,11 @@ pub fn process_get_epoch_info(
let start_slot = epoch_info.absolute_slot - epoch_info.slot_index;
let end_slot = start_slot + epoch_info.slots_in_epoch;
println_name_value(
"Epoch slot range:",
"Epoch Slot Range:",
&format!("[{}..{})", start_slot, end_slot),
);
println_name_value(
"Epoch completed percent:",
"Epoch Completed Percent:",
&format!(
"{:>3.3}%",
epoch_info.slot_index as f64 / epoch_info.slots_in_epoch as f64 * 100_f64
@ -441,14 +476,14 @@ pub fn process_get_epoch_info(
);
let remaining_slots_in_epoch = epoch_info.slots_in_epoch - epoch_info.slot_index;
println_name_value(
"Epoch completed slots:",
"Epoch Completed Slots:",
&format!(
"{}/{} ({} remaining)",
epoch_info.slot_index, epoch_info.slots_in_epoch, remaining_slots_in_epoch
),
);
println_name_value(
"Epoch completed time:",
"Epoch Completed Time:",
&format!(
"{}/{} ({} remaining)",
slot_to_human_time(epoch_info.slot_index),
@ -478,7 +513,7 @@ pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliComman
Ok(CliCommandInfo {
command: CliCommand::ShowBlockProduction { epoch, slot_limit },
require_keypair: false,
signers: vec![],
})
}
@ -673,8 +708,8 @@ pub fn process_ping(
) -> ProcessResult {
let to = Keypair::new().pubkey();
println_name_value("Source account:", &config.keypair.pubkey().to_string());
println_name_value("Destination account:", &to.to_string());
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
println_name_value("Destination Account:", &to.to_string());
println!();
let (signal_sender, signal_receiver) = std::sync::mpsc::channel();
@ -692,11 +727,13 @@ pub fn process_ping(
let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?;
last_blockhash = recent_blockhash;
let transaction =
system_transaction::transfer(&config.keypair, &to, lamports, recent_blockhash);
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
let message = Message::new(vec![ix]);
let mut transaction = Transaction::new_unsigned(message);
transaction.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&transaction.message,
)?;
@ -864,7 +901,7 @@ pub fn process_show_stakes(
}
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
let epoch_schedule = rpc_client.get_epoch_schedule()?;
let epoch_info = rpc_client.get_epoch_info()?;
let vote_accounts = rpc_client.get_vote_accounts()?;
let total_active_stake = vote_accounts
.current
@ -913,7 +950,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
"Commission",
"Last Vote",
"Root Block",
"Uptime",
"Credits",
"Active Stake",
))
.bold()
@ -921,7 +958,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
fn print_vote_account(
vote_account: RpcVoteAccountInfo,
epoch_schedule: &EpochSchedule,
current_epoch: Epoch,
total_active_stake: f64,
use_lamports_unit: bool,
delinquent: bool,
@ -934,17 +971,6 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
}
}
fn uptime(epoch_credits: Vec<(Epoch, u64, u64)>, epoch_schedule: &EpochSchedule) -> String {
let (total_credits, total_slots, _) =
aggregate_epoch_credits(&epoch_credits, &epoch_schedule);
if total_slots > 0 {
let total_uptime = 100_f64 * total_credits as f64 / total_slots as f64;
format!("{:.2}%", total_uptime)
} else {
"-".into()
}
}
println!(
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
if delinquent {
@ -957,7 +983,15 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
vote_account.commission,
non_zero_or_dash(vote_account.last_vote),
non_zero_or_dash(vote_account.root_slot),
uptime(vote_account.epoch_credits, epoch_schedule),
vote_account
.epoch_credits
.iter()
.find_map(|(epoch, credits, _)| if *epoch == current_epoch {
Some(*credits)
} else {
None
})
.unwrap_or(0),
if vote_account.activated_stake > 0 {
format!(
"{} ({:.2}%)",
@ -973,7 +1007,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
for vote_account in vote_accounts.current.into_iter() {
print_vote_account(
vote_account,
&epoch_schedule,
epoch_info.epoch,
total_active_stake,
use_lamports_unit,
false,
@ -982,7 +1016,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
for vote_account in vote_accounts.delinquent.into_iter() {
print_vote_account(
vote_account,
&epoch_schedule,
epoch_info.epoch,
total_active_stake,
use_lamports_unit,
true,
@ -996,28 +1030,38 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
mod tests {
use super::*;
use crate::cli::{app, parse_command};
use solana_sdk::signature::{write_keypair, Keypair};
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
#[test]
fn test_parse_command() {
let test_commands = app("test", "desc", "version");
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let test_cluster_version = test_commands
.clone()
.get_matches_from(vec!["test", "cluster-version"]);
assert_eq!(
parse_command(&test_cluster_version).unwrap(),
parse_command(&test_cluster_version, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::ClusterVersion,
require_keypair: false
signers: vec![],
}
);
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!(
parse_command(&test_fees).unwrap(),
parse_command(&test_fees, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::Fees,
require_keypair: false
signers: vec![],
}
);
@ -1027,10 +1071,10 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "block-time", &slot.to_string()]);
assert_eq!(
parse_command(&test_get_block_time).unwrap(),
parse_command(&test_get_block_time, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetBlockTime { slot },
require_keypair: false
signers: vec![],
}
);
@ -1038,12 +1082,12 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "epoch-info"]);
assert_eq!(
parse_command(&test_get_epoch_info).unwrap(),
parse_command(&test_get_epoch_info, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetEpochInfo {
commitment_config: CommitmentConfig::recent(),
},
require_keypair: false
signers: vec![],
}
);
@ -1051,21 +1095,21 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "genesis-hash"]);
assert_eq!(
parse_command(&test_get_genesis_hash).unwrap(),
parse_command(&test_get_genesis_hash, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetGenesisHash,
require_keypair: false
signers: vec![],
}
);
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
assert_eq!(
parse_command(&test_get_slot).unwrap(),
parse_command(&test_get_slot, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetSlot {
commitment_config: CommitmentConfig::recent(),
},
require_keypair: false
signers: vec![],
}
);
@ -1073,12 +1117,12 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "transaction-count"]);
assert_eq!(
parse_command(&test_transaction_count).unwrap(),
parse_command(&test_transaction_count, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetTransactionCount {
commitment_config: CommitmentConfig::recent(),
},
require_keypair: false
signers: vec![],
}
);
@ -1094,7 +1138,7 @@ mod tests {
"--confirmed",
]);
assert_eq!(
parse_command(&test_ping).unwrap(),
parse_command(&test_ping, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::Ping {
lamports: 1,
@ -1103,7 +1147,7 @@ mod tests {
timeout: Duration::from_secs(3),
commitment_config: CommitmentConfig::default(),
},
require_keypair: true
signers: vec![default_keypair.into()],
}
);
}

View File

@ -1,9 +1,5 @@
#[macro_use]
extern crate lazy_static;
pub mod cli;
pub mod cluster_query;
pub mod config;
pub mod display;
pub mod nonce;
pub mod offline;

View File

@ -2,20 +2,17 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches
use console::style;
use solana_clap_utils::{
input_validators::is_url,
keypair::{
self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
SKIP_SEED_PHRASE_VALIDATION_ARG,
},
input_parsers::derivation_of,
input_validators::{is_derivation, is_url},
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
};
use solana_cli::{
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliError},
config::{self, Config},
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
display::{println_name_value, println_name_value_or},
};
use solana_sdk::signature::read_keypair_file;
use std::error;
use solana_cli_config::config::{Config, CONFIG_FILE};
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
use std::{error, sync::Arc};
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() {
@ -24,21 +21,25 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
if let Some(config_file) = matches.value_of("config_file") {
let config = Config::load(config_file).unwrap_or_default();
if let Some(field) = subcommand_matches.value_of("specific_setting") {
let (value, default_value) = match field {
"url" => (config.url, CliConfig::default_json_rpc_url()),
"keypair" => (config.keypair_path, CliConfig::default_keypair_path()),
let (field_name, value, default_value) = match field {
"url" => ("RPC URL", config.url, CliConfig::default_json_rpc_url()),
"keypair" => (
"Key Path",
config.keypair_path,
CliConfig::default_keypair_path(),
),
_ => unreachable!(),
};
println_name_value_or(&format!("* {}:", field), &value, &default_value);
println_name_value_or(&format!("{}:", field_name), &value, &default_value);
} else {
println_name_value("Wallet Config:", config_file);
println_name_value("Config File:", config_file);
println_name_value_or(
"* url:",
"RPC URL:",
&config.url,
&CliConfig::default_json_rpc_url(),
);
println_name_value_or(
"* keypair:",
"Keypair Path:",
&config.keypair_path,
&CliConfig::default_keypair_path(),
);
@ -61,9 +62,9 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
config.keypair_path = keypair.to_string();
}
config.save(config_file)?;
println_name_value("Wallet Config Updated:", config_file);
println_name_value("* url:", &config.url);
println_name_value("* keypair:", &config.keypair_path);
println_name_value("Config File:", config_file);
println_name_value("RPC URL:", &config.url);
println_name_value("Keypair Path:", &config.keypair_path);
} else {
println!(
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
@ -79,7 +80,10 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
Ok(parse_args)
}
pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::Error>> {
pub fn parse_args<'a>(
matches: &ArgMatches<'_>,
wallet_manager: Option<Arc<RemoteWalletManager>>,
) -> Result<(CliConfig<'a>, CliSigners), Box<dyn error::Error>> {
let config = if let Some(config_file) = matches.value_of("config_file") {
Config::load(config_file).unwrap_or_default()
} else {
@ -94,57 +98,29 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
default.json_rpc_url
};
let CliCommandInfo {
command,
require_keypair,
} = parse_command(&matches)?;
let (keypair, keypair_path) = if require_keypair {
let KeypairWithSource { keypair, source } = keypair_input(&matches, "keypair")?;
match source {
keypair::Source::File => (
keypair,
Some(matches.value_of("keypair").unwrap().to_string()),
),
keypair::Source::SeedPhrase => (keypair, None),
keypair::Source::Generated => {
let keypair_path = if config.keypair_path != "" {
config.keypair_path
} else {
let default_keypair_path = CliConfig::default_keypair_path();
if !std::path::Path::new(&default_keypair_path).exists() {
return Err(CliError::KeypairFileNotFound(format!(
"Generate a new keypair at {} with `solana-keygen new`",
default_keypair_path
))
.into());
}
default_keypair_path
};
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
Err(CliError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, keypair_path
)))
})?;
(keypair, Some(keypair_path))
}
}
let default_signer_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap().to_string()
} else if config.keypair_path != "" {
config.keypair_path
} else {
let default = CliConfig::default();
(default.keypair, None)
CliConfig::default_keypair_path()
};
Ok(CliConfig {
command,
json_rpc_url,
keypair,
keypair_path,
rpc_client: None,
verbose: matches.is_present("verbose"),
})
let CliCommandInfo { command, signers } =
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
Ok((
CliConfig {
command,
json_rpc_url,
signers: vec![],
keypair_path: default_signer_path,
derivation_path: derivation_of(matches, "derivation_path"),
rpc_client: None,
verbose: matches.is_present("verbose"),
},
signers,
))
}
fn main() -> Result<(), Box<dyn error::Error>> {
@ -162,7 +138,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true)
.global(true)
.help("Configuration file to use");
if let Some(ref config_file) = *config::CONFIG_FILE {
if let Some(ref config_file) = *CONFIG_FILE {
arg.default_value(&config_file)
} else {
arg
@ -185,7 +161,16 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.value_name("PATH")
.global(true)
.takes_value(true)
.help("/path/to/id.json"),
.help("/path/to/id.json or usb://remote/wallet/path"),
)
.arg(
Arg::with_name("derivation_path")
.long("derivation-path")
.value_name("ACCOUNT or ACCOUNT/CHANGE")
.global(true)
.takes_value(true)
.validator(is_derivation)
.help("Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'")
)
.arg(
Arg::with_name("verbose")
@ -194,15 +179,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.global(true)
.help("Show extra information header"),
)
.arg(
Arg::with_name(ASK_SEED_PHRASE_ARG.name)
.long(ASK_SEED_PHRASE_ARG.long)
.value_name("KEYPAIR NAME")
.global(true)
.takes_value(true)
.possible_values(&["keypair"])
.help(ASK_SEED_PHRASE_ARG.help),
)
.arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
@ -240,7 +216,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.get_matches();
if parse_settings(&matches)? {
let config = parse_args(&matches)?;
let wallet_manager = maybe_wallet_manager()?;
let (mut config, signers) = parse_args(&matches, wallet_manager)?;
config.signers = signers.iter().map(|s| s.as_ref()).collect();
let result = process_command(&config)?;
println!("{}", result);
}

View File

@ -1,19 +1,21 @@
use crate::cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys,
log_instruction_custom_error, required_lamports_from, CliCommand, CliCommandInfo, CliConfig,
CliError, ProcessResult, SigningAuthority,
build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
SignerIndex,
};
use crate::offline::BLOCKHASH_ARG;
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{input_parsers::*, input_validators::*, ArgConstant};
use solana_clap_utils::{
input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
account_utils::StateMut,
hash::Hash,
message::Message,
nonce_state::{Meta, NonceState},
pubkey::Pubkey,
signature::{Keypair, KeypairUtil},
system_instruction::{
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
@ -22,6 +24,7 @@ use solana_sdk::{
system_program,
transaction::Transaction,
};
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub enum CliNonceError {
@ -65,8 +68,8 @@ 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 or PUBKEY")
.validator(is_pubkey_or_keypair_or_ask_keyword)
.value_name("KEYPAIR or PUBKEY or REMOTE WALLET PATH")
.validator(is_valid_signer)
.help(NONCE_AUTHORITY_ARG.help)
}
@ -121,15 +124,7 @@ impl NonceSubCommands for App<'_, '_> {
.takes_value(true)
.required(true)
.validator(is_amount)
.help("The amount to load the nonce account with (default unit SOL)"),
)
.arg(
Arg::with_name("unit")
.index(3)
.value_name("UNIT")
.takes_value(true)
.possible_values(&["SOL", "lamports"])
.help("Specify unit to use for request"),
.help("The amount to load the nonce account with, in SOL"),
)
.arg(
Arg::with_name(NONCE_AUTHORITY_ARG.name)
@ -216,58 +211,68 @@ impl NonceSubCommands for App<'_, '_> {
.takes_value(true)
.required(true)
.validator(is_amount)
.help("The amount to withdraw from the nonce account (default unit SOL)"),
)
.arg(
Arg::with_name("unit")
.index(4)
.value_name("UNIT")
.takes_value(true)
.possible_values(&["SOL", "lamports"])
.help("Specify unit to use for request"),
.help("The amount to withdraw from the nonce account, in SOL"),
)
.arg(nonce_authority_arg()),
)
}
}
pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pub fn parse_authorize_nonce_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
let new_authority = pubkey_of(matches, "new_authority").unwrap();
let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) {
Some(SigningAuthority::new_from_matches(
&matches,
NONCE_AUTHORITY_ARG.name,
None,
)?)
} else {
None
};
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount {
nonce_account,
nonce_authority,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
new_authority,
},
require_keypair: true,
signers: signer_info.signers,
})
}
pub fn parse_nonce_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let nonce_account = keypair_of(matches, "nonce_account_keypair").unwrap();
pub fn parse_nonce_create_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (nonce_account, nonce_account_pubkey) =
signer_of(matches, "nonce_account_keypair", wallet_manager)?;
let seed = matches.value_of("seed").map(|s| s.to_string());
let lamports = required_lamports_from(matches, "amount", "unit")?;
let lamports = lamports_of_sol(matches, "amount").unwrap();
let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name);
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_account],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::CreateNonceAccount {
nonce_account: nonce_account.into(),
nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
seed,
nonce_authority,
lamports,
},
require_keypair: true,
signers: signer_info.signers,
})
}
@ -276,28 +281,33 @@ pub fn parse_get_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliEr
Ok(CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_pubkey),
require_keypair: false,
signers: vec![],
})
}
pub fn parse_new_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pub fn parse_new_nonce(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) {
Some(SigningAuthority::new_from_matches(
&matches,
NONCE_AUTHORITY_ARG.name,
None,
)?)
} else {
None
};
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::NewNonce {
nonce_account,
nonce_authority,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
},
require_keypair: true,
signers: signer_info.signers,
})
}
@ -310,34 +320,37 @@ pub fn parse_show_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandIn
nonce_account_pubkey,
use_lamports_unit,
},
require_keypair: false,
signers: vec![],
})
}
pub fn parse_withdraw_from_nonce_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
let lamports = required_lamports_from(matches, "amount", "unit")?;
let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) {
Some(SigningAuthority::new_from_matches(
&matches,
NONCE_AUTHORITY_ARG.name,
None,
)?)
} else {
None
};
let lamports = lamports_of_sol(matches, "amount").unwrap();
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
destination_account_pubkey,
lamports,
},
require_keypair: true,
signers: signer_info.signers,
})
}
@ -373,41 +386,36 @@ pub fn process_authorize_nonce_account(
rpc_client: &RpcClient,
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: Option<&SigningAuthority>,
nonce_authority: SignerIndex,
new_authority: &Pubkey,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let nonce_authority = nonce_authority
.map(|a| a.keypair())
.unwrap_or(&config.keypair);
let nonce_authority = config.signers[nonce_authority];
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
let mut tx = Transaction::new_signed_with_payer(
vec![ix],
Some(&config.keypair.pubkey()),
&[&config.keypair, nonce_authority],
recent_blockhash,
);
let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<NonceError>(result)
}
pub fn process_create_nonce_account(
rpc_client: &RpcClient,
config: &CliConfig,
nonce_account: &Keypair,
nonce_account: SignerIndex,
seed: Option<String>,
nonce_authority: Option<Pubkey>,
lamports: u64,
) -> ProcessResult {
let nonce_account_pubkey = nonce_account.pubkey();
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
let nonce_account_address = if let Some(seed) = seed.clone() {
create_address_with_seed(&nonce_account_pubkey, &seed, &system_program::id())?
} else {
@ -415,7 +423,7 @@ pub fn process_create_nonce_account(
};
check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()),
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(&nonce_account_address, "nonce_account".to_string()),
)?;
@ -442,20 +450,20 @@ pub fn process_create_nonce_account(
.into());
}
let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.pubkey());
let nonce_authority = nonce_authority.unwrap_or_else(|| config.signers[0].pubkey());
let ixs = if let Some(seed) = seed {
create_nonce_account_with_seed(
&config.keypair.pubkey(), // from
&nonce_account_address, // to
&nonce_account_pubkey, // base
&seed, // seed
&config.signers[0].pubkey(), // from
&nonce_account_address, // to
&nonce_account_pubkey, // base
&seed, // seed
&nonce_authority,
lamports,
)
} else {
create_nonce_account(
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&nonce_account_pubkey,
&nonce_authority,
lamports,
@ -464,25 +472,17 @@ pub fn process_create_nonce_account(
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let signers = if nonce_account_pubkey != config.keypair.pubkey() {
vec![&config.keypair, nonce_account] // both must sign if `from` and `to` differ
} else {
vec![&config.keypair] // when stake_account == config.keypair and there's a seed, we only need one signature
};
let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let mut tx = Transaction::new_signed_with_payer(
ixs,
Some(&config.keypair.pubkey()),
&signers,
recent_blockhash,
);
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &signers);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
@ -510,10 +510,10 @@ pub fn process_new_nonce(
rpc_client: &RpcClient,
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: Option<&SigningAuthority>,
nonce_authority: SignerIndex,
) -> ProcessResult {
check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()),
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(&nonce_account, "nonce_account_pubkey".to_string()),
)?;
@ -524,25 +524,20 @@ pub fn process_new_nonce(
.into());
}
let nonce_authority = nonce_authority
.map(|a| a.keypair())
.unwrap_or(&config.keypair);
let nonce_authority = config.signers[nonce_authority];
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let mut tx = Transaction::new_signed_with_payer(
vec![ix],
Some(&config.keypair.pubkey()),
&[&config.keypair, nonce_authority],
recent_blockhash,
);
let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
rpc_client.send_and_confirm_transaction(&mut tx, &[config.signers[0], nonce_authority]);
log_instruction_custom_error::<SystemError>(result)
}
@ -561,11 +556,11 @@ pub fn process_show_nonce_account(
}
let print_account = |data: Option<(Meta, Hash)>| {
println!(
"balance: {}",
"Balance: {}",
build_balance_message(nonce_account.lamports, use_lamports_unit, true)
);
println!(
"minimum balance required: {}",
"Minimum Balance Required: {}",
build_balance_message(
rpc_client.get_minimum_balance_for_rent_exemption(NonceState::size())?,
use_lamports_unit,
@ -574,12 +569,12 @@ pub fn process_show_nonce_account(
);
match data {
Some((meta, hash)) => {
println!("nonce: {}", hash);
println!("authority: {}", meta.nonce_authority);
println!("Nonce: {}", hash);
println!("Authority: {}", meta.nonce_authority);
}
None => {
println!("nonce: uninitialized");
println!("authority: uninitialized");
println!("Nonce: uninitialized");
println!("Authority: uninitialized");
}
}
Ok("".to_string())
@ -599,35 +594,29 @@ pub fn process_withdraw_from_nonce_account(
rpc_client: &RpcClient,
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: Option<&SigningAuthority>,
nonce_authority: SignerIndex,
destination_account_pubkey: &Pubkey,
lamports: u64,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let nonce_authority = nonce_authority
.map(|a| a.keypair())
.unwrap_or(&config.keypair);
let nonce_authority = config.signers[nonce_authority];
let ix = withdraw_nonce_account(
nonce_account,
&nonce_authority.pubkey(),
destination_account_pubkey,
lamports,
);
let mut tx = Transaction::new_signed_with_payer(
vec![ix],
Some(&config.keypair.pubkey()),
&[&config.keypair, nonce_authority],
recent_blockhash,
);
let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<NonceError>(result)
}
@ -639,7 +628,7 @@ mod tests {
account::Account,
hash::hash,
nonce_state::{Meta as NonceMeta, NonceState},
signature::{read_keypair_file, write_keypair},
signature::{read_keypair_file, write_keypair, Keypair, Signer},
system_program,
};
use tempfile::NamedTempFile;
@ -652,6 +641,9 @@ mod tests {
#[test]
fn test_parse_command() {
let test_commands = app("test", "desc", "version");
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
@ -670,14 +662,14 @@ mod tests {
&Pubkey::default().to_string(),
]);
assert_eq!(
parse_command(&test_authorize_nonce_account).unwrap(),
parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey,
nonce_authority: None,
nonce_authority: 0,
new_authority: Pubkey::default(),
},
require_keypair: true,
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
@ -691,16 +683,17 @@ mod tests {
&authority_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize_nonce_account).unwrap(),
parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: Some(
read_keypair_file(&authority_keypair_file).unwrap().into()
),
nonce_authority: 1,
new_authority: Pubkey::default(),
},
require_keypair: true,
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authority_keypair_file).unwrap().into()
],
}
);
@ -710,18 +703,20 @@ mod tests {
"create-nonce-account",
&keypair_file,
"50",
"lamports",
]);
assert_eq!(
parse_command(&test_create_nonce_account).unwrap(),
parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
nonce_account: 1,
seed: None,
nonce_authority: None,
lamports: 50,
lamports: 50_000_000_000,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&keypair_file).unwrap().into()
],
}
);
@ -731,22 +726,22 @@ mod tests {
"create-nonce-account",
&keypair_file,
"50",
"lamports",
"--nonce-authority",
&authority_keypair_file,
]);
assert_eq!(
parse_command(&test_create_nonce_account).unwrap(),
parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
nonce_account: 1,
seed: None,
nonce_authority: Some(
read_keypair_file(&authority_keypair_file).unwrap().pubkey()
),
lamports: 50,
nonce_authority: Some(nonce_authority_keypair.pubkey()),
lamports: 50_000_000_000,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&keypair_file).unwrap().into()
],
}
);
@ -757,10 +752,10 @@ mod tests {
&nonce_account_string,
]);
assert_eq!(
parse_command(&test_get_nonce).unwrap(),
parse_command(&test_get_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_keypair.pubkey(),),
require_keypair: false
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
signers: vec![],
}
);
@ -771,13 +766,13 @@ mod tests {
.get_matches_from(vec!["test", "new-nonce", &keypair_file]);
let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!(
parse_command(&test_new_nonce).unwrap(),
parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
nonce_authority: None,
nonce_authority: 0,
},
require_keypair: true
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
@ -791,15 +786,16 @@ mod tests {
]);
let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!(
parse_command(&test_new_nonce).unwrap(),
parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
nonce_authority: Some(
read_keypair_file(&authority_keypair_file).unwrap().into()
),
nonce_authority: 1,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authority_keypair_file).unwrap().into()
],
}
);
@ -810,13 +806,13 @@ mod tests {
&nonce_account_string,
]);
assert_eq!(
parse_command(&test_show_nonce_account).unwrap(),
parse_command(&test_show_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::ShowNonceAccount {
nonce_account_pubkey: nonce_account_keypair.pubkey(),
use_lamports_unit: false,
},
require_keypair: false
signers: vec![],
}
);
@ -827,18 +823,22 @@ mod tests {
&keypair_file,
&nonce_account_string,
"42",
"lamports",
]);
assert_eq!(
parse_command(&test_withdraw_from_nonce_account).unwrap(),
parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: None,
nonce_authority: 0,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42
lamports: 42_000_000_000
},
require_keypair: true
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
@ -848,18 +848,22 @@ mod tests {
&keypair_file,
&nonce_account_string,
"42",
"SOL",
]);
assert_eq!(
parse_command(&test_withdraw_from_nonce_account).unwrap(),
parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: None,
nonce_authority: 0,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42000000000
},
require_keypair: true
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
@ -870,22 +874,27 @@ mod tests {
&keypair_file,
&nonce_account_string,
"42",
"lamports",
"--nonce-authority",
&authority_keypair_file,
]);
assert_eq!(
parse_command(&test_withdraw_from_nonce_account).unwrap(),
parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: Some(
read_keypair_file(&authority_keypair_file).unwrap().into()
),
nonce_authority: 1,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42
lamports: 42_000_000_000
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authority_keypair_file).unwrap().into()
],
}
);
}

View File

@ -1,26 +1,60 @@
use clap::{App, Arg};
use clap::{App, Arg, ArgMatches};
use serde_json::Value;
use solana_clap_utils::{
input_parsers::value_of,
input_validators::{is_hash, is_pubkey_sig},
ArgConstant,
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::Signature};
use std::str::FromStr;
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash",
long: "blockhash",
help: "Use the supplied blockhash",
};
#[derive(Clone, Debug, PartialEq)]
pub enum BlockhashQuery {
None(Hash, FeeCalculator),
FeeCalculator(Hash),
All,
}
pub const SIGN_ONLY_ARG: ArgConstant<'static> = ArgConstant {
name: "sign_only",
long: "sign-only",
help: "Sign the transaction offline",
};
impl BlockhashQuery {
pub fn new(blockhash: Option<Hash>, sign_only: bool) -> Self {
match blockhash {
Some(hash) if sign_only => Self::None(hash, FeeCalculator::default()),
Some(hash) if !sign_only => Self::FeeCalculator(hash),
None if !sign_only => Self::All,
_ => panic!("Cannot resolve blockhash"),
}
}
pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
name: "signer",
long: "signer",
help: "Provid a public-key/signature pair for the transaction",
};
pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self {
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
BlockhashQuery::new(blockhash, sign_only)
}
pub fn get_blockhash_fee_calculator(
&self,
rpc_client: &RpcClient,
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
let (hash, fee_calc) = match self {
BlockhashQuery::None(hash, fee_calc) => (Some(hash), Some(fee_calc)),
BlockhashQuery::FeeCalculator(hash) => (Some(hash), None),
BlockhashQuery::All => (None, None),
};
if None == fee_calc {
let (cluster_hash, fee_calc) = rpc_client.get_recent_blockhash()?;
Ok((*hash.unwrap_or(&cluster_hash), fee_calc))
} else {
Ok((*hash.unwrap(), fee_calc.unwrap().clone()))
}
}
}
impl Default for BlockhashQuery {
fn default() -> Self {
BlockhashQuery::All
}
}
fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(BLOCKHASH_ARG.name)
@ -61,3 +95,159 @@ impl OfflineArgs for App<'_, '_> {
.arg(signer_arg())
}
}
pub fn parse_sign_only_reply_string(reply: &str) -> (Hash, Vec<(Pubkey, Signature)>) {
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 signer_strings = object.get("signers").unwrap().as_array().unwrap();
let signers = signer_strings
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
(blockhash, signers)
}
#[cfg(test)]
mod tests {
use super::*;
use clap::App;
use serde_json::{self, json, Value};
use solana_client::{
rpc_request::RpcRequest,
rpc_response::{Response, RpcResponseContext},
};
use solana_sdk::{fee_calculator::FeeCalculator, hash::hash};
use std::collections::HashMap;
#[test]
fn test_blockhashspec_new_ok() {
let blockhash = hash(&[1u8]);
assert_eq!(
BlockhashQuery::new(Some(blockhash), true),
BlockhashQuery::None(blockhash, FeeCalculator::default()),
);
assert_eq!(
BlockhashQuery::new(Some(blockhash), false),
BlockhashQuery::FeeCalculator(blockhash),
);
assert_eq!(BlockhashQuery::new(None, false), BlockhashQuery::All,);
}
#[test]
#[should_panic]
fn test_blockhashspec_new_fail() {
BlockhashQuery::new(None, true);
}
#[test]
fn test_blockhashspec_new_from_matches_ok() {
let test_commands = App::new("blockhashspec_test").offline_args();
let blockhash = hash(&[1u8]);
let blockhash_string = blockhash.to_string();
let matches = test_commands.clone().get_matches_from(vec![
"blockhashspec_test",
"--blockhash",
&blockhash_string,
"--sign-only",
]);
assert_eq!(
BlockhashQuery::new_from_matches(&matches),
BlockhashQuery::None(blockhash, FeeCalculator::default()),
);
let matches = test_commands.clone().get_matches_from(vec![
"blockhashspec_test",
"--blockhash",
&blockhash_string,
]);
assert_eq!(
BlockhashQuery::new_from_matches(&matches),
BlockhashQuery::FeeCalculator(blockhash),
);
let matches = test_commands
.clone()
.get_matches_from(vec!["blockhashspec_test"]);
assert_eq!(
BlockhashQuery::new_from_matches(&matches),
BlockhashQuery::All,
);
}
#[test]
#[should_panic]
fn test_blockhashspec_new_from_matches_fail() {
let test_commands = App::new("blockhashspec_test")
.arg(blockhash_arg())
// We can really only hit this case unless the arg requirements
// are broken, so unset the requires() to recreate that condition
.arg(sign_only_arg().requires(""));
let matches = test_commands
.clone()
.get_matches_from(vec!["blockhashspec_test", "--sign-only"]);
BlockhashQuery::new_from_matches(&matches);
}
#[test]
fn test_blockhashspec_get_blockhash_fee_calc() {
let test_blockhash = hash(&[0u8]);
let rpc_blockhash = hash(&[1u8]);
let rpc_fee_calc = FeeCalculator::new(42, 42);
let get_recent_blockhash_response = json!(Response {
context: RpcResponseContext { slot: 1 },
value: json!((
Value::String(rpc_blockhash.to_string()),
serde_json::to_value(rpc_fee_calc.clone()).unwrap()
)),
});
let mut mocks = HashMap::new();
mocks.insert(
RpcRequest::GetRecentBlockhash,
get_recent_blockhash_response.clone(),
);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert_eq!(
BlockhashQuery::All
.get_blockhash_fee_calculator(&rpc_client)
.unwrap(),
(rpc_blockhash, rpc_fee_calc.clone()),
);
let mut mocks = HashMap::new();
mocks.insert(
RpcRequest::GetRecentBlockhash,
get_recent_blockhash_response.clone(),
);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert_eq!(
BlockhashQuery::FeeCalculator(test_blockhash)
.get_blockhash_fee_calculator(&rpc_client)
.unwrap(),
(test_blockhash, rpc_fee_calc.clone()),
);
let mut mocks = HashMap::new();
mocks.insert(
RpcRequest::GetRecentBlockhash,
get_recent_blockhash_response.clone(),
);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert_eq!(
BlockhashQuery::None(test_blockhash, FeeCalculator::default())
.get_blockhash_fee_calculator(&rpc_client)
.unwrap(),
(test_blockhash, FeeCalculator::default()),
);
let rpc_client = RpcClient::new_mock("fails".to_string());
assert!(BlockhashQuery::All
.get_blockhash_fee_calculator(&rpc_client)
.is_err());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,17 @@
use crate::cli::{
check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand,
CliCommandInfo, CliConfig, CliError, ProcessResult,
CliCommandInfo, CliConfig, CliError, ProcessResult, SignerIndex,
};
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{input_parsers::*, input_validators::*};
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
use solana_client::rpc_client::RpcClient;
use solana_sdk::signature::Keypair;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account_utils::StateMut, message::Message, pubkey::Pubkey, signature::KeypairUtil,
system_instruction::SystemError, transaction::Transaction,
account_utils::StateMut, message::Message, pubkey::Pubkey, system_instruction::SystemError,
transaction::Transaction,
};
use solana_storage_program::storage_instruction::{self, StorageAccountType};
use std::sync::Arc;
pub trait StorageSubCommands {
fn storage_subcommands(self) -> Self;
@ -99,35 +100,49 @@ impl StorageSubCommands for App<'_, '_> {
pub fn parse_storage_create_archiver_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
let storage_account = keypair_of(matches, "storage_account").unwrap();
Ok(CliCommandInfo {
command: CliCommand::CreateStorageAccount {
account_owner,
storage_account: storage_account.into(),
storage_account: 1,
account_type: StorageAccountType::Archiver,
},
require_keypair: true,
signers: vec![
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?,
storage_account.into(),
],
})
}
pub fn parse_storage_create_validator_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
let storage_account = keypair_of(matches, "storage_account").unwrap();
Ok(CliCommandInfo {
command: CliCommand::CreateStorageAccount {
account_owner,
storage_account: storage_account.into(),
storage_account: 1,
account_type: StorageAccountType::Validator,
},
require_keypair: true,
signers: vec![
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?,
storage_account.into(),
],
})
}
pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pub fn parse_storage_claim_reward(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap();
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommandInfo {
@ -135,7 +150,12 @@ pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommand
node_account_pubkey,
storage_account_pubkey,
},
require_keypair: true,
signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
})
}
@ -145,20 +165,21 @@ pub fn parse_storage_get_account_command(
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommandInfo {
command: CliCommand::ShowStorageAccount(storage_account_pubkey),
require_keypair: false,
signers: vec![],
})
}
pub fn process_create_storage_account(
rpc_client: &RpcClient,
config: &CliConfig,
storage_account: SignerIndex,
account_owner: &Pubkey,
storage_account: &Keypair,
account_type: StorageAccountType,
) -> ProcessResult {
let storage_account = config.signers[storage_account];
let storage_account_pubkey = storage_account.pubkey();
check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()),
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(
&storage_account_pubkey,
"storage_account_pubkey".to_string(),
@ -183,7 +204,7 @@ pub fn process_create_storage_account(
.max(1);
let ixs = storage_instruction::create_storage_account(
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&account_owner,
&storage_account_pubkey,
required_balance,
@ -191,19 +212,16 @@ pub fn process_create_storage_account(
);
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let mut tx = Transaction::new_signed_instructions(
&[&config.keypair, &storage_account],
ixs,
recent_blockhash,
);
let message = Message::new(ixs);
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, &storage_account]);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
@ -217,13 +235,13 @@ pub fn process_claim_storage_reward(
let instruction =
storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey);
let signers = [&config.keypair];
let signers = [config.signers[0]];
let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
let mut tx = Transaction::new(&signers, message, recent_blockhash);
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
@ -251,7 +269,7 @@ pub fn process_show_storage_account(
CliError::RpcRequestError(format!("Unable to deserialize storage account: {:?}", err))
})?;
println!("{:#?}", storage_contract);
println!("account lamports: {}", account.lamports);
println!("Account Lamports: {}", account.lamports);
Ok("".to_string())
}
@ -259,7 +277,7 @@ pub fn process_show_storage_account(
mod tests {
use super::*;
use crate::cli::{app, parse_command};
use solana_sdk::signature::write_keypair;
use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
@ -273,6 +291,10 @@ mod tests {
let pubkey = Pubkey::new_rand();
let pubkey_string = pubkey.to_string();
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let (keypair_file, mut tmp_file) = make_tmp_file();
let storage_account_keypair = Keypair::new();
write_keypair(&storage_account_keypair, tmp_file.as_file_mut()).unwrap();
@ -284,14 +306,22 @@ mod tests {
&keypair_file,
]);
assert_eq!(
parse_command(&test_create_archiver_storage_account).unwrap(),
parse_command(
&test_create_archiver_storage_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo {
command: CliCommand::CreateStorageAccount {
account_owner: pubkey,
storage_account: storage_account_keypair.into(),
storage_account: 1,
account_type: StorageAccountType::Archiver,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
storage_account_keypair.into()
],
}
);
@ -308,14 +338,22 @@ mod tests {
&keypair_file,
]);
assert_eq!(
parse_command(&test_create_validator_storage_account).unwrap(),
parse_command(
&test_create_validator_storage_account,
&default_keypair_file,
None
)
.unwrap(),
CliCommandInfo {
command: CliCommand::CreateStorageAccount {
account_owner: pubkey,
storage_account: storage_account_keypair.into(),
storage_account: 1,
account_type: StorageAccountType::Validator,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
storage_account_keypair.into()
],
}
);
@ -326,13 +364,13 @@ mod tests {
&storage_account_string,
]);
assert_eq!(
parse_command(&test_claim_storage_reward).unwrap(),
parse_command(&test_claim_storage_reward, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::ClaimStorageReward {
node_account_pubkey: pubkey,
storage_account_pubkey,
},
require_keypair: true
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
}

View File

@ -11,19 +11,21 @@ use serde_json::{Map, Value};
use solana_clap_utils::{
input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url},
keypair::signer_from_path,
};
use solana_client::rpc_client::RpcClient;
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
commitment_config::CommitmentConfig,
message::Message,
pubkey::Pubkey,
signature::{Keypair, KeypairUtil},
signature::{Keypair, Signer},
transaction::Transaction,
};
use std::error;
use std::{error, sync::Arc};
use titlecase::titlecase;
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
@ -224,17 +226,26 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
}
}
pub fn parse_validator_info_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pub fn parse_validator_info_command(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey");
// Prepare validator info
let validator_info = parse_args(&matches);
let validator_info = parse_args(matches);
Ok(CliCommandInfo {
command: CliCommand::SetValidatorInfo {
validator_info,
force_keybase: matches.is_present("force"),
info_pubkey,
},
require_keypair: true,
signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
})
}
@ -244,7 +255,7 @@ pub fn parse_get_validator_info_command(
let info_pubkey = pubkey_of(matches, "info_pubkey");
Ok(CliCommandInfo {
command: CliCommand::GetValidatorInfo(info_pubkey),
require_keypair: false,
signers: vec![],
})
}
@ -257,7 +268,7 @@ pub fn process_set_validator_info(
) -> ProcessResult {
// Validate keybase username
if let Some(string) = validator_info.get("keybaseUsername") {
let result = verify_keybase(&config.keypair.pubkey(), &string);
let result = verify_keybase(&config.signers[0].pubkey(), &string);
if result.is_err() {
if force_keybase {
println!("--force supplied, ignoring: {:?}", result);
@ -282,7 +293,7 @@ pub fn process_set_validator_info(
})
.find(|(pubkey, account)| {
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
validator_pubkey == config.keypair.pubkey()
validator_pubkey == config.signers[0].pubkey()
});
// Create validator-info keypair to use if info_pubkey not provided or does not exist
@ -300,8 +311,8 @@ pub fn process_set_validator_info(
.poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default())
.unwrap_or(0);
let keys = vec![(id(), false), (config.keypair.pubkey(), true)];
let (message, signers): (Message, Vec<&Keypair>) = if balance == 0 {
let keys = vec![(id(), false), (config.signers[0].pubkey(), true)];
let (message, signers): (Message, Vec<&dyn Signer>) = if balance == 0 {
if info_pubkey != info_keypair.pubkey() {
println!(
"Account {:?} does not exist. Generating new keypair...",
@ -311,12 +322,12 @@ pub fn process_set_validator_info(
}
println!(
"Publishing info for Validator {:?}",
config.keypair.pubkey()
config.signers[0].pubkey()
);
let lamports = rpc_client
.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;
let mut instructions = config_instruction::create_account::<ValidatorInfo>(
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&info_keypair.pubkey(),
lamports,
keys.clone(),
@ -327,13 +338,13 @@ pub fn process_set_validator_info(
keys,
&validator_info,
)]);
let signers = vec![&config.keypair, &info_keypair];
let signers = vec![config.signers[0], &info_keypair];
let message = Message::new(instructions);
(message, signers)
} else {
println!(
"Updating Validator {:?} info at: {:?}",
config.keypair.pubkey(),
config.signers[0].pubkey(),
info_pubkey
);
let instructions = vec![config_instruction::store(
@ -342,17 +353,18 @@ pub fn process_set_validator_info(
keys,
&validator_info,
)];
let message = Message::new_with_payer(instructions, Some(&config.keypair.pubkey()));
let signers = vec![&config.keypair];
let message = Message::new_with_payer(instructions, Some(&config.signers[0].pubkey()));
let signers = vec![config.signers[0]];
(message, signers)
};
// Submit transaction
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let mut tx = Transaction::new(&signers, message, recent_blockhash);
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
@ -390,9 +402,12 @@ pub fn process_get_validator_info(rpc_client: &RpcClient, pubkey: Option<Pubkey>
parse_validator_info(&validator_info_pubkey, &validator_info_account)?;
println!();
println_name_value("Validator Identity Pubkey:", &validator_pubkey.to_string());
println_name_value(" info pubkey:", &validator_info_pubkey.to_string());
println_name_value(" Info Pubkey:", &validator_info_pubkey.to_string());
for (key, value) in validator_info.iter() {
println_name_value(&format!(" {}:", key), &value.as_str().unwrap_or("?"));
println_name_value(
&format!(" {}:", titlecase(key)),
&value.as_str().unwrap_or("?"),
);
}
}

View File

@ -1,19 +1,16 @@
use crate::{
cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
},
cluster_query::aggregate_epoch_credits,
use crate::cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, CliSignerInfo,
ProcessResult,
};
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{input_parsers::*, input_validators::*};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
message::Message,
pubkey::Pubkey,
signature::Keypair,
signature::KeypairUtil,
system_instruction::{create_address_with_seed, SystemError},
transaction::Transaction,
};
@ -21,6 +18,7 @@ use solana_vote_program::{
vote_instruction::{self, VoteError},
vote_state::{VoteAuthorize, VoteInit, VoteState},
};
use std::sync::Arc;
pub trait VoteSubCommands {
fn vote_subcommands(self) -> Self;
@ -176,84 +174,91 @@ impl VoteSubCommands for App<'_, '_> {
.help("Display balance in lamports instead of SOL"),
),
)
.subcommand(
SubCommand::with_name("uptime")
.about("Show the uptime of a validator, based on epoch voting history")
.arg(
Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE ACCOUNT PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey_or_keypair)
.help("Vote account pubkey"),
)
.arg(
Arg::with_name("span")
.long("span")
.value_name("NUM OF EPOCHS")
.takes_value(true)
.help("Number of recent epochs to examine"),
)
.arg(
Arg::with_name("aggregate")
.long("aggregate")
.help("Aggregate uptime data across span"),
),
)
}
}
pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let vote_account = keypair_of(matches, "vote_account").unwrap();
pub fn parse_vote_create_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (vote_account, _) = signer_of(matches, "vote_account", wallet_manager)?;
let seed = matches.value_of("seed").map(|s| s.to_string());
let identity_pubkey = pubkey_of(matches, "identity_pubkey").unwrap();
let commission = value_t_or_exit!(matches, "commission", u8);
let authorized_voter = pubkey_of(matches, "authorized_voter");
let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer");
let payer_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![payer_provided, vote_account],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: vote_account.into(),
seed,
node_pubkey: identity_pubkey,
authorized_voter,
authorized_withdrawer,
commission,
},
require_keypair: true,
signers,
})
}
pub fn parse_vote_authorize(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
let authorized_voter_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![authorized_voter_provided],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey,
vote_authorize,
},
require_keypair: true,
signers,
})
}
pub fn parse_vote_update_validator(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let new_identity_pubkey = pubkey_of(matches, "new_identity_pubkey").unwrap();
let authorized_voter = keypair_of(matches, "authorized_voter").unwrap();
let (authorized_voter, _) = signer_of(matches, "authorized_voter", wallet_manager)?;
let payer_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![payer_provided, authorized_voter],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey,
new_identity_pubkey,
authorized_voter: authorized_voter.into(),
},
require_keypair: true,
signers,
})
}
@ -267,38 +272,20 @@ pub fn parse_vote_get_account_command(
pubkey: vote_account_pubkey,
use_lamports_unit,
},
require_keypair: false,
})
}
pub fn parse_vote_uptime_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let aggregate = matches.is_present("aggregate");
let span = if matches.is_present("span") {
Some(value_t_or_exit!(matches, "span", u64))
} else {
None
};
Ok(CliCommandInfo {
command: CliCommand::Uptime {
pubkey: vote_account_pubkey,
aggregate,
span,
},
require_keypair: false,
signers: vec![],
})
}
pub fn process_create_vote_account(
rpc_client: &RpcClient,
config: &CliConfig,
vote_account: &Keypair,
seed: &Option<String>,
identity_pubkey: &Pubkey,
authorized_voter: &Option<Pubkey>,
authorized_withdrawer: &Option<Pubkey>,
commission: u8,
) -> ProcessResult {
let vote_account = config.signers[1];
let vote_account_pubkey = vote_account.pubkey();
let vote_account_address = if let Some(seed) = seed {
create_address_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
@ -306,7 +293,7 @@ pub fn process_create_vote_account(
vote_account_pubkey
};
check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()),
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(&vote_account_address, "vote_account".to_string()),
)?;
@ -340,16 +327,16 @@ pub fn process_create_vote_account(
let ixs = if let Some(seed) = seed {
vote_instruction::create_account_with_seed(
&config.keypair.pubkey(), // from
&vote_account_address, // to
&vote_account_pubkey, // base
seed, // seed
&config.signers[0].pubkey(), // from
&vote_account_address, // to
&vote_account_pubkey, // base
seed, // seed
&vote_init,
required_balance,
)
} else {
vote_instruction::create_account(
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&vote_account_pubkey,
&vote_init,
required_balance,
@ -357,20 +344,16 @@ pub fn process_create_vote_account(
};
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let signers = if vote_account_pubkey != config.keypair.pubkey() {
vec![&config.keypair, vote_account] // both must sign if `from` and `to` differ
} else {
vec![&config.keypair] // when stake_account == config.keypair and there's a seed, we only need one signature
};
let mut tx = Transaction::new_signed_instructions(&signers, ixs, recent_blockhash);
let message = Message::new(ixs);
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &signers);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
@ -387,25 +370,22 @@ pub fn process_vote_authorize(
)?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = vec![vote_instruction::authorize(
vote_account_pubkey, // vote account to update
&config.keypair.pubkey(), // current authorized voter
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
vote_account_pubkey, // vote account to update
&config.signers[0].pubkey(), // current authorized voter
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)];
let mut tx = Transaction::new_signed_with_payer(
ixs,
Some(&config.keypair.pubkey()),
&[&config.keypair],
recent_blockhash,
);
let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.signers[0]]);
log_instruction_custom_error::<VoteError>(result)
}
@ -414,8 +394,8 @@ pub fn process_vote_update_validator(
config: &CliConfig,
vote_account_pubkey: &Pubkey,
new_identity_pubkey: &Pubkey,
authorized_voter: &Keypair,
) -> ProcessResult {
let authorized_voter = config.signers[1];
check_unique_pubkeys(
(vote_account_pubkey, "vote_account_pubkey".to_string()),
(new_identity_pubkey, "new_identity_pubkey".to_string()),
@ -427,19 +407,16 @@ pub fn process_vote_update_validator(
new_identity_pubkey,
)];
let mut tx = Transaction::new_signed_with_payer(
ixs,
Some(&config.keypair.pubkey()),
&[&config.keypair, authorized_voter],
recent_blockhash,
);
let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.signers);
log_instruction_custom_error::<VoteError>(result)
}
@ -476,25 +453,25 @@ pub fn process_show_vote_account(
let epoch_schedule = rpc_client.get_epoch_schedule()?;
println!(
"account balance: {}",
"Account Balance: {}",
build_balance_message(vote_account.lamports, use_lamports_unit, true)
);
println!("validator identity: {}", vote_state.node_pubkey);
println!("authorized voter: {}", vote_state.authorized_voter);
println!("Validator Identity: {}", vote_state.node_pubkey);
println!("Authorized Voter: {}", vote_state.authorized_voter);
println!(
"authorized withdrawer: {}",
"Authorized Withdrawer: {}",
vote_state.authorized_withdrawer
);
println!("credits: {}", vote_state.credits());
println!("commission: {}%", vote_state.commission);
println!("Credits: {}", vote_state.credits());
println!("Commission: {}%", vote_state.commission);
println!(
"root slot: {}",
"Root Slot: {}",
match vote_state.root_slot {
Some(slot) => slot.to_string(),
None => "~".to_string(),
}
);
println!("recent timestamp: {:?}", vote_state.last_timestamp);
println!("Recent Timestamp: {:?}", vote_state.last_timestamp);
if !vote_state.votes.is_empty() {
println!("recent votes:");
for vote in &vote_state.votes {
@ -504,7 +481,7 @@ pub fn process_show_vote_account(
);
}
println!("epoch voting history:");
println!("Epoch Voting History:");
for (epoch, credits, prev_credits) in vote_state.epoch_credits() {
let credits_earned = credits - prev_credits;
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
@ -517,65 +494,11 @@ pub fn process_show_vote_account(
Ok("".to_string())
}
pub fn process_uptime(
rpc_client: &RpcClient,
_config: &CliConfig,
vote_account_pubkey: &Pubkey,
aggregate: bool,
span: Option<u64>,
) -> ProcessResult {
let (_vote_account, vote_state) = get_vote_account(rpc_client, vote_account_pubkey)?;
let epoch_schedule = rpc_client.get_epoch_schedule()?;
println!("validator identity: {}", vote_state.node_pubkey);
println!("authorized voter: {}", vote_state.authorized_voter);
if !vote_state.votes.is_empty() {
println!("uptime:");
let epoch_credits: Vec<(u64, u64, u64)> = if let Some(x) = span {
vote_state
.epoch_credits()
.iter()
.rev()
.take(x as usize)
.cloned()
.collect()
} else {
vote_state.epoch_credits().iter().rev().cloned().collect()
};
if aggregate {
let (total_credits, total_slots, epochs) =
aggregate_epoch_credits(&epoch_credits, &epoch_schedule);
if total_slots > 0 {
let total_uptime = 100_f64 * total_credits as f64 / total_slots as f64;
println!("{:.2}% over {} epochs", total_uptime, epochs);
} else {
println!("Insufficient voting history available");
}
} else {
for (epoch, credits, prev_credits) in epoch_credits {
let credits_earned = credits - prev_credits;
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
let uptime = credits_earned as f64 / slots_in_epoch as f64;
println!("- epoch: {} {:.2}% uptime", epoch, uptime * 100_f64,);
}
}
if let Some(x) = span {
if x > vote_state.epoch_credits().len() as u64 {
println!("(span longer than available epochs)");
}
}
}
Ok("".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::{app, parse_command};
use solana_sdk::signature::write_keypair;
use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
@ -593,6 +516,10 @@ mod tests {
let pubkey2 = keypair2.pubkey();
let pubkey2_string = pubkey2.to_string();
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter",
@ -600,14 +527,14 @@ mod tests {
&pubkey2_string,
]);
assert_eq!(
parse_command(&test_authorize_voter).unwrap(),
parse_command(&test_authorize_voter, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter
},
require_keypair: true
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
@ -626,17 +553,19 @@ mod tests {
"10",
]);
assert_eq!(
parse_command(&test_create_vote_account).unwrap(),
parse_command(&test_create_vote_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None,
node_pubkey,
authorized_voter: None,
authorized_withdrawer: None,
commission: 10,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
}
);
@ -651,17 +580,19 @@ mod tests {
&node_pubkey_string,
]);
assert_eq!(
parse_command(&test_create_vote_account2).unwrap(),
parse_command(&test_create_vote_account2, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None,
node_pubkey,
authorized_voter: None,
authorized_withdrawer: None,
commission: 100,
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
}
);
@ -680,17 +611,19 @@ mod tests {
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account3).unwrap(),
parse_command(&test_create_vote_account3, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None,
node_pubkey,
authorized_voter: Some(authed),
authorized_withdrawer: None,
commission: 100
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
}
);
@ -707,17 +640,19 @@ mod tests {
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account4).unwrap(),
parse_command(&test_create_vote_account4, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: keypair.into(),
seed: None,
node_pubkey,
authorized_voter: None,
authorized_withdrawer: Some(authed),
commission: 100
},
require_keypair: true
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(keypair)
],
}
);
@ -729,38 +664,16 @@ mod tests {
&keypair_file,
]);
assert_eq!(
parse_command(&test_update_validator).unwrap(),
parse_command(&test_update_validator, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey,
new_identity_pubkey: pubkey2,
authorized_voter: solana_sdk::signature::read_keypair_file(&keypair_file)
.unwrap()
.into(),
},
require_keypair: true
}
);
// Test Uptime Subcommand
let pubkey = Pubkey::new_rand();
let matches = test_commands.clone().get_matches_from(vec![
"test",
"uptime",
&pubkey.to_string(),
"--span",
"4",
"--aggregate",
]);
assert_eq!(
parse_command(&matches).unwrap(),
CliCommandInfo {
command: CliCommand::Uptime {
pubkey,
aggregate: true,
span: Some(4)
},
require_keypair: false
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(read_keypair_file(&keypair_file).unwrap())
],
}
);
}

View File

@ -3,7 +3,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::new_validator_for_tests;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{bpf_loader, pubkey::Pubkey};
use solana_sdk::{bpf_loader, pubkey::Pubkey, signature::Keypair};
use std::{
fs::{remove_dir_all, File},
io::Read,
@ -38,13 +38,15 @@ fn test_cli_deploy_program() {
.unwrap();
let mut config = CliConfig::default();
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: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing
use_lamports_unit: true,
};
config.signers = vec![&keypair];
process_command(&config).unwrap();
config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string());

View File

@ -1,29 +1,17 @@
use solana_cli::cli::{
process_command, request_and_confirm_airdrop, CliCommand, CliConfig, SigningAuthority,
};
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::{read_keypair_file, write_keypair, Keypair, KeypairUtil},
signature::{keypair_from_seed, Keypair, Signer},
system_instruction::create_address_with_seed,
system_program,
};
use std::fs::remove_dir_all;
use std::sync::mpsc::channel;
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
#[cfg(test)]
use solana_core::validator::new_validator_for_tests;
use std::thread::sleep;
use std::time::Duration;
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| {
@ -46,26 +34,9 @@ fn test_nonce() {
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_nonce = CliConfig::default();
config_nonce.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
full_battery_tests(
&rpc_client,
&faucet_addr,
&mut config_payer,
&mut config_nonce,
&keypair_file,
None,
None,
);
full_battery_tests(&rpc_client, &faucet_addr, json_rpc_url, None, false);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
@ -79,25 +50,14 @@ fn test_nonce_with_seed() {
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_nonce = CliConfig::default();
config_nonce.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
full_battery_tests(
&rpc_client,
&faucet_addr,
&mut config_payer,
&mut config_nonce,
&keypair_file,
json_rpc_url,
Some(String::from("seed")),
None,
false,
);
server.close().unwrap();
@ -112,78 +72,73 @@ fn test_nonce_with_authority() {
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_nonce = CliConfig::default();
config_nonce.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
let nonce_authority = Keypair::new();
let (authority_keypair_file, mut tmp_file2) = make_tmp_file();
write_keypair(&nonce_authority, tmp_file2.as_file_mut()).unwrap();
full_battery_tests(
&rpc_client,
&faucet_addr,
&mut config_payer,
&mut config_nonce,
&nonce_keypair_file,
None,
Some(&authority_keypair_file),
);
full_battery_tests(&rpc_client, &faucet_addr, json_rpc_url, None, true);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option<SigningAuthority> {
keypair_file.map(|akf| read_keypair_file(&akf).unwrap().into())
}
fn full_battery_tests(
rpc_client: &RpcClient,
faucet_addr: &std::net::SocketAddr,
config_payer: &mut CliConfig,
config_nonce: &mut CliConfig,
nonce_keypair_file: &str,
json_rpc_url: String,
seed: Option<String>,
authority_keypair_file: Option<&str>,
use_nonce_authority: bool,
) {
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url = json_rpc_url.clone();
let payer = Keypair::new();
config_payer.signers = vec![&payer];
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.keypair.pubkey(),
&config_payer.signers[0].pubkey(),
2000,
)
.unwrap();
check_balance(2000, &rpc_client, &config_payer.keypair.pubkey());
check_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
let mut config_nonce = CliConfig::default();
config_nonce.json_rpc_url = json_rpc_url;
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
config_nonce.signers = vec![&nonce_keypair];
let nonce_account = if let Some(seed) = seed.as_ref() {
create_address_with_seed(&config_nonce.keypair.pubkey(), seed, &system_program::id())
.unwrap()
create_address_with_seed(
&config_nonce.signers[0].pubkey(),
seed,
&system_program::id(),
)
.unwrap()
} else {
config_nonce.keypair.pubkey()
nonce_keypair.pubkey()
};
let nonce_authority = Keypair::new();
let optional_authority = if use_nonce_authority {
Some(nonce_authority.pubkey())
} else {
None
};
// Create nonce account
config_payer.signers.push(&nonce_keypair);
config_payer.command = CliCommand::CreateNonceAccount {
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
nonce_account: 1,
seed,
nonce_authority: read_keypair_from_option(&authority_keypair_file)
.map(|na: SigningAuthority| na.pubkey()),
nonce_authority: optional_authority,
lamports: 1000,
};
process_command(&config_payer).unwrap();
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey());
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
check_balance(1000, &rpc_client, &nonce_account);
// Get nonce
config_payer.signers.pop();
config_payer.command = CliCommand::GetNonce(nonce_account);
let first_nonce_string = process_command(&config_payer).unwrap();
let first_nonce = first_nonce_string.parse::<Hash>().unwrap();
@ -195,14 +150,24 @@ fn full_battery_tests(
assert_eq!(first_nonce, second_nonce);
let mut authorized_signers: Vec<&dyn Signer> = vec![&payer];
let index = if use_nonce_authority {
authorized_signers.push(&nonce_authority);
1
} else {
0
};
// New nonce
config_payer.signers = authorized_signers.clone();
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file),
nonce_authority: index,
};
process_command(&config_payer).unwrap();
// Get nonce
config_payer.signers = vec![&payer];
config_payer.command = CliCommand::GetNonce(nonce_account);
let third_nonce_string = process_command(&config_payer).unwrap();
let third_nonce = third_nonce_string.parse::<Hash>().unwrap();
@ -211,14 +176,15 @@ fn full_battery_tests(
// Withdraw from nonce account
let payee_pubkey = Pubkey::new_rand();
config_payer.signers = authorized_signers;
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file),
nonce_authority: index,
destination_account_pubkey: payee_pubkey,
lamports: 100,
};
process_command(&config_payer).unwrap();
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey());
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
check_balance(900, &rpc_client, &nonce_account);
check_balance(100, &rpc_client, &payee_pubkey);
@ -231,48 +197,37 @@ fn full_battery_tests(
// Set new authority
let new_authority = Keypair::new();
let (new_authority_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&new_authority, tmp_file.as_file_mut()).unwrap();
config_payer.command = CliCommand::AuthorizeNonceAccount {
nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file),
new_authority: read_keypair_file(&new_authority_keypair_file)
.unwrap()
.pubkey(),
nonce_authority: index,
new_authority: new_authority.pubkey(),
};
process_command(&config_payer).unwrap();
// Old authority fails now
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: read_keypair_from_option(&authority_keypair_file),
nonce_authority: index,
};
process_command(&config_payer).unwrap_err();
// New authority can advance nonce
config_payer.signers = vec![&payer, &new_authority];
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: Some(
read_keypair_file(&new_authority_keypair_file)
.unwrap()
.into(),
),
nonce_authority: 1,
};
process_command(&config_payer).unwrap();
// New authority can withdraw from nonce account
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: Some(
read_keypair_file(&new_authority_keypair_file)
.unwrap()
.into(),
),
nonce_authority: 1,
destination_account_pubkey: payee_pubkey,
lamports: 100,
};
process_command(&config_payer).unwrap();
check_balance(1000, &rpc_client, &config_payer.keypair.pubkey());
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
check_balance(800, &rpc_client, &nonce_account);
check_balance(200, &rpc_client, &payee_pubkey);
}

View File

@ -1,31 +1,23 @@
use chrono::prelude::*;
use serde_json::Value;
use solana_cli::cli::{
process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand,
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
offline::{parse_sign_only_reply_string, BlockhashQuery},
};
use solana_client::rpc_client::RpcClient;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
account_utils::StateMut,
hash::Hash,
fee_calculator::FeeCalculator,
nonce_state::NonceState,
pubkey::Pubkey,
signature::{read_keypair_file, write_keypair, Keypair, KeypairUtil, Signature},
signature::{Keypair, Signer},
};
use std::fs::remove_dir_all;
use std::str::FromStr;
use std::sync::mpsc::channel;
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
#[cfg(test)]
use solana_core::validator::new_validator_for_tests;
use std::thread::sleep;
use std::time::Duration;
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| {
@ -50,32 +42,36 @@ fn test_cli_timestamp_tx() {
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::default();
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::default();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!(
config_payer.keypair.pubkey(),
config_witness.keypair.pubkey()
config_payer.signers[0].pubkey(),
config_witness.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.keypair.pubkey(),
&config_payer.signers[0].pubkey(),
50,
)
.unwrap();
check_balance(50, &rpc_client, &config_payer.keypair.pubkey());
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_witness.keypair.pubkey(),
&config_witness.signers[0].pubkey(),
1,
)
.unwrap();
@ -87,7 +83,7 @@ fn test_cli_timestamp_tx() {
lamports: 10,
to: bob_pubkey,
timestamp: Some(dt),
timestamp_pubkey: Some(config_witness.keypair.pubkey()),
timestamp_pubkey: Some(config_witness.signers[0].pubkey()),
..PayCommand::default()
});
let sig_response = process_command(&config_payer);
@ -99,7 +95,7 @@ fn test_cli_timestamp_tx() {
.expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec);
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(10, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -107,7 +103,7 @@ fn test_cli_timestamp_tx() {
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
process_command(&config_witness).unwrap();
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(0, &rpc_client, &process_id); // contract balance
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
@ -125,30 +121,34 @@ fn test_cli_witness_tx() {
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::default();
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::default();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!(
config_payer.keypair.pubkey(),
config_witness.keypair.pubkey()
config_payer.signers[0].pubkey(),
config_witness.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.keypair.pubkey(),
&config_payer.signers[0].pubkey(),
50,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_witness.keypair.pubkey(),
&config_witness.signers[0].pubkey(),
1,
)
.unwrap();
@ -157,7 +157,7 @@ fn test_cli_witness_tx() {
config_payer.command = CliCommand::Pay(PayCommand {
lamports: 10,
to: bob_pubkey,
witnesses: Some(vec![config_witness.keypair.pubkey()]),
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
..PayCommand::default()
});
let sig_response = process_command(&config_payer);
@ -169,7 +169,7 @@ fn test_cli_witness_tx() {
.expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec);
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(10, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -177,7 +177,7 @@ fn test_cli_witness_tx() {
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
process_command(&config_witness).unwrap();
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(0, &rpc_client, &process_id); // contract balance
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
@ -195,23 +195,27 @@ fn test_cli_cancel_tx() {
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::default();
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::default();
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
config_witness.signers = vec![&default_signer1];
assert_ne!(
config_payer.keypair.pubkey(),
config_witness.keypair.pubkey()
config_payer.signers[0].pubkey(),
config_witness.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_payer.keypair.pubkey(),
&config_payer.signers[0].pubkey(),
50,
)
.unwrap();
@ -220,7 +224,7 @@ fn test_cli_cancel_tx() {
config_payer.command = CliCommand::Pay(PayCommand {
lamports: 10,
to: bob_pubkey,
witnesses: Some(vec![config_witness.keypair.pubkey()]),
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
cancelable: true,
..PayCommand::default()
});
@ -233,7 +237,7 @@ fn test_cli_cancel_tx() {
.expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec);
check_balance(40, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(10, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -241,7 +245,7 @@ fn test_cli_cancel_tx() {
config_payer.command = CliCommand::Cancel(process_id);
process_command(&config_payer).unwrap();
check_balance(50, &rpc_client, &config_payer.keypair.pubkey()); // config_payer balance
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
check_balance(0, &rpc_client, &process_id); // contract balance
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
@ -259,22 +263,26 @@ fn test_offline_pay_tx() {
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::default();
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::default();
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.keypair.pubkey(),
config_online.keypair.pubkey()
config_offline.signers[0].pubkey(),
config_online.signers[0].pubkey()
);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_offline.keypair.pubkey(),
&config_offline.signers[0].pubkey(),
50,
)
.unwrap();
@ -282,49 +290,42 @@ fn test_offline_pay_tx() {
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config_online.keypair.pubkey(),
&config_online.signers[0].pubkey(),
50,
)
.unwrap();
check_balance(50, &rpc_client, &config_offline.keypair.pubkey());
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
config_offline.command = CliCommand::Pay(PayCommand {
lamports: 10,
to: bob_pubkey,
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
sign_only: true,
..PayCommand::default()
});
let sig_response = process_command(&config_offline).unwrap();
check_balance(50, &rpc_client, &config_offline.keypair.pubkey());
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
check_balance(0, &rpc_client, &bob_pubkey);
let object: Value = serde_json::from_str(&sig_response).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let signer_strings = object.get("signers").unwrap().as_array().unwrap();
let signers: Vec<_> = signer_strings
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner =
presigner_from_pubkey_sigs(&config_offline.signers[0].pubkey(), &signers).unwrap();
let online_pubkey = config_online.signers[0].pubkey();
config_online.signers = vec![&offline_presigner];
config_online.command = CliCommand::Pay(PayCommand {
lamports: 10,
to: bob_pubkey,
signers: Some(signers),
blockhash: Some(blockhash_str.parse::<Hash>().unwrap()),
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
..PayCommand::default()
});
process_command(&config_online).unwrap();
check_balance(40, &rpc_client, &config_offline.keypair.pubkey());
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
check_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
check_balance(50, &rpc_client, &online_pubkey);
check_balance(10, &rpc_client, &bob_pubkey);
server.close().unwrap();
@ -341,9 +342,11 @@ fn test_nonced_pay_tx() {
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let default_signer = Keypair::new();
let mut config = CliConfig::default();
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())
@ -352,29 +355,28 @@ fn test_nonced_pay_tx() {
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
50 + minimum_nonce_balance,
)
.unwrap();
check_balance(
50 + minimum_nonce_balance,
&rpc_client,
&config.keypair.pubkey(),
&config.signers[0].pubkey(),
);
// Create nonce account
let nonce_account = Keypair::new();
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
config.command = CliCommand::CreateNonceAccount {
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
nonce_account: 1,
seed: None,
nonce_authority: Some(config.keypair.pubkey()),
nonce_authority: Some(config.signers[0].pubkey()),
lamports: minimum_nonce_balance,
};
config.signers.push(&nonce_account);
process_command(&config).unwrap();
check_balance(50, &rpc_client, &config.keypair.pubkey());
check_balance(50, &rpc_client, &config.signers[0].pubkey());
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash
@ -386,16 +388,17 @@ fn test_nonced_pay_tx() {
};
let bob_pubkey = Pubkey::new_rand();
config.signers = vec![&default_signer];
config.command = CliCommand::Pay(PayCommand {
lamports: 10,
to: bob_pubkey,
blockhash: Some(nonce_hash),
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
nonce_account: Some(nonce_account.pubkey()),
..PayCommand::default()
});
process_command(&config).expect("failed to process pay command");
check_balance(40, &rpc_client, &config.keypair.pubkey());
check_balance(40, &rpc_client, &config.signers[0].pubkey());
check_balance(10, &rpc_client, &bob_pubkey);
// Verify that nonce has been used

View File

@ -2,9 +2,8 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::new_validator_for_tests;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::signature::KeypairUtil;
use std::fs::remove_dir_all;
use std::sync::mpsc::channel;
use solana_sdk::signature::Keypair;
use std::{fs::remove_dir_all, sync::mpsc::channel};
#[test]
fn test_cli_request_airdrop() {
@ -18,9 +17,11 @@ fn test_cli_request_airdrop() {
bob_config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 50,
use_lamports_unit: true,
};
let keypair = Keypair::new();
bob_config.signers = vec![&keypair];
let sig_response = process_command(&bob_config);
sig_response.unwrap();
@ -28,7 +29,7 @@ fn test_cli_request_airdrop() {
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let balance = rpc_client
.retry_get_balance(&bob_config.keypair.pubkey(), 1)
.retry_get_balance(&bob_config.signers[0].pubkey(), 1)
.unwrap()
.unwrap();
assert_eq!(balance, 50);

File diff suppressed because it is too large Load Diff

209
cli/tests/transfer.rs Normal file
View File

@ -0,0 +1,209 @@
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::{parse_sign_only_reply_string, BlockhashQuery},
};
use solana_client::rpc_client::RpcClient;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
account_utils::StateMut,
fee_calculator::FeeCalculator,
nonce_state::NonceState,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer},
};
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
#[cfg(test)]
use solana_core::validator::new_validator_for_tests_ex;
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| {
let balance = client.retry_get_balance(pubkey, 1).unwrap().unwrap();
if balance == expected_balance {
return;
}
if tries == 4 {
assert_eq!(balance, expected_balance);
}
sleep(Duration::from_millis(500));
});
}
#[test]
fn test_transfer() {
let (server, leader_data, mint_keypair, ledger_path, _) = new_validator_for_tests_ex(1, 42_000);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, 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 = CliConfig::default();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.signers = vec![&default_signer];
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000).unwrap();
check_balance(50_000, &rpc_client, &sender_pubkey);
check_balance(0, &rpc_client, &recipient_pubkey);
// Plain ole transfer
config.command = CliCommand::Transfer {
lamports: 10,
to: recipient_pubkey,
from: 0,
sign_only: false,
blockhash_query: BlockhashQuery::All,
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
process_command(&config).unwrap();
check_balance(49_989, &rpc_client, &sender_pubkey);
check_balance(10, &rpc_client, &recipient_pubkey);
let mut offline = CliConfig::default();
offline.json_rpc_url = String::default();
offline.signers = vec![&default_offline_signer];
// Verify we cannot contact the cluster
offline.command = CliCommand::ClusterVersion;
process_command(&offline).unwrap_err();
let offline_pubkey = offline.signers[0].pubkey();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50).unwrap();
check_balance(50, &rpc_client, &offline_pubkey);
// Offline transfer
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
offline.command = CliCommand::Transfer {
lamports: 10,
to: recipient_pubkey,
from: 0,
sign_only: true,
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
let sign_only_reply = process_command(&offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::Transfer {
lamports: 10,
to: recipient_pubkey,
from: 0,
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
process_command(&config).unwrap();
check_balance(39, &rpc_client, &offline_pubkey);
check_balance(20, &rpc_client, &recipient_pubkey);
// Create nonce account
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
let minimum_nonce_balance = rpc_client
.get_minimum_balance_for_rent_exemption(NonceState::size())
.unwrap();
config.signers = vec![&default_signer, &nonce_account];
config.command = CliCommand::CreateNonceAccount {
nonce_account: 1,
seed: None,
nonce_authority: None,
lamports: minimum_nonce_balance,
};
process_command(&config).unwrap();
check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
let nonce_state: NonceState = account.state().unwrap();
let nonce_hash = match nonce_state {
NonceState::Initialized(_meta, hash) => hash,
_ => panic!("Nonce is not initialized"),
};
// Nonced transfer
config.signers = vec![&default_signer];
config.command = CliCommand::Transfer {
lamports: 10,
to: recipient_pubkey,
from: 0,
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
fee_payer: 0,
};
process_command(&config).unwrap();
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
check_balance(30, &rpc_client, &recipient_pubkey);
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
let nonce_state: NonceState = account.state().unwrap();
let new_nonce_hash = match nonce_state {
NonceState::Initialized(_meta, hash) => hash,
_ => panic!("Nonce is not initialized"),
};
assert_ne!(nonce_hash, new_nonce_hash);
// Assign nonce authority to offline
config.signers = vec![&default_signer];
config.command = CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account.pubkey(),
nonce_authority: 0,
new_authority: offline_pubkey,
};
process_command(&config).unwrap();
check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
let nonce_state: NonceState = account.state().unwrap();
let nonce_hash = match nonce_state {
NonceState::Initialized(_meta, hash) => hash,
_ => panic!("Nonce is not initialized"),
};
// Offline, nonced transfer
offline.signers = vec![&default_offline_signer];
offline.command = CliCommand::Transfer {
lamports: 10,
to: recipient_pubkey,
from: 0,
sign_only: true,
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
fee_payer: 0,
};
let sign_only_reply = process_command(&offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::Transfer {
lamports: 10,
to: recipient_pubkey,
from: 0,
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
fee_payer: 0,
};
process_command(&config).unwrap();
check_balance(28, &rpc_client, &offline_pubkey);
check_balance(40, &rpc_client, &recipient_pubkey);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}

View File

@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "0.23.0"
version = "0.23.9"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -19,11 +19,12 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.44"
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
thiserror = "1.0"
[dev-dependencies]
assert_matches = "1.3.0"
jsonrpc-core = "14.0.5"
jsonrpc-http-server = "14.0.5"
solana-logger = { path = "../logger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.9" }

View File

@ -1,14 +1,16 @@
use crate::rpc_request;
use solana_sdk::transaction::TransactionError;
use solana_sdk::{signature::SignerError, transaction::TransactionError};
use std::{fmt, io};
use thiserror::Error;
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum ClientError {
Io(io::Error),
Reqwest(reqwest::Error),
RpcError(rpc_request::RpcError),
SerdeJson(serde_json::error::Error),
TransactionError(TransactionError),
Io(#[from] io::Error),
Reqwest(#[from] reqwest::Error),
RpcError(#[from] rpc_request::RpcError),
SerdeJson(#[from] serde_json::error::Error),
SigningError(#[from] SignerError),
TransactionError(#[from] TransactionError),
}
impl fmt::Display for ClientError {
@ -16,35 +18,3 @@ impl fmt::Display for ClientError {
write!(f, "solana client error")
}
}
impl std::error::Error for ClientError {}
impl From<io::Error> for ClientError {
fn from(err: io::Error) -> ClientError {
ClientError::Io(err)
}
}
impl From<reqwest::Error> for ClientError {
fn from(err: reqwest::Error) -> ClientError {
ClientError::Reqwest(err)
}
}
impl From<rpc_request::RpcError> for ClientError {
fn from(err: rpc_request::RpcError) -> ClientError {
ClientError::RpcError(err)
}
}
impl From<serde_json::error::Error> for ClientError {
fn from(err: serde_json::error::Error) -> ClientError {
ClientError::SerdeJson(err)
}
}
impl From<TransactionError> for ClientError {
fn from(err: TransactionError) -> ClientError {
ClientError::TransactionError(err)
}
}

View File

@ -60,13 +60,10 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
Value::Null
}
}
RpcRequest::GetBalance => {
let n = if self.url == "airdrop" { 0 } else { 50 };
serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 },
value: Value::Number(Number::from(n)),
})?
}
RpcRequest::GetBalance => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 },
value: Value::Number(Number::from(50)),
})?,
RpcRequest::GetRecentBlockhash => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 },
value: (

View File

@ -22,7 +22,8 @@ use solana_sdk::{
hash::Hash,
inflation::Inflation,
pubkey::Pubkey,
signature::{KeypairUtil, Signature},
signature::Signature,
signers::Signers,
transaction::{self, Transaction, TransactionError},
};
use std::{
@ -177,7 +178,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetVoteAccounts parse failure: {}", err),
format!("GetVoteAccounts parse failure: {:?}", err),
)
})
}
@ -196,7 +197,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetClusterNodes parse failure: {}", err),
format!("GetClusterNodes parse failure: {:?}", err),
)
})
}
@ -215,7 +216,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetConfirmedBlock parse failure: {}", err),
format!("GetConfirmedBlock parse failure: {:?}", err),
)
})
}
@ -242,7 +243,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetConfirmedBlocks parse failure: {}", err),
format!("GetConfirmedBlocks parse failure: {:?}", err),
)
})
}
@ -293,7 +294,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetEpochInfo parse failure: {}", err),
format!("GetEpochInfo parse failure: {:?}", err),
)
})
}
@ -324,7 +325,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetLeaderSchedule failure: {}", err),
format!("GetLeaderSchedule failure: {:?}", err),
)
})
}
@ -343,7 +344,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetEpochSchedule parse failure: {}", err),
format!("GetEpochSchedule parse failure: {:?}", err),
)
})
}
@ -381,7 +382,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetVersion parse failure: {}", err),
format!("GetVersion parse failure: {:?}", err),
)
})
}
@ -400,15 +401,15 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("MinimumLedgerSlot parse failure: {}", err),
format!("MinimumLedgerSlot parse failure: {:?}", err),
)
})
}
pub fn send_and_confirm_transaction<T: KeypairUtil>(
pub fn send_and_confirm_transaction<T: Signers>(
&self,
transaction: &mut Transaction,
signer_keys: &[&T],
signer_keys: &T,
) -> Result<String, ClientError> {
let mut send_retries = 20;
loop {
@ -456,10 +457,10 @@ impl RpcClient {
}
}
pub fn send_and_confirm_transactions<T: KeypairUtil>(
pub fn send_and_confirm_transactions<T: Signers>(
&self,
mut transactions: Vec<Transaction>,
signer_keys: &[&T],
signer_keys: &T,
) -> Result<(), Box<dyn error::Error>> {
let mut send_retries = 5;
loop {
@ -516,24 +517,22 @@ impl RpcClient {
// Re-sign any failed transactions with a new blockhash and retry
let (blockhash, _fee_calculator) =
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
transactions = transactions_signatures
.into_iter()
.map(|(mut transaction, _)| {
transaction.sign(signer_keys, blockhash);
transaction
})
.collect();
transactions = vec![];
for (mut transaction, _) in transactions_signatures.into_iter() {
transaction.try_sign(signer_keys, blockhash)?;
transactions.push(transaction);
}
}
}
pub fn resign_transaction<T: KeypairUtil>(
pub fn resign_transaction<T: Signers>(
&self,
tx: &mut Transaction,
signer_keys: &[&T],
signer_keys: &T,
) -> Result<(), ClientError> {
let (blockhash, _fee_calculator) =
self.get_new_blockhash(&tx.message().recent_blockhash)?;
tx.sign(signer_keys, blockhash);
tx.try_sign(signer_keys, blockhash)?;
Ok(())
}
@ -612,7 +611,7 @@ impl RpcClient {
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("AccountNotFound: pubkey={}: {}", pubkey, err),
format!("AccountNotFound: pubkey={}: {:?}", pubkey, err),
)
})?
}
@ -698,7 +697,7 @@ impl RpcClient {
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("AccountNotFound: pubkey={}: {}", pubkey, err),
format!("AccountNotFound: pubkey={}: {:?}", pubkey, err),
)
})?;
@ -749,7 +748,7 @@ impl RpcClient {
serde_json::from_value(response).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("GetTransactionCount parse failure: {}", err),
format!("GetTransactionCount parse failure: {:?}", err),
)
})
}
@ -1129,9 +1128,7 @@ mod tests {
use serde_json::Number;
use solana_logger;
use solana_sdk::{
instruction::InstructionError,
signature::{Keypair, KeypairUtil},
system_transaction,
instruction::InstructionError, signature::Keypair, system_transaction,
transaction::TransactionError,
};
use std::{sync::mpsc::channel, thread};

View File

@ -15,10 +15,7 @@ pub struct RpcClientRequest {
impl RpcClientRequest {
pub fn new(url: String) -> Self {
Self {
client: reqwest::blocking::Client::new(),
url,
}
Self::new_with_timeout(url, Duration::from_secs(20))
}
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {

View File

@ -32,6 +32,14 @@ pub struct RpcBlockCommitment<T> {
pub total_stake: u64,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct RpcReward {
pub pubkey: String,
pub lamports: i64,
}
pub type RpcRewards = Vec<RpcReward>;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcConfirmedBlock {
@ -39,6 +47,7 @@ pub struct RpcConfirmedBlock {
pub blockhash: String,
pub parent_slot: Slot,
pub transactions: Vec<RpcTransactionWithStatusMeta>,
pub rewards: RpcRewards,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]

View File

@ -17,7 +17,8 @@ use solana_sdk::{
message::Message,
packet::PACKET_DATA_SIZE,
pubkey::Pubkey,
signature::{Keypair, KeypairUtil, Signature},
signature::{Keypair, Signature, Signer},
signers::Signers,
system_instruction,
timing::duration_as_ms,
transaction::{self, Transaction},
@ -202,9 +203,9 @@ impl ThinClient {
}
/// Retry sending a signed Transaction to the server for processing
pub fn send_and_confirm_transaction(
pub fn send_and_confirm_transaction<T: Signers>(
&self,
keypairs: &[&Keypair],
keypairs: &T,
transaction: &mut Transaction,
tries: usize,
pending_confirmations: usize,
@ -351,9 +352,13 @@ impl Client for ThinClient {
}
impl SyncClient for ThinClient {
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> TransportResult<Signature> {
fn send_message<T: Signers>(
&self,
keypairs: &T,
message: Message,
) -> TransportResult<Signature> {
let (blockhash, _fee_calculator) = self.get_recent_blockhash()?;
let mut transaction = Transaction::new(&keypairs, message, blockhash);
let mut transaction = Transaction::new(keypairs, message, blockhash);
let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?;
Ok(signature)
}
@ -561,13 +566,13 @@ impl AsyncClient for ThinClient {
.send_to(&buf[..], &self.tpu_addr())?;
Ok(transaction.signatures[0])
}
fn async_send_message(
fn async_send_message<T: Signers>(
&self,
keypairs: &[&Keypair],
keypairs: &T,
message: Message,
recent_blockhash: Hash,
) -> io::Result<Signature> {
let transaction = Transaction::new(&keypairs, message, recent_blockhash);
let transaction = Transaction::new(keypairs, message, recent_blockhash);
self.async_send_transaction(transaction)
}
fn async_send_instruction(

View File

@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "0.23.0"
version = "0.23.9"
documentation = "https://docs.rs/solana"
homepage = "https://solana.com/"
readme = "../README.md"
@ -18,6 +18,7 @@ bincode = "1.2.1"
bs58 = "0.3.0"
byteorder = "1.3.2"
chrono = { version = "0.4.10", features = ["serde"] }
compression = "0.1.5"
core_affinity = "0.5.10"
crc = { version = "1.8.1", optional = true }
crossbeam-channel = "0.3"
@ -37,29 +38,30 @@ num-traits = "0.2"
rand = "0.6.5"
rand_chacha = "0.1.1"
rayon = "1.2.0"
regex = "1.3.4"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.44"
solana-budget-program = { path = "../programs/budget", version = "0.23.0" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.0" }
solana-client = { path = "../client", version = "0.23.0" }
solana-faucet = { path = "../faucet", version = "0.23.0" }
solana-budget-program = { path = "../programs/budget", version = "0.23.9" }
solana-clap-utils = { path = "../clap-utils", version = "0.23.9" }
solana-client = { path = "../client", version = "0.23.9" }
solana-faucet = { path = "../faucet", version = "0.23.9" }
ed25519-dalek = "=1.0.0-pre.1"
solana-ledger = { path = "../ledger", version = "0.23.0" }
solana-logger = { path = "../logger", version = "0.23.0" }
solana-merkle-tree = { path = "../merkle-tree", version = "0.23.0" }
solana-metrics = { path = "../metrics", version = "0.23.0" }
solana-measure = { path = "../measure", version = "0.23.0" }
solana-net-utils = { path = "../net-utils", version = "0.23.0" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "0.23.0" }
solana-perf = { path = "../perf", version = "0.23.0" }
solana-runtime = { path = "../runtime", version = "0.23.0" }
solana-sdk = { path = "../sdk", version = "0.23.0" }
solana-stake-program = { path = "../programs/stake", version = "0.23.0" }
solana-storage-program = { path = "../programs/storage", version = "0.23.0" }
solana-vote-program = { path = "../programs/vote", version = "0.23.0" }
solana-vote-signer = { path = "../vote-signer", version = "0.23.0" }
solana-sys-tuner = { path = "../sys-tuner", version = "0.23.0" }
solana-ledger = { path = "../ledger", version = "0.23.9" }
solana-logger = { path = "../logger", version = "0.23.9" }
solana-merkle-tree = { path = "../merkle-tree", version = "0.23.9" }
solana-metrics = { path = "../metrics", version = "0.23.9" }
solana-measure = { path = "../measure", version = "0.23.9" }
solana-net-utils = { path = "../net-utils", version = "0.23.9" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "0.23.9" }
solana-perf = { path = "../perf", version = "0.23.9" }
solana-runtime = { path = "../runtime", version = "0.23.9" }
solana-sdk = { path = "../sdk", version = "0.23.9" }
solana-stake-program = { path = "../programs/stake", version = "0.23.9" }
solana-storage-program = { path = "../programs/storage", version = "0.23.9" }
solana-vote-program = { path = "../programs/vote", version = "0.23.9" }
solana-vote-signer = { path = "../vote-signer", version = "0.23.9" }
solana-sys-tuner = { path = "../sys-tuner", version = "0.23.9" }
symlink = "0.1.0"
sys-info = "0.5.8"
tempfile = "3.1.0"
@ -69,8 +71,9 @@ tokio-codec = "0.1"
tokio-fs = "0.1"
tokio-io = "0.1"
untrusted = "0.7.0"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "0.23.0" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "0.23.9" }
reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] }
trees = "0.2.1"
[dev-dependencies]
matches = "0.1.6"

View File

@ -21,8 +21,8 @@ use solana_sdk::genesis_config::GenesisConfig;
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use solana_sdk::signature::KeypairUtil;
use solana_sdk::signature::Signature;
use solana_sdk::signature::Signer;
use solana_sdk::system_instruction;
use solana_sdk::system_transaction;
use solana_sdk::timing::{duration_as_us, timestamp};

View File

@ -3,7 +3,7 @@ extern crate test;
use solana_ledger::entry::{next_entry_mut, Entry, EntrySlice};
use solana_sdk::hash::{hash, Hash};
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use test::Bencher;

View File

@ -9,7 +9,7 @@ use solana_ledger::shred::{
};
use solana_perf::test_tx;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use std::sync::Arc;
use test::Bencher;

View File

@ -11,7 +11,7 @@ use solana_core::sigverify::TransactionSigVerifier;
use solana_core::sigverify_stage::SigVerifyStage;
use solana_perf::test_tx::test_tx;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use solana_sdk::timing::duration_as_ms;
use std::sync::mpsc::channel;

View File

@ -404,7 +404,7 @@ impl BankingStage {
if unprocessed_packets.is_empty() {
continue;
}
let num = unprocessed_packets
let num: usize = unprocessed_packets
.iter()
.map(|(_, unprocessed)| unprocessed.len())
.sum();
@ -1029,7 +1029,7 @@ mod tests {
use solana_runtime::bank::HashAgeKind;
use solana_sdk::{
instruction::InstructionError,
signature::{Keypair, KeypairUtil},
signature::{Keypair, Signer},
system_transaction,
transaction::TransactionError,
};

View File

@ -180,7 +180,7 @@ mod test {
use chrono::{DateTime, FixedOffset};
use serde_json::Value;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use std::collections::HashSet;
use std::path::PathBuf;

View File

@ -58,31 +58,35 @@ impl BlockstreamService {
let timeout = Duration::new(1, 0);
let (slot, slot_leader) = slot_full_receiver.recv_timeout(timeout)?;
let entries = blockstore.get_slot_entries(slot, 0, None).unwrap();
let blockstore_meta = blockstore.meta(slot).unwrap().unwrap();
let _parent_slot = if slot == 0 {
None
} else {
Some(blockstore_meta.parent_slot)
};
let ticks_per_slot = entries.iter().filter(|entry| entry.is_tick()).count() as u64;
let mut tick_height = ticks_per_slot * slot;
// Slot might not exist due to LedgerCleanupService, check first
let blockstore_meta = blockstore.meta(slot).unwrap();
if let Some(blockstore_meta) = blockstore_meta {
// Return error to main loop. Thread won't exit, will just log the error
let entries = blockstore.get_slot_entries(slot, 0, None)?;
let _parent_slot = if slot == 0 {
None
} else {
Some(blockstore_meta.parent_slot)
};
let ticks_per_slot = entries.iter().filter(|entry| entry.is_tick()).count() as u64;
let mut tick_height = ticks_per_slot * slot;
for (i, entry) in entries.iter().enumerate() {
if entry.is_tick() {
tick_height += 1;
}
blockstream
.emit_entry_event(slot, tick_height, &slot_leader, &entry)
.unwrap_or_else(|e| {
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
});
if i == entries.len() - 1 {
for (i, entry) in entries.iter().enumerate() {
if entry.is_tick() {
tick_height += 1;
}
blockstream
.emit_block_event(slot, tick_height, &slot_leader, entry.hash)
.emit_entry_event(slot, tick_height, &slot_leader, &entry)
.unwrap_or_else(|e| {
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
});
if i == entries.len() - 1 {
blockstream
.emit_block_event(slot, tick_height, &slot_leader, entry.hash)
.unwrap_or_else(|e| {
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
});
}
}
}
Ok(())
@ -103,7 +107,7 @@ mod test {
use solana_ledger::create_new_tmp_ledger;
use solana_ledger::entry::{create_ticks, Entry};
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use std::path::PathBuf;
use std::sync::mpsc::channel;

View File

@ -259,7 +259,7 @@ mod test {
use solana_runtime::bank::Bank;
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Keypair, Signer};
use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::mpsc::channel;

View File

@ -82,7 +82,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
// Broadcast data
let all_shred_bufs: Vec<Vec<u8>> = shreds.to_vec().into_iter().map(|s| s.payload).collect();
cluster_info
.read()
.write()
.unwrap()
.broadcast_shreds(sock, all_shred_bufs, &all_seeds, stakes)?;
Ok(())

View File

@ -264,7 +264,7 @@ impl StandardBroadcastRun {
trace!("Broadcasting {:?} shreds", shred_bufs.len());
cluster_info
.read()
.write()
.unwrap()
.broadcast_shreds(sock, shred_bufs, &seeds, stakes)?;
@ -362,7 +362,7 @@ mod test {
use solana_sdk::{
clock::Slot,
genesis_config::GenesisConfig,
signature::{Keypair, KeypairUtil},
signature::{Keypair, Signer},
};
use std::sync::{Arc, RwLock};
use std::time::Duration;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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