Compare commits

..

111 Commits

Author SHA1 Message Date
mergify[bot]
4b5a05bf38 limits number of crds values associated with a pubkey (bp #14467) (#14490)
* limits number of crds values associated with a pubkey (#14467)

(cherry picked from commit 766195dded)

* updates smallvec

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-01-08 21:52:40 +00:00
mergify[bot]
7dd7141307 Suppress cargo audit failure for difference crate (bp #14488) (#14493)
* Suppress cargo audit failure for `difference` crate, there's no newer crate to upgrade to yet

(cherry picked from commit 3eaa826ad9)

* Bump smallvec version

(cherry picked from commit 21a0a83543)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-01-08 21:52:28 +00:00
mergify[bot]
e5175c843d Add buffer authority to upgradeable loader (#14482) (#14485)
(cherry picked from commit 58487c6360)

Co-authored-by: Jack May <jack@solana.com>
2021-01-08 18:54:11 +00:00
mergify[bot]
d5ff64b0d7 docs: Validator tuning improvements (bp #14478) (#14480)
* docs: wrap lines

(cherry picked from commit 140642ea21)

* docs: Prefer `dd` to `fallocate` when creating swap file

(cherry picked from commit c035f2a745)

* docs: Add RUST_LOG explainer

(cherry picked from commit 30038a8849)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-01-07 19:41:45 +00:00
mergify[bot]
0fbdc7e152 Enable program upgrades via CPI (#14449) (#14469)
(cherry picked from commit 5eacc5d08d)

Co-authored-by: Jack May <jack@solana.com>
2021-01-06 23:45:10 +00:00
Tyera Eulberg
49aca9ecd8 Add fixed tick rate adjustment (#14447) (#14464)
Co-authored-by: sakridge <sakridge@gmail.com>
2021-01-06 21:44:06 +00:00
mergify[bot]
fcc147b4f2 Gate cpi program account passing (#14443) (#14446)
(cherry picked from commit a8b5a32b50)

Co-authored-by: Jack May <jack@solana.com>
2021-01-06 19:20:49 +00:00
mergify[bot]
c455d1b1c5 Enable program-id account index for supply calculations (#14444) (#14456)
* Enable program-id account index for supply calculations

* Fixup comments

(cherry picked from commit ce1766d798)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-01-06 04:04:44 +00:00
mergify[bot]
e9b29fc697 Bump serum-dex pegged commit (#14448) (#14454)
(cherry picked from commit d2b0fd973f)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-01-05 19:17:03 -07:00
Ryo Onodera
fdea6fad26 Save 7G mem on mainnet fixing AccIndex overalloc. (#14435)
(cherry picked from commit c9df6134fa)
2021-01-05 17:55:44 -08:00
mergify[bot]
a4bc31341a Lower recycle store count (#14429) (#14442)
Too many stores can cause swap usage which
is detrimental to account store times.

(cherry picked from commit 53d65009a0)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-01-05 21:39:31 +00:00
mergify[bot]
4af797c0a2 Introduce rpc url monikers for cli (#14409) (#14433)
* Introduce rpc url monikers for cli

* Use https:// and support initials as well

(cherry picked from commit 54a5876c48)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-01-05 12:16:11 +00:00
mergify[bot]
a1e06df4a8 Add validator --account-index docs (#14418) (#14428)
(cherry picked from commit efd9b769fc)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-01-05 03:06:15 +00:00
mergify[bot]
1f2480fd9f Fix pre-merge old name in the docs (#14425) (#14427)
(cherry picked from commit 974eb6e1ef)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-01-05 02:55:02 +00:00
mergify[bot]
8587bd0d69 Improve solana catchup (#14313) (#14424)
* Improve solana catchup

* Overidable port, retry, args error clean up

* print cleanup

* Reduce diff

* Tweak warns a bit

(cherry picked from commit aa4da339ff)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-01-05 02:34:53 +00:00
mergify[bot]
0063a58e95 Upgradeable programs needs program account's address as program id (#14417) (#14420)
(cherry picked from commit 0619805806)

Co-authored-by: Jack May <jack@solana.com>
2021-01-04 23:00:36 +00:00
mergify[bot]
9aeb3bc5d6 docs: Use "msg!" instead of "info!" (#14411) (#14416)
* docs: Use "msg!" instead of "info!"

* Update docs/src/developing/deployed-programs/developing-rust.md

Co-authored-by: Michael Vines <mvines@gmail.com>

* Fix typo / format

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

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2021-01-04 20:02:09 +00:00
Michael Vines
97665b977e Bump version to v1.5.2 2021-01-04 06:44:52 +00:00
Michael Vines
c45ed29cf4 Use max commitment when fetching epoch info for block production
(cherry picked from commit 2724f37d0e)
2021-01-03 21:05:13 -08:00
mergify[bot]
635afbabff snapshot_utils: Don't bother restoring snapshots, they're never used (bp #14392) (#14396)
* Remove dead code

(cherry picked from commit b6dcdb90e8)

* Don't bother restoring snapshots, they're never used

(cherry picked from commit db6ee289c9)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-01-03 05:20:26 +00:00
mergify[bot]
98afdad1dd Tune rewards output (#14395)
(cherry picked from commit 560ed90168)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-01-03 02:39:35 +00:00
mergify[bot]
c085b94b43 docs: Update tmpfs partition guidance to include swap (bp #14387) (#14397)
* Update tmpfs partition guidance to include swap

(cherry picked from commit 68a84cf581)

* Update docs/src/running-validator/validator-start.md

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

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-01-03 01:45:07 +00:00
Michael Vines
a53946c485 Use singleGossip for program deployment
(cherry picked from commit c63e14dd0e)
2021-01-02 09:21:36 -08:00
mergify[bot]
f6de92c346 Add secondary indexes (#14212) (#14382)
(cherry picked from commit 5affd8aa72)

Co-authored-by: carllin <wumu727@gmail.com>
2021-01-01 07:42:47 +00:00
mergify[bot]
46f9822d62 Only initialize BigTable upload service when requested (#14380)
(cherry picked from commit 4a3d217839)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-01-01 03:06:34 +00:00
mergify[bot]
6dad84d228 Add --ignore-http-bad-gateway flag (#14377)
(cherry picked from commit 6c167615ad)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-31 22:00:29 +00:00
mergify[bot]
3582607aa0 solana-test-validator: bind RPC and faucet to 0.0.0.0 (bp #14369) (#14370)
* Minor help improvements

(cherry picked from commit 04bf5ce830)

* Bind RPC and faucet to 0.0.0.0

(cherry picked from commit 0b23abd479)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-31 09:10:07 +00:00
Michael Vines
f051077350 Require tokio 0.3.5 2020-12-30 22:25:23 -08:00
Michael Vines
ffd6f3e6bf Revert "Upgrade in-tree tokio 0.2 usage to tokio 0.3 (#14326)"
This reverts commit 6c5be574c8.
2020-12-30 22:25:23 -08:00
mergify[bot]
c6b2eb07ee Gate CPI authorized programs (#14361) (#14365)
(cherry picked from commit 2d8dacb72b)

Co-authored-by: Jack May <jack@solana.com>
2020-12-31 03:29:46 +00:00
mergify[bot]
7a3e1f9826 Remove assert (#14356) (#14360)
(cherry picked from commit 1c5427ff17)

Co-authored-by: Jack May <jack@solana.com>
2020-12-30 22:39:55 +00:00
mergify[bot]
8a690b6cf7 nit: clarify loader id (#14355) (#14358)
(cherry picked from commit 6c6095abe7)

Co-authored-by: Jack May <jack@solana.com>
2020-12-30 21:25:41 +00:00
mergify[bot]
8688efa89b Speed up UDP reachable port checks (#14351)
(cherry picked from commit 71b88da48e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-30 19:00:28 +00:00
mergify[bot]
3b047e5b99 Port ip-echo-server to tokio 0.3 (bp #14345) (#14350)
* Port ip-echo-server to tokio 0.3

(cherry picked from commit fb6c660cfd)

# Conflicts:
#	net-utils/Cargo.toml

* Update Cargo.toml

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-30 18:55:24 +00:00
mergify[bot]
3cddc731b2 Add --test arg to cargo-test-bpf (#14342) (#14344)
(cherry picked from commit 3d0cd2cdb0)

Co-authored-by: Justin Starry <justin@solana.com>
2020-12-30 07:55:38 +00:00
mergify[bot]
1d29a583c6 Rewrite faucet with tokio v0.3 (bp #14336) (#14343)
* Rewrite faucet with tokio v0.3 (#14336)

* Rewrite faucet for contemporary tokio

* Move away from framed decoder

(cherry picked from commit d63dd95806)

# Conflicts:
#	faucet/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-12-30 05:09:00 +00:00
mergify[bot]
b5335edb35 Add experimental knob for tuning PoH pinned CPU core (bp #14330) (#14341)
* core: Update stale error message

(cherry picked from commit 82f61c0c4a)

* validator: Add experimental flag to select PoH pinned core

(cherry picked from commit fe667db910)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-12-30 03:33:39 +00:00
mergify[bot]
abee1e83eb Add poh speed check and tick speed calibration (#14292) (#14328)
(cherry picked from commit 2074e407cd)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-12-29 19:40:49 +00:00
mergify[bot]
6c5be574c8 Upgrade in-tree tokio 0.2 usage to tokio 0.3 (#14326)
(cherry picked from commit 444ed768dc)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-29 19:03:18 +00:00
mergify[bot]
c1f993d2fc Retry durable-nonce transactions (#14308) (#14325)
* Retry durable-nonce transactions

* Add metric to track durable-nonce txs in queue

* Populate send-tx-service initial addresses with tpu_address if empty (primarily for testing)

* Reinstate last_valid_slot check for durable-nonce txs; use arbitrary future slot

(cherry picked from commit 3f10fb993b)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-12-29 18:03:04 +00:00
mergify[bot]
e2ddb2f0ea Limit CPI instruction size (#14317) (#14321)
(cherry picked from commit 5524938a50)

Co-authored-by: Jack May <jack@solana.com>
2020-12-29 02:38:22 +00:00
mergify[bot]
f3faba5ca9 Remove Testnet-specific old code (#14305) (#14315)
(cherry picked from commit 7893e2e307)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-28 22:18:33 +00:00
mergify[bot]
3a6fd91739 Log error from AppendVec removal & a panic clean (#14302) (#14310)
(cherry picked from commit addffd7694)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-28 22:08:22 +00:00
mergify[bot]
2d2b3d8287 CLI: Support retrieving past leader schedules (bp #14304) (#14312)
* clap-utils: Add epoch validator

(cherry picked from commit a709850ee4)

* CLI: Support displaying past leader schedules

(cherry picked from commit bd761e2a52)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-12-28 21:41:55 +00:00
mergify[bot]
6e47b88399 run.sh: add env knob for solana-validor (#14303) (#14307)
(cherry picked from commit 4af33674a7)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-28 20:48:03 +00:00
Michael Vines
941e56c6c7 Avoid creating "..tmp" files 2020-12-28 08:58:41 -08:00
Michael Vines
d1adc2a446 Persist gossip contact info
(cherry picked from commit 9ddd6f08e8)
2020-12-27 22:09:00 -08:00
Michael Vines
02da7dfedf Bump version to v1.5.1 2020-12-27 21:57:43 -08:00
mergify[bot]
eb0fd3625a Fix subtraction overflow in metrics (#14290) (#14296)
(cherry picked from commit c693ffaa08)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-12-28 02:34:58 +00:00
mergify[bot]
b87e606626 Fix download speed (#14291) (#14295)
(cherry picked from commit 7b49c85aa7)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-12-28 02:21:40 +00:00
mergify[bot]
1c91376f78 obtains staked-nodes from the root-bank (#14257) (#14293)
... as opposed to the working bank

(cherry picked from commit 49019c6613)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-27 14:49:29 +00:00
mergify[bot]
10067ad07b indexes votes in crds table (#14272) (#14294)
(cherry picked from commit 2fd38d9912)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-27 14:49:23 +00:00
Michael Vines
eb76289107 Fix windows build 2020-12-24 17:49:41 -08:00
Michael Vines
8926736e1c Remove stray dbg 2020-12-24 10:45:34 -08:00
mergify[bot]
bf4c169703 Prevent bpf loader impersonators (#14278) (#14279)
(cherry picked from commit ee0a80a092)

Co-authored-by: Jack May <jack@solana.com>
2020-12-24 04:24:30 +00:00
mergify[bot]
0020e43476 Don't use caller passed executable account (#14276) (#14277)
(cherry picked from commit b1d702a618)

Co-authored-by: Jack May <jack@solana.com>
2020-12-23 23:52:04 +00:00
mergify[bot]
a9a2c76221 Limit CPI from calling loader or native programs (#14252) (#14275)
(cherry picked from commit 0b479ab180)

Co-authored-by: Jack May <jack@solana.com>
2020-12-23 20:01:56 +00:00
mergify[bot]
4754b4e871 Save cloning program account data (#14251) (#14274)
(cherry picked from commit 5945305b1d)

Co-authored-by: Jack May <jack@solana.com>
2020-12-23 19:35:09 +00:00
mergify[bot]
52ffb9a64a Add accounts shrink paths (bp #14238) (#14270)
* Add shrink paths (#14238)


(cherry picked from commit baa9602411)

* Ignore long/hanging test (#14261)

Co-authored-by: sakridge <sakridge@gmail.com>
Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-12-23 08:03:33 +00:00
Trent Nelson
bd0b1503c6 Deinitialize stake data upon zero balance 2020-12-23 06:17:59 +00:00
Trent Nelson
10e7fa40ac Deinitialize vote data upon zero balance 2020-12-23 06:17:59 +00:00
Trent Nelson
198ed407b7 vote: Add helper for creating current-versioned states 2020-12-23 06:17:59 +00:00
Trent Nelson
d96af2dd23 Deinitialize nonce data upon zero balance 2020-12-23 06:17:59 +00:00
mergify[bot]
192cca8f98 validator: Multiple --entrypoint support (bp #14256) (#14264)
* Update entrypoint contact info even when shred version adoption is not requested

(cherry picked from commit 3373082ffa)

* Multiple entrypoint support

(cherry picked from commit ace360ade2)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-23 04:15:44 +00:00
Michael Vines
ee716e1c55 Add log message for when a local snapshot is too old
(cherry picked from commit 65dcb3dc81)
2020-12-22 19:58:29 -08:00
mergify[bot]
6dd3c7c2dd removes &Arc<Self> receivers (#14234) (#14262)
(cherry picked from commit a14cfd660a)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-23 02:11:08 +00:00
mergify[bot]
582b4c9edf Upgradeable programs called same as non-upgradeable (#14239) (#14254)
* Upgradeable programs called same as non-upgradeable

* nudge

(cherry picked from commit ab205b682a)

Co-authored-by: Jack May <jack@solana.com>
2020-12-22 21:17:18 +00:00
mergify[bot]
f15add2a74 Feature-gate stake-program-v3 (#14232) (#14250)
* Remove deprecated legacy stake program

* Add legacy stake program

* Strip out duplicative legacy code

* Feature-deploy stake-program-v3

* Add ownership check in stake processor

(cherry picked from commit 7042f11791)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-12-22 19:42:30 +00:00
mergify[bot]
74d48910e2 Rework upgradeable loader cli (#14209) (#14236)
(cherry picked from commit 3316e7166c)

Co-authored-by: Jack May <jack@solana.com>
2020-12-21 22:26:11 +00:00
mergify[bot]
c53e8ee3ad improves performance in replay-stage (#14217) (#14233)
bank::vote_accounts returns a hash-map which is slow to iterate, but all uses
only require an iterator:
https://github.com/solana-labs/solana/blob/b3dc98856/runtime/src/bank.rs#L4300-L4306
Similarly, calculate_stake_weighted_timestamp takes a hash-map whereas it only
requires an iterator:
https://github.com/solana-labs/solana/blob/b3dc98856/sdk/src/stake_weighted_timestamp.rs#L21-L28

(cherry picked from commit 7b08cb1f0d)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-21 21:23:35 +00:00
Michael Vines
c5e5fedc47 Allow multiple --accounts arguments
(cherry picked from commit 8082a2454c)
2020-12-21 11:43:04 -08:00
Tyera Eulberg
b9929dcd67 Warp-timestamp pr# 2020-12-21 10:53:43 -07:00
sakridge
554a158443 Fix test_max_hashes (#14189)
(cherry picked from commit a5db6399ad)
2020-12-21 09:05:26 -08:00
behzad nouri
b7fa4b7ee1 caches staked nodes computed from vote-accounts (#13929)
(cherry picked from commit d6d76219b6)
2020-12-21 09:05:17 -08:00
behzad nouri
fd44cee8cc limits number of crds values returned when responding to pull requests (#13739)
Crds values buffered when responding to pull-requests can be very large taking a lot of memory.
Added a limit for number of buffered crds values based on outbound data budget.

(cherry picked from commit 691031fefd)
2020-12-21 09:04:50 -08:00
mergify[bot]
c6a362cce2 Do not delete ALL other snapshots before downloading a new snapshot (#14227)
(cherry picked from commit 93ae177503)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-21 10:27:25 +00:00
mergify[bot]
252180c244 Restore Content-Length header for streaming snapshot download (#14222)
(cherry picked from commit 57b03c5bc1)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-21 08:41:02 +00:00
mergify[bot]
e02b4e698e Fix timestamp handling on ledger warp (#14210) (#14218)
* Reset timestamp for slot and epoch-start on warp

* Fix genesis timestamp metric source

* Remove check that timestamp > unix_timestamp_from_genesis

Default to previous timestamp, not genesis timestamp

* Move timestamp metrics to report even on warp

* Initialize slot 0 timestamps correctly

* Add feature gate to warp testnet timestamp

* Review suggestion: simplify warp-timestamp slot check

(cherry picked from commit e15f95a36f)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-12-20 22:52:23 +00:00
mergify[bot]
4811afe8eb Stream RPC snapshot downloads (bp #14213) (#14215)
* Stream RPC snapshot downloads

(cherry picked from commit b3dc988564)

# Conflicts:
#	core/Cargo.toml

* Update Cargo.toml

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-20 01:28:41 +00:00
Michael Vines
bc4568b10f Update Cargo.toml 2020-12-18 20:16:48 -08:00
Michael Vines
d59c131e90 Create a random -keypair.json file alongside the program deploy artifact for easy upgrades
(cherry picked from commit 636a455790)
2020-12-18 20:16:48 -08:00
Michael Vines
825027f9f7 Use AsRef
(cherry picked from commit 9993d2c623)
2020-12-18 20:16:48 -08:00
mergify[bot]
9b8f0bee99 adds crds-value for broadcasting duplicate shreds through gossip (bp #14133) (#14203)
* adds crds-value for broadcasting duplicate shreds through gossip (#14133)

In gossip, the header overhead we get from:
https://github.com/solana-labs/solana/blob/de9ac43eb/core/src/cluster_info.rs#L434-L435
https://github.com/solana-labs/solana/blob/de9ac43eb/core/src/crds_value.rs#L31-L36
https://github.com/solana-labs/solana/blob/de9ac43eb/core/src/crds_value.rs#L73
already exceeds SIZE_OF_NONCE in shreds. We also need aditional
meta-data (wallclock, source pubkey, ...). Which means that given the
SHRED_PAYLOAD_SIZE, we cannot fit all these in PACKET_DATA_SIZE:
https://github.com/solana-labs/solana/blob/de9ac43eb/ledger/src/shred.rs#L80

On top of that, we need 2 shred payloads as the proof of duplicate. So
each DuplicateShred crds value includes only a chunk of the payload,
along with the meta-data to reconstruct the full payload from the chunks
on the receiving end.

(cherry picked from commit 6a3797e164)

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

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-18 22:54:50 +00:00
mergify[bot]
fc13c1d654 getBlockTime RPC method now falls back to BigTable in all cases (#14207)
(cherry picked from commit 0090106f60)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-18 22:23:35 +00:00
mergify[bot]
a57758e9c9 Add CPI support for upgradeable loader (bp #14193) (#14199) 2020-12-18 11:23:00 -08:00
Michael Vines
564590462a Add transactionCount field to GetEpochInfo
(cherry picked from commit efc091e28a)
2020-12-18 10:09:30 -08:00
Michael Vines
269f6af97e fix: add transactionCount field to GetEpochInfo
(cherry picked from commit 01fe835e73)
2020-12-18 10:09:30 -08:00
mergify[bot]
57b8a59632 Reject invalid --expected-shred-version (#14183) (#14202)
* Reject invalid --expected-shred-version

* less code

(cherry picked from commit 3c9b853268)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-18 19:19:57 +09:00
Trent Nelson
4289f52d2b net/gce.sh: Upgrade to Ubuntu 20.04
(cherry picked from commit 3322b83183)
2020-12-17 18:17:01 -07:00
Trent Nelson
573f68620b net/gce.sh: Switch to SSD boot disks
(cherry picked from commit a0507505f4)
2020-12-17 18:17:01 -07:00
Trent Nelson
4bfe64688b net/gce.sh: Bump machine type to 24-core, 64GB RAM
(cherry picked from commit ffe0532ded)
2020-12-17 18:17:01 -07:00
mergify[bot]
50034848a5 Improved Transaction Forwarding (#13944) (#14195)
* Forwarding

* Dedupe leaders

* Use consistent commitment for last_valid_slot in rpc send_transaction

* Plumb rpc send_transaction options into solana-validator

* Extend num slots banking-stage holds forwarded txs

Co-authored-by: Tyera Eulberg <tyera@solana.com>
(cherry picked from commit da7d1e2302)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-12-17 18:14:06 -07:00
Michael Vines
981294cbc6 Don't require increased open file limit at ledger creation
Follow-up to 0b92720fdb, `create_new_ledger()` does not require a higher fd limit
2020-12-17 08:49:23 -08:00
mergify[bot]
ff728e5e56 Fix program account rent exemption (#14176) (#14180)
(cherry picked from commit 593ad80954)

Co-authored-by: Jack May <jack@solana.com>
2020-12-17 03:46:43 -08:00
Michael Vines
9aaf41bef2 Don't require increased open file limit in solana-test-validator
Travis CI in particular does not allow the open file limit to be
increased.

(cherry picked from commit 0b92720fdb)
2020-12-16 22:59:56 -08:00
Michael Vines
271eec656c Use an ephemeral mint address if the client keypair is not available
Typically this can occur in a CI environment

(cherry picked from commit 8d700c3b94)
2020-12-16 22:59:56 -08:00
Trent Nelson
13d071607f Revert "Ignore RUSTSEC-2020-0077 until next 1.4 release"
This reverts commit 1792100e2b.
2020-12-17 01:54:26 +00:00
Trent Nelson
ffe35d9a10 Bump SPL crates 2020-12-17 01:54:26 +00:00
mergify[bot]
bb2fb07b39 Add blockstore skipped api (#14145) (#14167)
* Add blockstore api to determine if a slot was skipped

* Return custom rpc error if slot is skipped

(cherry picked from commit ac0d32bc7e)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-12-16 22:26:54 +00:00
mergify[bot]
85fc51dc61 fix formatting error in docs (#14163)
(cherry picked from commit 41a93ced23)

Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2020-12-16 18:51:24 +00:00
mergify[bot]
0276b6c4c2 Correctly show reward percent changes (#14161)
(cherry picked from commit bebfa6e93c)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-16 18:15:36 +00:00
mergify[bot]
c481e4fe7f Partial shred deserialize cleanup and shred type differentiation (#14094) (#14139)
* Partial shred deserialize cleanup and shred type differentiation in retransmit

* consolidate packet hashing logic

(cherry picked from commit d4a174fb7c)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-12-16 08:57:21 -08:00
mergify[bot]
76a3b3ad11 Remove lock files from programs/bpf/rust (#14148) (#14158)
(cherry picked from commit 49c3f14016)

Co-authored-by: Jack May <jack@solana.com>
2020-12-16 11:56:48 +00:00
mergify[bot]
356c663e88 check for resize access violations (#14142) (#14152)
(cherry picked from commit 025f886e10)

Co-authored-by: Jack May <jack@solana.com>
2020-12-16 10:28:27 +00:00
mergify[bot]
015bbc1e12 Fix up upgradeable bpf loader activation (#14149)
(cherry picked from commit 501fd83afd)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-16 07:54:44 +00:00
mergify[bot]
454a9f3175 Switch solana deploy commitment default from "max" to "singleGossip" (#14146)
(cherry picked from commit db4ac17259)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-16 04:46:45 +00:00
mergify[bot]
485b3d64a1 Add Program loader/environment instruction errors (#14120) (#14143)
(cherry picked from commit d513b0c4ca)

Co-authored-by: Jack May <jack@solana.com>
2020-12-16 03:50:04 +00:00
Michael Vines
5d170d83c0 Remove stray println 2020-12-15 16:44:56 -08:00
mergify[bot]
f54d8ea3ab solana-test-validator usability improvements (bp #14129) (#14136)
* Clean up Cargo.toml

(cherry picked from commit d2af09a647)

* Prevent multiple test-validators from using the same ledger directory

(cherry picked from commit f3272db7f7)

* Add --reset flag to allow for easy ledger reset

(cherry picked from commit 00c46c528e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-12-15 23:21:21 +00:00
mergify[bot]
ef9f54b3d4 Fix race between setting tick height and calculating accounts hash (#14101) (#14132)
Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit 75e9e321de)

Co-authored-by: carllin <wumu727@gmail.com>
2020-12-15 22:05:44 +00:00
mergify[bot]
8d0b102b44 Cleanup ledger builtins (#14083) (#14130)
(cherry picked from commit 582418de5e)

Co-authored-by: Jack May <jack@solana.com>
2020-12-15 21:45:44 +00:00
1379 changed files with 85508 additions and 220807 deletions

View File

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

View File

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

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
/docs/html/
/docs/src/tests.ok
/docs/src/cli/usage.md
/docs/src/.gitbook/assets/*.svg
/farf/
/solana-release/
/solana-release.tar.bz2

View File

@@ -50,6 +50,14 @@ pull_request_rules:
label:
add:
- automerge
- name: v1.3 backport
conditions:
- label=v1.3
actions:
backport:
ignore_conflicts: true
branches:
- v1.3
- name: v1.4 backport
conditions:
- label=v1.4
@@ -74,11 +82,3 @@ pull_request_rules:
ignore_conflicts: true
branches:
- v1.6
- name: v1.7 backport
conditions:
- label=v1.7
actions:
backport:
ignore_conflicts: true
branches:
- v1.7

View File

@@ -73,7 +73,7 @@ jobs:
language: node_js
node_js:
- "lts/*"
- "node"
cache:
directories:

2367
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
[workspace]
members = [
"accounts-cluster-bench",
"bench-exchange",
"bench-streamer",
"bench-tps",
@@ -21,7 +20,6 @@ members = [
"perf",
"validator",
"genesis",
"genesis-utils",
"gossip",
"install",
"keygen",
@@ -30,8 +28,8 @@ members = [
"local-cluster",
"logger",
"log-analyzer",
"merkle-root-bench",
"merkle-tree",
"stake-o-matic",
"storage-bigtable",
"storage-proto",
"streamer",
@@ -39,20 +37,21 @@ members = [
"metrics",
"net-shaper",
"notifier",
"poh",
"poh-bench",
"program-test",
"programs/secp256k1",
"programs/bpf_loader",
"programs/budget",
"programs/config",
"programs/exchange",
"programs/failure",
"programs/noop",
"programs/ownable",
"programs/stake",
"programs/vest",
"programs/vote",
"remote-wallet",
"rpc",
"ramp-tps",
"runtime",
"runtime/store-tool",
"sdk",
@@ -76,6 +75,3 @@ members = [
exclude = [
"programs/bpf",
]
[profile.dev]
split-debuginfo = "unpacked"

View File

@@ -1,6 +1,6 @@
<p align="center">
<a href="https://solana.com">
<img alt="Solana" src="https://i.imgur.com/uBVzyX3.png" width="250" />
<img alt="Solana" src="https://i.imgur.com/OMnvVEz.png" width="250" />
</a>
</p>
@@ -32,12 +32,6 @@ $ sudo apt-get update
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang make
```
On Mac M1s, make sure you set up your terminal & homebrew [to use](https://5balloons.info/correct-way-to-install-and-use-homebrew-on-m1-macs/) Rosetta. You can install it with:
```bash
$ softwareupdate --install-rosetta
```
## **2. Download the source code.**
```bash
@@ -113,41 +107,6 @@ send us that patch!
# Disclaimer
All claims, content, designs, algorithms, estimates, roadmaps,
specifications, and performance measurements described in this project
are done with the Solana Foundation's ("SF") best efforts. It is up to
the reader to check and validate their accuracy and truthfulness.
Furthermore nothing in this project constitutes a solicitation for
investment.
All claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the author's best effort. It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment.
Any content produced by SF or developer resources that SF provides, are
for educational and inspiration purposes only. SF does not encourage,
induce or sanction the deployment, integration or use of any such
applications (including the code comprising the Solana blockchain
protocol) in violation of applicable laws or regulations and hereby
prohibits any such deployment, integration or use. This includes use of
any such applications by the reader (a) in violation of export control
or sanctions laws of the United States or any other applicable
jurisdiction, (b) if the reader is located in or ordinarily resident in
a country or territory subject to comprehensive sanctions administered
by the U.S. Office of Foreign Assets Control (OFAC), or (c) if the
reader is or is working on behalf of a Specially Designated National
(SDN) or a person subject to similar blocking or denied party
prohibitions.
The reader should be aware that U.S. export control and sanctions laws
prohibit U.S. persons (and other persons that are subject to such laws)
from transacting with persons in certain countries and territories or
that are on the SDN list. As a project based primarily on open-source
software, it is possible that such sanctioned persons may nevertheless
bypass prohibitions, obtain the code comprising the Solana blockchain
protocol (or other project code or applications) and deploy, integrate,
or otherwise use it. Accordingly, there is a risk to individuals that
other persons using the Solana blockchain protocol may be sanctioned
persons and that transactions with such persons would be a violation of
U.S. export controls and sanctions law. This risk applies to
individuals, organizations, and other ecosystem participants that
deploy, integrate, or use the Solana blockchain protocol code directly
(e.g., as a node operator), and individuals that transact on the Solana
blockchain through light clients, third party interfaces, and/or wallet
software.
Any content produced by Solana, or developer resources that Solana provides, are for educational and inspiration purposes only. Solana does not encourage, induce or sanction the deployment of any such applications in violation of applicable laws or regulations.

View File

@@ -1,169 +0,0 @@
# Security Policy
1. [Reporting security problems](#reporting)
4. [Security Bug Bounties](#bounty)
2. [Incident Response Process](#process)
<a name="reporting"></a>
## Reporting security problems to Solana
**DO NOT CREATE AN ISSUE** to report a security problem. Instead, please send an
email to security@solana.com and provide your github username so we can add you
to a new draft security advisory for further discussion.
Expect a response as fast as possible, within one business day at the latest.
<a name="bounty"></a>
## Security Bug Bounties
We offer bounties for critical security issues. Please see below for more details.
Loss of Funds:
$500,000 USD in locked SOL tokens (locked for 12 months)
* Theft of funds without users signature from any account
* Theft of funds without users interaction in system, token, stake, vote programs
* Theft of funds that requires users signature - creating a vote program that drains the delegated stakes.
Consensus/Safety Violations:
$250,000 USD in locked SOL tokens (locked for 12 months)
* Consensus safety violation
* Tricking a validator to accept an optimistic confirmation or rooted slot without a double vote, etc..
Other Attacks:
$100,000 USD in locked SOL tokens (locked for 12 months)
* Protocol liveness attacks,
* Eclipse attacks,
* Remote attacks that partition the network,
DoS Attacks:
$25,000 USD in locked SOL tokens (locked for 12 months)
* Remote resource exaustion via Non-RPC protocols
RPC DoS/Crashes:
$5,000 USD in locked SOL tokens (locked for 12 months)
* RPC attacks
Out of Scope:
The following components are out of scope for the bounty program
* Metrics: `/metrics` in the monorepo as well as https://metrics.solana.com
* Explorer: `/explorer` in the monorepo as well as https://explorer.solana.com
* Any encrypted credentials, auth tokens, etc. checked into the repo
* Bugs in dependencies. Please take them upstream!
* Attacks that require social engineering
Eligibility:
* The participant submitting the bug report shall follow the process outlined within this document
* Valid exploits can be eligible even if they are not successfully executed on the cluster
* Multiple submissions for the same class of exploit are still eligible for compensation, though may be compensated at a lower rate, however these will be assessed on a case-by-case basis
* Participants must complete KYC and sign the participation agreement here when the registrations are open https://solana.com/validator-registration. Security exploits will still be assessed and open for submission at all times. This needs only be done prior to distribution of tokens.
Payment of Bug Bounties:
* Payments for eligible bug reports are distributed monthly.
* Bounties for all bug reports submitted in a given month are paid out in the middle of the
following month.
* The SOL/USD conversion rate used for payments is the market price at the end of
the last day of the month for the month in which the bug was submitted.
* The reference for this price is the Closing Price given by Coingecko.com on
that date given here:
https://www.coingecko.com/en/coins/solana/historical_data/usd#panel
* For example, for all bugs submitted in March 2021, the SOL/USD price for bug
payouts is the Close price on 2021-03-31 of $19.49. This applies to all bugs
submitted in March 2021, to be paid in mid-April 2021.
* Bug bounties are paid out in
[stake accounts](https://solana.com/staking) with a
[lockup](https://docs.solana.com/staking/stake-accounts#lockups)
expiring 12 months from the last day of the month in which the bug was submitted.
<a name="process"></a>
## Incident Response Process
In case an incident is discovered or reported, the following process will be
followed to contain, respond and remediate:
### 1. Establish a new draft security advisory
In response to an email to security@solana.com, a member of the `solana-labs/admins` group will
1. Create a new draft security advisory for the incident at https://github.com/solana-labs/solana/security/advisories
1. Add the reporter's github user and the `solana-labs/security-incident-response` group to the draft security advisory
1. Create a private fork of the repository (grey button towards the bottom of the page)
1. Respond to the reporter by email, sharing a link to the draft security advisory
### 2. Triage
Within the draft security advisory, discuss and determine the severity of the
issue. If necessary, members of the `solana-labs/security-incident-response`
group may add other github users to the advisory to assist.
If it is determined that this not a critical network issue then the advisory
should be closed and if more follow-up is required a normal Solana public github
issue should be created.
### 3. Prepare Fixes
For the affected branches, typically all three (edge, beta and stable), prepare
a fix for the issue and push them to the corresponding branch in the private
repository associated with the draft security advisory.
There is no CI available in the private repository so you must build from source
and manually verify fixes.
Code review from the reporter is ideal, as well as from multiple members of the
core development team.
### 4. Notify Security Group Validators
Once an ETA is available for the fix, a member of the
`solana-labs/security-incident-response` group should notify the validators so
they can prepare for an update using the "Solana Red Alert" notification system.
The teams are all over the world and it's critical to provide actionable
information at the right time. Don't be the person that wakes everybody up at
2am when a fix won't be available for hours.
### 5. Ship the patch
Once the fix is accepted, a member of the
`solana-labs/security-incident-response` group should prepare a single patch
file for each affected branch. The commit title for the patch should only
contain the advisory id, and not disclose any further details about the
incident.
Copy the patches to https://release.solana.com/ under a subdirectory named after
the advisory id (example:
https://release.solana.com/GHSA-hx59-f5g4-jghh/v1.4.patch). Contact a member of
the `solana-labs/admins` group if you require access to release.solana.com
Using the "Solana Red Alert" channel:
1. Notify validators that there's an issue and a patch will be provided in X minutes
2. If X minutes expires and there's no patch, notify of the delay and provide a
new ETA
3. Provide links to patches of https://release.solana.com/ for each affected branch
Validators can be expected to build the patch from source against the latest
release for the affected branch.
Since the software version will not change after the patch is applied, request
that each validator notify in the existing channel once they've updated. Manually
monitor the roll out until a sufficient amount of stake has updated - typically
at least 33.3% or 66.6% depending on the issue.
### 6. Public Disclosure and Release
Once the fix has been deployed to the security group validators, the patches from the security
advisory may be merged into the main source repository. A new official release
for each affected branch should be shipped and all validators requested to
upgrade as quickly as possible.
### 7. Security Advisory Bounty Accounting and Cleanup
If this issue is eligible for a bounty, prefix the title of the security
advisory with one of the following, depending on the severity:
* `[Bounty Category: Critical: Loss of Funds]`
* `[Bounty Category: Critical: Loss of Availability]`
* `[Bounty Category: Critical: DoS]`
* `[Bounty Category: Critical: Other]`
* `[Bounty Category: Non-critical]`
* `[Bounty Category: RPC]`
Confirm with the reporter that they agree with the severity assessment, and
discuss as required to reach a conclusion.
We currently do not use the Github workflow to publish security advisories.
Once the issue and fix have been disclosed, and a bounty category is assessed if
appropriate, the GitHub security advisory is no longer needed and can be closed.
Bounties are currently awarded once a quarter (TODO: link to this process, or
inline the workflow)

View File

@@ -1,11 +1,10 @@
[package]
name = "solana-account-decoder"
version = "1.7.2"
version = "1.5.2"
description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-account-decoder"
license = "Apache-2.0"
edition = "2018"
@@ -16,13 +15,14 @@ bs58 = "0.3.1"
bv = "0.11.1"
Inflector = "0.11.4"
lazy_static = "1.4.0"
serde = "1.0.122"
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-config-program = { path = "../programs/config", version = "=1.7.2" }
solana-sdk = { path = "../sdk", version = "=1.7.2" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
solana-config-program = { path = "../programs/config", version = "1.5.2" }
solana-sdk = { path = "../sdk", version = "1.5.2" }
solana-stake-program = { path = "../programs/stake", version = "1.5.2" }
solana-vote-program = { path = "../programs/vote", version = "1.5.2" }
spl-token-v2-0 = { package = "spl-token", version = "=3.0.1", features = ["no-entrypoint"] }
thiserror = "1.0"
zstd = "0.5.1"

View File

@@ -1,11 +1,9 @@
#![allow(clippy::integer_arithmetic)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
pub mod parse_account_data;
pub mod parse_bpf_loader;
pub mod parse_config;
pub mod parse_nonce;
pub mod parse_stake;
@@ -16,10 +14,7 @@ pub mod validator_info;
use {
crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount},
solana_sdk::{
account::ReadableAccount, account::WritableAccount, clock::Epoch,
fee_calculator::FeeCalculator, pubkey::Pubkey,
},
solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey},
std::{
io::{Read, Write},
str::FromStr,
@@ -27,7 +22,6 @@ use {
};
pub type StringAmount = String;
pub type StringDecimals = String;
/// A duplicate representation of an Account for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -48,7 +42,7 @@ pub enum UiAccountData {
Binary(String, UiAccountEncoding),
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum UiAccountEncoding {
Binary, // Legacy. Retained for RPC backwards compatibility
@@ -60,61 +54,58 @@ pub enum UiAccountEncoding {
}
impl UiAccount {
pub fn encode<T: ReadableAccount>(
pub fn encode(
pubkey: &Pubkey,
account: &T,
account: Account,
encoding: UiAccountEncoding,
additional_data: Option<AccountAdditionalData>,
data_slice_config: Option<UiDataSliceConfig>,
) -> Self {
let data = match encoding {
UiAccountEncoding::Binary => UiAccountData::LegacyBinary(
bs58::encode(slice_data(&account.data(), data_slice_config)).into_string(),
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
),
UiAccountEncoding::Base58 => UiAccountData::Binary(
bs58::encode(slice_data(&account.data(), data_slice_config)).into_string(),
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
encoding,
),
UiAccountEncoding::Base64 => UiAccountData::Binary(
base64::encode(slice_data(&account.data(), data_slice_config)),
base64::encode(slice_data(&account.data, data_slice_config)),
encoding,
),
UiAccountEncoding::Base64Zstd => {
let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap();
match encoder
.write_all(slice_data(&account.data(), data_slice_config))
.write_all(slice_data(&account.data, data_slice_config))
.and_then(|()| encoder.finish())
{
Ok(zstd_data) => UiAccountData::Binary(base64::encode(zstd_data), encoding),
Err(_) => UiAccountData::Binary(
base64::encode(slice_data(&account.data(), data_slice_config)),
base64::encode(slice_data(&account.data, data_slice_config)),
UiAccountEncoding::Base64,
),
}
}
UiAccountEncoding::JsonParsed => {
if let Ok(parsed_data) =
parse_account_data(pubkey, &account.owner(), &account.data(), additional_data)
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
{
UiAccountData::Json(parsed_data)
} else {
UiAccountData::Binary(
base64::encode(&account.data()),
UiAccountEncoding::Base64,
)
UiAccountData::Binary(base64::encode(&account.data), UiAccountEncoding::Base64)
}
}
};
UiAccount {
lamports: account.lamports(),
lamports: account.lamports,
data,
owner: account.owner().to_string(),
executable: account.executable(),
rent_epoch: account.rent_epoch(),
owner: account.owner.to_string(),
executable: account.executable,
rent_epoch: account.rent_epoch,
}
}
pub fn decode<T: WritableAccount>(&self) -> Option<T> {
pub fn decode(&self) -> Option<Account> {
let data = match &self.data {
UiAccountData::Json(_) => None,
UiAccountData::LegacyBinary(blob) => bs58::decode(blob).into_vec().ok(),
@@ -134,13 +125,13 @@ impl UiAccount {
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
},
}?;
Some(T::create(
self.lamports,
Some(Account {
lamports: self.lamports,
data,
Pubkey::from_str(&self.owner).ok()?,
self.executable,
self.rent_epoch,
))
owner: Pubkey::from_str(&self.owner).ok()?,
executable: self.executable,
rent_epoch: self.rent_epoch,
})
}
}
@@ -190,7 +181,6 @@ fn slice_data(data: &[u8], data_slice_config: Option<UiDataSliceConfig>) -> &[u8
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::account::{Account, AccountSharedData};
#[test]
fn test_slice_data() {
@@ -224,10 +214,10 @@ mod test {
fn test_base64_zstd() {
let encoded_account = UiAccount::encode(
&Pubkey::default(),
&AccountSharedData::from(Account {
Account {
data: vec![0; 1024],
..Account::default()
}),
},
UiAccountEncoding::Base64Zstd,
None,
None,
@@ -237,9 +227,7 @@ mod test {
UiAccountData::Binary(_, UiAccountEncoding::Base64Zstd)
));
let decoded_account = encoded_account.decode::<Account>().unwrap();
assert_eq!(decoded_account.data(), &vec![0; 1024]);
let decoded_account = encoded_account.decode::<AccountSharedData>().unwrap();
assert_eq!(decoded_account.data(), &vec![0; 1024]);
let decoded_account = encoded_account.decode().unwrap();
assert_eq!(decoded_account.data, vec![0; 1024]);
}
}

View File

@@ -1,5 +1,4 @@
use crate::{
parse_bpf_loader::parse_bpf_upgradeable_loader,
parse_config::parse_config,
parse_nonce::parse_nonce,
parse_stake::parse_stake,
@@ -9,24 +8,19 @@ use crate::{
};
use inflector::Inflector;
use serde_json::Value;
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, stake, system_program, sysvar};
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program, sysvar};
use std::collections::HashMap;
use thiserror::Error;
lazy_static! {
static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id();
static ref CONFIG_PROGRAM_ID: Pubkey = solana_config_program::id();
static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id();
static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id();
static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id();
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();
static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id();
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
let mut m = HashMap::new();
m.insert(
*BPF_UPGRADEABLE_LOADER_PROGRAM_ID,
ParsableAccount::BpfUpgradeableLoader,
);
m.insert(*CONFIG_PROGRAM_ID, ParsableAccount::Config);
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::SplToken);
@@ -66,7 +60,6 @@ pub struct ParsedAccount {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ParsableAccount {
BpfUpgradeableLoader,
Config,
Nonce,
SplToken,
@@ -91,9 +84,6 @@ pub fn parse_account_data(
.ok_or(ParseAccountError::ProgramNotParsable)?;
let additional_data = additional_data.unwrap_or_default();
let parsed_json = match program_name {
ParsableAccount::BpfUpgradeableLoader => {
serde_json::to_value(parse_bpf_upgradeable_loader(data)?)?
}
ParsableAccount::Config => serde_json::to_value(parse_config(data, pubkey)?)?,
ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
ParsableAccount::SplToken => {

View File

@@ -1,181 +0,0 @@
use crate::{
parse_account_data::{ParsableAccount, ParseAccountError},
UiAccountData, UiAccountEncoding,
};
use bincode::{deserialize, serialized_size};
use solana_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey};
pub fn parse_bpf_upgradeable_loader(
data: &[u8],
) -> Result<BpfUpgradeableLoaderAccountType, ParseAccountError> {
let account_state: UpgradeableLoaderState = deserialize(data).map_err(|_| {
ParseAccountError::AccountNotParsable(ParsableAccount::BpfUpgradeableLoader)
})?;
let parsed_account = match account_state {
UpgradeableLoaderState::Uninitialized => BpfUpgradeableLoaderAccountType::Uninitialized,
UpgradeableLoaderState::Buffer { authority_address } => {
let offset = if authority_address.is_some() {
UpgradeableLoaderState::buffer_data_offset().unwrap()
} else {
// This case included for code completeness; in practice, a Buffer account will
// always have authority_address.is_some()
UpgradeableLoaderState::buffer_data_offset().unwrap()
- serialized_size(&Pubkey::default()).unwrap() as usize
};
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
authority: authority_address.map(|pubkey| pubkey.to_string()),
data: UiAccountData::Binary(
base64::encode(&data[offset as usize..]),
UiAccountEncoding::Base64,
),
})
}
UpgradeableLoaderState::Program {
programdata_address,
} => BpfUpgradeableLoaderAccountType::Program(UiProgram {
program_data: programdata_address.to_string(),
}),
UpgradeableLoaderState::ProgramData {
slot,
upgrade_authority_address,
} => {
let offset = if upgrade_authority_address.is_some() {
UpgradeableLoaderState::programdata_data_offset().unwrap()
} else {
UpgradeableLoaderState::programdata_data_offset().unwrap()
- serialized_size(&Pubkey::default()).unwrap() as usize
};
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
slot,
authority: upgrade_authority_address.map(|pubkey| pubkey.to_string()),
data: UiAccountData::Binary(
base64::encode(&data[offset as usize..]),
UiAccountEncoding::Base64,
),
})
}
};
Ok(parsed_account)
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum BpfUpgradeableLoaderAccountType {
Uninitialized,
Buffer(UiBuffer),
Program(UiProgram),
ProgramData(UiProgramData),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiBuffer {
pub authority: Option<String>,
pub data: UiAccountData,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiProgram {
pub program_data: String,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiProgramData {
pub slot: u64,
pub authority: Option<String>,
pub data: UiAccountData,
}
#[cfg(test)]
mod test {
use super::*;
use bincode::serialize;
use solana_sdk::pubkey::Pubkey;
#[test]
fn test_parse_bpf_upgradeable_loader_accounts() {
let bpf_loader_state = UpgradeableLoaderState::Uninitialized;
let account_data = serialize(&bpf_loader_state).unwrap();
assert_eq!(
parse_bpf_upgradeable_loader(&account_data).unwrap(),
BpfUpgradeableLoaderAccountType::Uninitialized
);
let program = vec![7u8; 64]; // Arbitrary program data
let authority = Pubkey::new_unique();
let bpf_loader_state = UpgradeableLoaderState::Buffer {
authority_address: Some(authority),
};
let mut account_data = serialize(&bpf_loader_state).unwrap();
account_data.extend_from_slice(&program);
assert_eq!(
parse_bpf_upgradeable_loader(&account_data).unwrap(),
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
authority: Some(authority.to_string()),
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
})
);
// This case included for code completeness; in practice, a Buffer account will always have
// authority_address.is_some()
let bpf_loader_state = UpgradeableLoaderState::Buffer {
authority_address: None,
};
let mut account_data = serialize(&bpf_loader_state).unwrap();
account_data.extend_from_slice(&program);
assert_eq!(
parse_bpf_upgradeable_loader(&account_data).unwrap(),
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
authority: None,
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
})
);
let programdata_address = Pubkey::new_unique();
let bpf_loader_state = UpgradeableLoaderState::Program {
programdata_address,
};
let account_data = serialize(&bpf_loader_state).unwrap();
assert_eq!(
parse_bpf_upgradeable_loader(&account_data).unwrap(),
BpfUpgradeableLoaderAccountType::Program(UiProgram {
program_data: programdata_address.to_string(),
})
);
let authority = Pubkey::new_unique();
let slot = 42;
let bpf_loader_state = UpgradeableLoaderState::ProgramData {
slot,
upgrade_authority_address: Some(authority),
};
let mut account_data = serialize(&bpf_loader_state).unwrap();
account_data.extend_from_slice(&program);
assert_eq!(
parse_bpf_upgradeable_loader(&account_data).unwrap(),
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
slot,
authority: Some(authority.to_string()),
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
})
);
let bpf_loader_state = UpgradeableLoaderState::ProgramData {
slot,
upgrade_authority_address: None,
};
let mut account_data = serialize(&bpf_loader_state).unwrap();
account_data.extend_from_slice(&program);
assert_eq!(
parse_bpf_upgradeable_loader(&account_data).unwrap(),
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
slot,
authority: None,
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
})
);
}
}

View File

@@ -6,10 +6,10 @@ use bincode::deserialize;
use serde_json::Value;
use solana_config_program::{get_config_data, ConfigKeys};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::stake::config::{self as stake_config, Config as StakeConfig};
use solana_stake_program::config::Config as StakeConfig;
pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
let parsed_account = if pubkey == &stake_config::id() {
let parsed_account = if pubkey == &solana_stake_program::config::id() {
get_config_data(data)
.ok()
.and_then(|data| deserialize::<StakeConfig>(data).ok())
@@ -91,7 +91,6 @@ mod test {
use crate::validator_info::ValidatorInfo;
use serde_json::json;
use solana_config_program::create_config_account;
use solana_sdk::account::ReadableAccount;
#[test]
fn test_parse_config() {
@@ -101,7 +100,11 @@ mod test {
};
let stake_config_account = create_config_account(vec![], &stake_config, 10);
assert_eq!(
parse_config(&stake_config_account.data(), &stake_config::id()).unwrap(),
parse_config(
&stake_config_account.data,
&solana_stake_program::config::id()
)
.unwrap(),
ConfigAccountType::StakeConfig(UiStakeConfig {
warmup_cooldown_rate: 0.25,
slash_penalty: 50,
@@ -121,7 +124,7 @@ mod test {
10,
);
assert_eq!(
parse_config(&validator_info_config_account.data(), &info_pubkey).unwrap(),
parse_config(&validator_info_config_account.data, &info_pubkey).unwrap(),
ConfigAccountType::ValidatorInfo(UiConfig {
keys: vec![
UiConfigKey {

View File

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

View File

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

View File

@@ -214,13 +214,13 @@ pub struct UiStakeHistoryEntry {
mod test {
use super::*;
use solana_sdk::{
account::create_account_for_test, fee_calculator::FeeCalculator, hash::Hash,
account::create_account, fee_calculator::FeeCalculator, hash::Hash,
sysvar::recent_blockhashes::IterItem,
};
#[test]
fn test_parse_sysvars() {
let clock_sysvar = create_account_for_test(&Clock::default());
let clock_sysvar = create_account(&Clock::default(), 1);
assert_eq!(
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
SysvarAccountType::Clock(UiClock::default()),
@@ -233,13 +233,13 @@ mod test {
first_normal_epoch: 1,
first_normal_slot: 12,
};
let epoch_schedule_sysvar = create_account_for_test(&epoch_schedule);
let epoch_schedule_sysvar = create_account(&epoch_schedule, 1);
assert_eq!(
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
SysvarAccountType::EpochSchedule(epoch_schedule),
);
let fees_sysvar = create_account_for_test(&Fees::default());
let fees_sysvar = create_account(&Fees::default(), 1);
assert_eq!(
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
SysvarAccountType::Fees(UiFees::default()),
@@ -252,7 +252,7 @@ mod test {
let recent_blockhashes: RecentBlockhashes = vec![IterItem(0, &hash, &fee_calculator)]
.into_iter()
.collect();
let recent_blockhashes_sysvar = create_account_for_test(&recent_blockhashes);
let recent_blockhashes_sysvar = create_account(&recent_blockhashes, 1);
assert_eq!(
parse_sysvar(
&recent_blockhashes_sysvar.data,
@@ -270,13 +270,13 @@ mod test {
exemption_threshold: 2.0,
burn_percent: 5,
};
let rent_sysvar = create_account_for_test(&rent);
let rent_sysvar = create_account(&rent, 1);
assert_eq!(
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
SysvarAccountType::Rent(rent.into()),
);
let rewards_sysvar = create_account_for_test(&Rewards::default());
let rewards_sysvar = create_account(&Rewards::default(), 1);
assert_eq!(
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
SysvarAccountType::Rewards(UiRewards::default()),
@@ -284,7 +284,7 @@ mod test {
let mut slot_hashes = SlotHashes::default();
slot_hashes.add(1, hash);
let slot_hashes_sysvar = create_account_for_test(&slot_hashes);
let slot_hashes_sysvar = create_account(&slot_hashes, 1);
assert_eq!(
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
@@ -295,7 +295,7 @@ mod test {
let mut slot_history = SlotHistory::default();
slot_history.add(42);
let slot_history_sysvar = create_account_for_test(&slot_history);
let slot_history_sysvar = create_account(&slot_history, 1);
assert_eq!(
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
SysvarAccountType::SlotHistory(UiSlotHistory {
@@ -311,7 +311,7 @@ mod test {
deactivating: 3,
};
stake_history.add(1, stake_history_entry.clone());
let stake_history_sysvar = create_account_for_test(&stake_history);
let stake_history_sysvar = create_account(&stake_history, 1);
assert_eq!(
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {

View File

@@ -1,6 +1,6 @@
use crate::{
parse_account_data::{ParsableAccount, ParseAccountError},
StringAmount, StringDecimals,
StringAmount,
};
use solana_sdk::pubkey::Pubkey;
use spl_token_v2_0::{
@@ -14,23 +14,23 @@ use std::str::FromStr;
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey
pub fn spl_token_id_v2_0() -> Pubkey {
Pubkey::new_from_array(spl_token_v2_0::id().to_bytes())
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
}
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey
pub fn spl_token_v2_0_native_mint() -> Pubkey {
Pubkey::new_from_array(spl_token_v2_0::native_mint::id().to_bytes())
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
}
// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
pub fn spl_token_v2_0_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
SplTokenPubkey::new_from_array(pubkey.to_bytes())
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
}
// A helper function to convert a spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
pub fn pubkey_from_spl_token_v2_0(pubkey: &SplTokenPubkey) -> Pubkey {
Pubkey::new_from_array(pubkey.to_bytes())
Pubkey::from_str(&pubkey.to_string()).unwrap()
}
pub fn parse_token(
@@ -158,66 +158,46 @@ impl From<AccountState> for UiAccountState {
}
}
pub fn real_number_string(amount: u64, decimals: u8) -> StringDecimals {
let decimals = decimals as usize;
if decimals > 0 {
// Left-pad zeros to decimals + 1, so we at least have an integer zero
let mut s = format!("{:01$}", amount, decimals + 1);
// Add the decimal point (Sorry, "," locales!)
s.insert(s.len() - decimals, '.');
s
} else {
amount.to_string()
}
}
pub fn real_number_string_trimmed(amount: u64, decimals: u8) -> StringDecimals {
let mut s = real_number_string(amount, decimals);
if decimals > 0 {
let zeros_trimmed = s.trim_end_matches('0');
s = zeros_trimmed.trim_end_matches('.').to_string();
}
s
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiTokenAmount {
pub ui_amount: Option<f64>,
pub ui_amount: f64,
pub decimals: u8,
pub amount: StringAmount,
pub ui_amount_string: StringDecimals,
}
impl UiTokenAmount {
pub fn real_number_string(&self) -> String {
real_number_string(
u64::from_str(&self.amount).unwrap_or_default(),
self.decimals as u8,
)
let decimals = self.decimals as usize;
if decimals > 0 {
let amount = u64::from_str(&self.amount).unwrap_or(0);
// Left-pad zeros to decimals + 1, so we at least have an integer zero
let mut s = format!("{:01$}", amount, decimals + 1);
// Add the decimal point (Sorry, "," locales!)
s.insert(s.len() - decimals, '.');
s
} else {
self.amount.clone()
}
}
pub fn real_number_string_trimmed(&self) -> String {
if !self.ui_amount_string.is_empty() {
self.ui_amount_string.clone()
} else {
real_number_string_trimmed(
u64::from_str(&self.amount).unwrap_or_default(),
self.decimals as u8,
)
}
let s = self.real_number_string();
let zeros_trimmed = s.trim_end_matches('0');
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
decimal_trimmed.to_string()
}
}
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
let amount_decimals = 10_usize
.checked_pow(decimals as u32)
.map(|dividend| amount as f64 / dividend as f64);
// Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211
let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64;
UiTokenAmount {
ui_amount: amount_decimals,
decimals,
amount: amount.to_string(),
ui_amount_string: real_number_string_trimmed(amount, decimals),
}
}
@@ -273,10 +253,9 @@ mod test {
mint: mint_pubkey.to_string(),
owner: owner_pubkey.to_string(),
token_amount: UiTokenAmount {
ui_amount: Some(0.42),
ui_amount: 0.42,
decimals: 2,
amount: "42".to_string(),
ui_amount_string: "0.42".to_string()
amount: "42".to_string()
},
delegate: None,
state: UiAccountState::Initialized,
@@ -357,87 +336,17 @@ mod test {
#[test]
fn test_ui_token_amount_real_string() {
assert_eq!(&real_number_string(1, 0), "1");
assert_eq!(&real_number_string_trimmed(1, 0), "1");
let token_amount = token_amount_to_ui_amount(1, 0);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(1, 0)
);
assert_eq!(token_amount.ui_amount, Some(1.0));
assert_eq!(&real_number_string(10, 0), "10");
assert_eq!(&real_number_string_trimmed(10, 0), "10");
let token_amount = token_amount_to_ui_amount(10, 0);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(10, 0)
);
assert_eq!(token_amount.ui_amount, Some(10.0));
assert_eq!(&real_number_string(1, 9), "0.000000001");
assert_eq!(&real_number_string_trimmed(1, 9), "0.000000001");
assert_eq!(&token_amount.real_number_string(), "1");
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
let token_amount = token_amount_to_ui_amount(1, 9);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(1, 9)
);
assert_eq!(token_amount.ui_amount, Some(0.000000001));
assert_eq!(&real_number_string(1_000_000_000, 9), "1.000000000");
assert_eq!(&real_number_string_trimmed(1_000_000_000, 9), "1");
assert_eq!(&token_amount.real_number_string(), "0.000000001");
assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001");
let token_amount = token_amount_to_ui_amount(1_000_000_000, 9);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(1_000_000_000, 9)
);
assert_eq!(token_amount.ui_amount, Some(1.0));
assert_eq!(&real_number_string(1_234_567_890, 3), "1234567.890");
assert_eq!(&real_number_string_trimmed(1_234_567_890, 3), "1234567.89");
assert_eq!(&token_amount.real_number_string(), "1.000000000");
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
let token_amount = token_amount_to_ui_amount(1_234_567_890, 3);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(1_234_567_890, 3)
);
assert_eq!(token_amount.ui_amount, Some(1234567.89));
assert_eq!(
&real_number_string(1_234_567_890, 25),
"0.0000000000000001234567890"
);
assert_eq!(
&real_number_string_trimmed(1_234_567_890, 25),
"0.000000000000000123456789"
);
let token_amount = token_amount_to_ui_amount(1_234_567_890, 20);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(1_234_567_890, 20)
);
assert_eq!(token_amount.ui_amount, None);
}
#[test]
fn test_ui_token_amount_real_string_zero() {
assert_eq!(&real_number_string(0, 0), "0");
assert_eq!(&real_number_string_trimmed(0, 0), "0");
let token_amount = token_amount_to_ui_amount(0, 0);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(0, 0)
);
assert_eq!(token_amount.ui_amount, Some(0.0));
assert_eq!(&real_number_string(0, 9), "0.000000000");
assert_eq!(&real_number_string_trimmed(0, 9), "0");
let token_amount = token_amount_to_ui_amount(0, 9);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(0, 9)
);
assert_eq!(token_amount.ui_amount, Some(0.0));
assert_eq!(&real_number_string(0, 25), "0.0000000000000000000000000");
assert_eq!(&real_number_string_trimmed(0, 25), "0");
let token_amount = token_amount_to_ui_amount(0, 20);
assert_eq!(
token_amount.ui_amount_string,
real_number_string_trimmed(0, 20)
);
assert_eq!(token_amount.ui_amount, None);
assert_eq!(&token_amount.real_number_string(), "1234567.890");
assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89");
}
}

View File

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

View File

@@ -1,17 +1,14 @@
#![allow(clippy::integer_arithmetic)]
#[macro_use]
extern crate log;
use clap::{crate_description, crate_name, value_t, App, Arg};
use rayon::prelude::*;
use solana_measure::measure::Measure;
use solana_runtime::{
accounts::{create_test_accounts, update_accounts_bench, Accounts},
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
ancestors::Ancestors,
accounts::{create_test_accounts, update_accounts, Accounts},
accounts_index::Ancestors,
};
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
use std::{env, fs, path::PathBuf};
use std::env;
use std::fs;
use std::path::PathBuf;
fn main() {
solana_logger::setup();
@@ -56,17 +53,10 @@ fn main() {
let path = PathBuf::from(env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_owned()))
.join("accounts-bench");
println!("cleaning file system: {:?}", path);
if fs::remove_dir_all(path.clone()).is_err() {
println!("Warning: Couldn't remove {:?}", path);
}
let accounts = Accounts::new_with_config(
vec![path],
&ClusterType::Testnet,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let accounts = Accounts::new(vec![path], &ClusterType::Testnet);
println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts");
let pubkeys: Vec<_> = (0..num_slots)
@@ -90,60 +80,31 @@ fn main() {
num_slots,
create_time
);
let mut ancestors = Vec::with_capacity(num_slots);
ancestors.push(0);
let mut ancestors: Ancestors = vec![(0, 0)].into_iter().collect();
for i in 1..num_slots {
ancestors.push(i as u64);
ancestors.insert(i as u64, i - 1);
accounts.add_root(i as u64);
}
let ancestors = Ancestors::from(ancestors);
let mut elapsed = vec![0; iterations];
let mut elapsed_store = vec![0; iterations];
for x in 0..iterations {
if clean {
let mut time = Measure::start("clean");
accounts.accounts_db.clean_accounts(None, false);
accounts.accounts_db.clean_accounts(None);
time.stop();
println!("{}", time);
for slot in 0..num_slots {
update_accounts_bench(&accounts, &pubkeys, ((x + 1) * num_slots + slot) as u64);
update_accounts(&accounts, &pubkeys, ((x + 1) * num_slots + slot) as u64);
accounts.add_root((x * num_slots + slot) as u64);
}
} else {
let mut pubkeys: Vec<Pubkey> = vec![];
let mut time = Measure::start("hash");
let results = accounts.accounts_db.update_accounts_hash(0, &ancestors);
let hash = accounts
.accounts_db
.update_accounts_hash(0, &ancestors, true)
.0;
time.stop();
let mut time_store = Measure::start("hash using store");
let results_store = accounts.accounts_db.update_accounts_hash_with_index_option(
false,
false,
solana_sdk::clock::Slot::default(),
&ancestors,
None,
false,
);
time_store.stop();
if results != results_store {
error!("results different: \n{:?}\n{:?}", results, results_store);
}
println!(
"hash,{},{},{},{}%",
results.0,
time,
time_store,
(time_store.as_us() as f64 / time.as_us() as f64 * 100.0f64) as u32
);
println!("hash: {} {}", hash, time);
create_test_accounts(&accounts, &mut pubkeys, 1, 0);
elapsed[x] = time.as_us();
elapsed_store[x] = time_store.as_us();
}
}
for x in elapsed {
info!("update_accounts_hash(us),{}", x);
}
for x in elapsed_store {
info!("calculate_accounts_hash_without_index(us),{}", x);
}
}

View File

@@ -1 +0,0 @@
/farf/

View File

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

View File

@@ -1,747 +0,0 @@
#![allow(clippy::integer_arithmetic)]
use clap::{crate_description, crate_name, value_t, values_t_or_exit, App, Arg};
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_account_decoder::parse_token::spl_token_v2_0_pubkey;
use solana_clap_utils::input_parsers::pubkey_of;
use solana_client::rpc_client::RpcClient;
use solana_faucet::faucet::{request_airdrop_transaction, FAUCET_PORT};
use solana_gossip::gossip_service::discover;
use solana_measure::measure::Measure;
use solana_runtime::inline_spl_token_v2_0;
use solana_sdk::{
commitment_config::CommitmentConfig,
message::Message,
pubkey::Pubkey,
rpc_port::DEFAULT_RPC_PORT,
signature::{read_keypair_file, Keypair, Signature, Signer},
system_instruction, system_program,
timing::timestamp,
transaction::Transaction,
};
use solana_transaction_status::parse_token::spl_token_v2_0_instruction;
use std::{
net::SocketAddr,
process::exit,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc, RwLock,
},
thread::{sleep, Builder, JoinHandle},
time::{Duration, Instant},
};
// Create and close messages both require 2 signatures; if transaction construction changes, update
// this magic number
const NUM_SIGNATURES: u64 = 2;
pub fn airdrop_lamports(
client: &RpcClient,
faucet_addr: &SocketAddr,
id: &Keypair,
desired_balance: u64,
) -> bool {
let starting_balance = client.get_balance(&id.pubkey()).unwrap_or(0);
info!("starting balance {}", starting_balance);
if starting_balance < desired_balance {
let airdrop_amount = desired_balance - starting_balance;
info!(
"Airdropping {:?} lamports from {} for {}",
airdrop_amount,
faucet_addr,
id.pubkey(),
);
let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap();
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
Ok(transaction) => {
let mut tries = 0;
loop {
tries += 1;
let result = client.send_and_confirm_transaction(&transaction);
if result.is_ok() {
break;
}
if tries >= 5 {
panic!(
"Error requesting airdrop: to addr: {:?} amount: {} {:?}",
faucet_addr, airdrop_amount, result
)
}
}
}
Err(err) => {
panic!(
"Error requesting airdrop: {:?} to addr: {:?} amount: {}",
err, faucet_addr, airdrop_amount
);
}
};
let current_balance = client.get_balance(&id.pubkey()).unwrap_or_else(|e| {
panic!("airdrop error {}", e);
});
info!("current balance {}...", current_balance);
if current_balance - starting_balance != airdrop_amount {
info!(
"Airdrop failed? {} {} {} {}",
id.pubkey(),
current_balance,
starting_balance,
airdrop_amount,
);
}
}
true
}
// signature, timestamp, id
type PendingQueue = Vec<(Signature, u64, u64)>;
struct TransactionExecutor {
sig_clear_t: JoinHandle<()>,
sigs: Arc<RwLock<PendingQueue>>,
cleared: Arc<RwLock<Vec<u64>>>,
exit: Arc<AtomicBool>,
counter: AtomicU64,
client: RpcClient,
}
impl TransactionExecutor {
fn new(entrypoint_addr: SocketAddr) -> Self {
let sigs = Arc::new(RwLock::new(Vec::new()));
let cleared = Arc::new(RwLock::new(Vec::new()));
let exit = Arc::new(AtomicBool::new(false));
let sig_clear_t = Self::start_sig_clear_thread(&exit, &sigs, &cleared, entrypoint_addr);
let client =
RpcClient::new_socket_with_commitment(entrypoint_addr, CommitmentConfig::confirmed());
Self {
sigs,
cleared,
sig_clear_t,
exit,
counter: AtomicU64::new(0),
client,
}
}
fn num_outstanding(&self) -> usize {
self.sigs.read().unwrap().len()
}
fn push_transactions(&self, txs: Vec<Transaction>) -> Vec<u64> {
let mut ids = vec![];
let new_sigs = txs.into_iter().filter_map(|tx| {
let id = self.counter.fetch_add(1, Ordering::Relaxed);
ids.push(id);
match self.client.send_transaction(&tx) {
Ok(sig) => {
return Some((sig, timestamp(), id));
}
Err(e) => {
info!("error: {:#?}", e);
}
}
None
});
let mut sigs_w = self.sigs.write().unwrap();
sigs_w.extend(new_sigs);
ids
}
fn drain_cleared(&self) -> Vec<u64> {
std::mem::take(&mut *self.cleared.write().unwrap())
}
fn close(self) {
self.exit.store(true, Ordering::Relaxed);
self.sig_clear_t.join().unwrap();
}
fn start_sig_clear_thread(
exit: &Arc<AtomicBool>,
sigs: &Arc<RwLock<PendingQueue>>,
cleared: &Arc<RwLock<Vec<u64>>>,
entrypoint_addr: SocketAddr,
) -> JoinHandle<()> {
let sigs = sigs.clone();
let exit = exit.clone();
let cleared = cleared.clone();
Builder::new()
.name("sig_clear".to_string())
.spawn(move || {
let client = RpcClient::new_socket_with_commitment(
entrypoint_addr,
CommitmentConfig::confirmed(),
);
let mut success = 0;
let mut error_count = 0;
let mut timed_out = 0;
let mut last_log = Instant::now();
while !exit.load(Ordering::Relaxed) {
let sigs_len = sigs.read().unwrap().len();
if sigs_len > 0 {
let mut sigs_w = sigs.write().unwrap();
let mut start = Measure::start("sig_status");
let statuses: Vec<_> = sigs_w
.chunks(200)
.flat_map(|sig_chunk| {
let only_sigs: Vec<_> = sig_chunk.iter().map(|s| s.0).collect();
client
.get_signature_statuses(&only_sigs)
.expect("status fail")
.value
})
.collect();
let mut num_cleared = 0;
let start_len = sigs_w.len();
let now = timestamp();
let mut new_ids = vec![];
let mut i = 0;
let mut j = 0;
while i != sigs_w.len() {
let mut retain = true;
let sent_ts = sigs_w[i].1;
if let Some(e) = &statuses[j] {
debug!("error: {:?}", e);
if e.status.is_ok() {
success += 1;
} else {
error_count += 1;
}
num_cleared += 1;
retain = false;
} else if now - sent_ts > 30_000 {
retain = false;
timed_out += 1;
}
if !retain {
new_ids.push(sigs_w.remove(i).2);
} else {
i += 1;
}
j += 1;
}
let final_sigs_len = sigs_w.len();
drop(sigs_w);
cleared.write().unwrap().extend(new_ids);
start.stop();
debug!(
"sigs len: {:?} success: {} took: {}ms cleared: {}/{}",
final_sigs_len,
success,
start.as_ms(),
num_cleared,
start_len,
);
if last_log.elapsed().as_millis() > 5000 {
info!(
"success: {} error: {} timed_out: {}",
success, error_count, timed_out,
);
last_log = Instant::now();
}
}
sleep(Duration::from_millis(200));
}
})
.unwrap()
}
}
struct SeedTracker {
max_created: Arc<AtomicU64>,
max_closed: Arc<AtomicU64>,
}
fn make_create_message(
keypair: &Keypair,
base_keypair: &Keypair,
max_created_seed: Arc<AtomicU64>,
num_instructions: usize,
balance: u64,
maybe_space: Option<u64>,
mint: Option<Pubkey>,
) -> Message {
let space = maybe_space.unwrap_or_else(|| thread_rng().gen_range(0, 1000));
let instructions: Vec<_> = (0..num_instructions)
.into_iter()
.map(|_| {
let program_id = if mint.is_some() {
inline_spl_token_v2_0::id()
} else {
system_program::id()
};
let seed = max_created_seed.fetch_add(1, Ordering::Relaxed).to_string();
let to_pubkey =
Pubkey::create_with_seed(&base_keypair.pubkey(), &seed, &program_id).unwrap();
let mut instructions = vec![system_instruction::create_account_with_seed(
&keypair.pubkey(),
&to_pubkey,
&base_keypair.pubkey(),
&seed,
balance,
space,
&program_id,
)];
if let Some(mint_address) = mint {
instructions.push(spl_token_v2_0_instruction(
spl_token_v2_0::instruction::initialize_account(
&spl_token_v2_0::id(),
&spl_token_v2_0_pubkey(&to_pubkey),
&spl_token_v2_0_pubkey(&mint_address),
&spl_token_v2_0_pubkey(&base_keypair.pubkey()),
)
.unwrap(),
));
}
instructions
})
.collect();
let instructions: Vec<_> = instructions.into_iter().flatten().collect();
Message::new(&instructions, Some(&keypair.pubkey()))
}
fn make_close_message(
keypair: &Keypair,
base_keypair: &Keypair,
max_closed_seed: Arc<AtomicU64>,
num_instructions: usize,
balance: u64,
spl_token: bool,
) -> Message {
let instructions: Vec<_> = (0..num_instructions)
.into_iter()
.map(|_| {
let program_id = if spl_token {
inline_spl_token_v2_0::id()
} else {
system_program::id()
};
let seed = max_closed_seed.fetch_add(1, Ordering::Relaxed).to_string();
let address =
Pubkey::create_with_seed(&base_keypair.pubkey(), &seed, &program_id).unwrap();
if spl_token {
spl_token_v2_0_instruction(
spl_token_v2_0::instruction::close_account(
&spl_token_v2_0::id(),
&spl_token_v2_0_pubkey(&address),
&spl_token_v2_0_pubkey(&keypair.pubkey()),
&spl_token_v2_0_pubkey(&base_keypair.pubkey()),
&[],
)
.unwrap(),
)
} else {
system_instruction::transfer_with_seed(
&address,
&base_keypair.pubkey(),
seed,
&program_id,
&keypair.pubkey(),
balance,
)
}
})
.collect();
Message::new(&instructions, Some(&keypair.pubkey()))
}
#[allow(clippy::too_many_arguments)]
fn run_accounts_bench(
entrypoint_addr: SocketAddr,
faucet_addr: SocketAddr,
payer_keypairs: &[&Keypair],
iterations: usize,
maybe_space: Option<u64>,
batch_size: usize,
close_nth_batch: u64,
maybe_lamports: Option<u64>,
num_instructions: usize,
mint: Option<Pubkey>,
) {
assert!(num_instructions > 0);
let client =
RpcClient::new_socket_with_commitment(entrypoint_addr, CommitmentConfig::confirmed());
info!("Targeting {}", entrypoint_addr);
let mut last_blockhash = Instant::now();
let mut last_log = Instant::now();
let mut count = 0;
let mut recent_blockhash = client.get_recent_blockhash().expect("blockhash");
let mut tx_sent_count = 0;
let mut total_accounts_created = 0;
let mut total_accounts_closed = 0;
let mut balances: Vec<_> = payer_keypairs
.iter()
.map(|keypair| client.get_balance(&keypair.pubkey()).unwrap_or(0))
.collect();
let mut last_balance = Instant::now();
let default_max_lamports = 1000;
let min_balance = maybe_lamports.unwrap_or_else(|| {
let space = maybe_space.unwrap_or(default_max_lamports);
client
.get_minimum_balance_for_rent_exemption(space as usize)
.expect("min balance")
});
let base_keypair = Keypair::new();
let seed_tracker = SeedTracker {
max_created: Arc::new(AtomicU64::default()),
max_closed: Arc::new(AtomicU64::default()),
};
info!("Starting balance(s): {:?}", balances);
let executor = TransactionExecutor::new(entrypoint_addr);
loop {
if last_blockhash.elapsed().as_millis() > 10_000 {
recent_blockhash = client.get_recent_blockhash().expect("blockhash");
last_blockhash = Instant::now();
}
let fee = recent_blockhash
.1
.lamports_per_signature
.saturating_mul(NUM_SIGNATURES);
let lamports = min_balance + fee;
for (i, balance) in balances.iter_mut().enumerate() {
if *balance < lamports || last_balance.elapsed().as_millis() > 2000 {
if let Ok(b) = client.get_balance(&payer_keypairs[i].pubkey()) {
*balance = b;
}
last_balance = Instant::now();
if *balance < lamports * 2 {
info!(
"Balance {} is less than needed: {}, doing aidrop...",
balance, lamports
);
if !airdrop_lamports(
&client,
&faucet_addr,
&payer_keypairs[i],
lamports * 100_000,
) {
warn!("failed airdrop, exiting");
return;
}
}
}
}
// Create accounts
let sigs_len = executor.num_outstanding();
if sigs_len < batch_size {
let num_to_create = batch_size - sigs_len;
if num_to_create >= payer_keypairs.len() {
info!("creating {} new", num_to_create);
let chunk_size = num_to_create / payer_keypairs.len();
if chunk_size > 0 {
for (i, keypair) in payer_keypairs.iter().enumerate() {
let txs: Vec<_> = (0..chunk_size)
.into_par_iter()
.map(|_| {
let message = make_create_message(
keypair,
&base_keypair,
seed_tracker.max_created.clone(),
num_instructions,
min_balance,
maybe_space,
mint,
);
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
Transaction::new(&signers, message, recent_blockhash.0)
})
.collect();
balances[i] = balances[i].saturating_sub(lamports * txs.len() as u64);
info!("txs: {}", txs.len());
let new_ids = executor.push_transactions(txs);
info!("ids: {}", new_ids.len());
tx_sent_count += new_ids.len();
total_accounts_created += num_instructions * new_ids.len();
}
}
}
if close_nth_batch > 0 {
let num_batches_to_close =
total_accounts_created as u64 / (close_nth_batch * batch_size as u64);
let expected_closed = num_batches_to_close * batch_size as u64;
let max_closed_seed = seed_tracker.max_closed.load(Ordering::Relaxed);
// Close every account we've created with seed between max_closed_seed..expected_closed
if max_closed_seed < expected_closed {
let txs: Vec<_> = (0..expected_closed - max_closed_seed)
.into_par_iter()
.map(|_| {
let message = make_close_message(
&payer_keypairs[0],
&base_keypair,
seed_tracker.max_closed.clone(),
1,
min_balance,
mint.is_some(),
);
let signers: Vec<&Keypair> = vec![&payer_keypairs[0], &base_keypair];
Transaction::new(&signers, message, recent_blockhash.0)
})
.collect();
balances[0] = balances[0].saturating_sub(fee * txs.len() as u64);
info!("close txs: {}", txs.len());
let new_ids = executor.push_transactions(txs);
info!("close ids: {}", new_ids.len());
tx_sent_count += new_ids.len();
total_accounts_closed += new_ids.len() as u64;
}
}
} else {
let _ = executor.drain_cleared();
}
count += 1;
if last_log.elapsed().as_millis() > 3000 {
info!(
"total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}",
total_accounts_created, total_accounts_closed, tx_sent_count, count, balances
);
last_log = Instant::now();
}
if iterations != 0 && count >= iterations {
break;
}
if executor.num_outstanding() >= batch_size {
sleep(Duration::from_millis(500));
}
}
executor.close();
}
fn main() {
solana_logger::setup_with_default("solana=info");
let matches = App::new(crate_name!())
.about(crate_description!())
.version(solana_version::version!())
.arg(
Arg::with_name("entrypoint")
.long("entrypoint")
.takes_value(true)
.value_name("HOST:PORT")
.help("RPC entrypoint address. Usually <ip>:8899"),
)
.arg(
Arg::with_name("faucet_addr")
.long("faucet")
.takes_value(true)
.value_name("HOST:PORT")
.help("Faucet entrypoint address. Usually <ip>:9900"),
)
.arg(
Arg::with_name("space")
.long("space")
.takes_value(true)
.value_name("BYTES")
.help("Size of accounts to create"),
)
.arg(
Arg::with_name("lamports")
.long("lamports")
.takes_value(true)
.value_name("LAMPORTS")
.help("How many lamports to fund each account"),
)
.arg(
Arg::with_name("identity")
.long("identity")
.takes_value(true)
.multiple(true)
.value_name("FILE")
.help("keypair file"),
)
.arg(
Arg::with_name("batch_size")
.long("batch-size")
.takes_value(true)
.value_name("BYTES")
.help("Number of transactions to send per batch"),
)
.arg(
Arg::with_name("close_nth_batch")
.long("close-frequency")
.takes_value(true)
.value_name("BYTES")
.help(
"Every `n` batches, create a batch of close transactions for
the earliest remaining batch of accounts created.
Note: Should be > 1 to avoid situations where the close \
transactions will be submitted before the corresponding \
create transactions have been confirmed",
),
)
.arg(
Arg::with_name("num_instructions")
.long("num-instructions")
.takes_value(true)
.value_name("NUM")
.help("Number of accounts to create on each transaction"),
)
.arg(
Arg::with_name("iterations")
.long("iterations")
.takes_value(true)
.value_name("NUM")
.help("Number of iterations to make"),
)
.arg(
Arg::with_name("check_gossip")
.long("check-gossip")
.help("Just use entrypoint address directly"),
)
.arg(
Arg::with_name("mint")
.long("mint")
.takes_value(true)
.help("Mint address to initialize account"),
)
.get_matches();
let skip_gossip = !matches.is_present("check_gossip");
let port = if skip_gossip { DEFAULT_RPC_PORT } else { 8001 };
let mut entrypoint_addr = SocketAddr::from(([127, 0, 0, 1], port));
if let Some(addr) = matches.value_of("entrypoint") {
entrypoint_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
eprintln!("failed to parse entrypoint address: {}", e);
exit(1)
});
}
let mut faucet_addr = SocketAddr::from(([127, 0, 0, 1], FAUCET_PORT));
if let Some(addr) = matches.value_of("faucet_addr") {
faucet_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
eprintln!("failed to parse entrypoint address: {}", e);
exit(1)
});
}
let space = value_t!(matches, "space", u64).ok();
let lamports = value_t!(matches, "lamports", u64).ok();
let batch_size = value_t!(matches, "batch_size", usize).unwrap_or(4);
let close_nth_batch = value_t!(matches, "close_nth_batch", u64).unwrap_or(0);
let iterations = value_t!(matches, "iterations", usize).unwrap_or(10);
let num_instructions = value_t!(matches, "num_instructions", usize).unwrap_or(1);
if num_instructions == 0 || num_instructions > 500 {
eprintln!("bad num_instructions: {}", num_instructions);
exit(1);
}
let mint = pubkey_of(&matches, "mint");
let payer_keypairs: Vec<_> = values_t_or_exit!(matches, "identity", String)
.iter()
.map(|keypair_string| {
read_keypair_file(keypair_string)
.unwrap_or_else(|_| panic!("bad keypair {:?}", keypair_string))
})
.collect();
let mut payer_keypair_refs: Vec<&Keypair> = vec![];
for keypair in payer_keypairs.iter() {
payer_keypair_refs.push(keypair);
}
let rpc_addr = if !skip_gossip {
info!("Finding cluster entry: {:?}", entrypoint_addr);
let (gossip_nodes, _validators) = discover(
None, // keypair
Some(&entrypoint_addr),
None, // num_nodes
Duration::from_secs(60), // timeout
None, // find_node_by_pubkey
Some(&entrypoint_addr), // find_node_by_gossip_addr
None, // my_gossip_addr
0, // my_shred_version
)
.unwrap_or_else(|err| {
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
exit(1);
});
info!("done found {} nodes", gossip_nodes.len());
gossip_nodes[0].rpc
} else {
info!("Using {:?} as the RPC address", entrypoint_addr);
entrypoint_addr
};
run_accounts_bench(
rpc_addr,
faucet_addr,
&payer_keypair_refs,
iterations,
space,
batch_size,
close_nth_batch,
lamports,
num_instructions,
mint,
);
}
#[cfg(test)]
pub mod test {
use super::*;
use solana_core::validator::ValidatorConfig;
use solana_local_cluster::{
local_cluster::{ClusterConfig, LocalCluster},
validator_configs::make_identical_validator_configs,
};
use solana_sdk::poh_config::PohConfig;
#[test]
fn test_accounts_cluster_bench() {
solana_logger::setup();
let validator_config = ValidatorConfig::default();
let num_nodes = 1;
let mut config = ClusterConfig {
cluster_lamports: 10_000_000,
poh_config: PohConfig::new_sleep(Duration::from_millis(50)),
node_stakes: vec![100; num_nodes],
validator_configs: make_identical_validator_configs(&validator_config, num_nodes),
..ClusterConfig::default()
};
let faucet_addr = SocketAddr::from(([127, 0, 0, 1], 9900));
let cluster = LocalCluster::new(&mut config);
let iterations = 10;
let maybe_space = None;
let batch_size = 100;
let close_nth_batch = 100;
let maybe_lamports = None;
let num_instructions = 2;
let mut start = Measure::start("total accounts run");
run_accounts_bench(
cluster.entry_point_info.rpc,
faucet_addr,
&[&cluster.funding_keypair],
iterations,
maybe_space,
batch_size,
close_nth_batch,
maybe_lamports,
num_instructions,
None,
);
start.stop();
info!("{}", start);
}
}

View File

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

View File

@@ -1,11 +1,15 @@
#![allow(clippy::integer_arithmetic)]
use clap::{crate_description, crate_name, value_t, App, Arg};
use crossbeam_channel::unbounded;
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_core::banking_stage::BankingStage;
use solana_gossip::{cluster_info::ClusterInfo, cluster_info::Node};
use solana_core::{
banking_stage::{create_test_recorder, BankingStage},
cluster_info::ClusterInfo,
cluster_info::Node,
poh_recorder::PohRecorder,
poh_recorder::WorkingBankEntry,
};
use solana_ledger::{
blockstore::Blockstore,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
@@ -13,9 +17,8 @@ use solana_ledger::{
};
use solana_measure::measure::Measure;
use solana_perf::packet::to_packets_chunked;
use solana_poh::poh_recorder::{create_test_recorder, PohRecorder, WorkingBankEntry};
use solana_runtime::{
accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks,
accounts_background_service::ABSRequestSender, bank::Bank, bank_forks::BankForks,
};
use solana_sdk::{
hash::Hash,
@@ -74,7 +77,7 @@ fn make_accounts_txs(
.into_par_iter()
.map(|_| {
let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
if !same_payer {
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
}
@@ -185,7 +188,7 @@ fn main() {
genesis_config.hash(),
);
// Ignore any pesky duplicate signature errors in the case we are using single-payer
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
fund.signatures = vec![Signature::new(&sig[0..64])];
let x = bank.process_transaction(&fund);
x.unwrap();
@@ -322,7 +325,7 @@ fn main() {
poh_recorder.lock().unwrap().set_bank(&bank);
assert!(poh_recorder.lock().unwrap().bank().is_some());
if bank.slot() > 32 {
bank_forks.set_root(root, &AbsRequestSender::default(), None);
bank_forks.set_root(root, &ABSRequestSender::default(), None);
root += 1;
}
debug!(
@@ -351,7 +354,7 @@ fn main() {
if bank.slot() > 0 && bank.slot() % 16 == 0 {
for tx in transactions.iter_mut() {
tx.message.recent_blockhash = bank.last_blockhash();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
tx.signatures[0] = Signature::new(&sig[0..64]);
}
verified = to_packets_chunked(&transactions.clone(), packets_per_chunk);

View File

@@ -1,30 +1,26 @@
[package]
name = "solana-banks-client"
version = "1.7.2"
version = "1.5.2"
description = "Solana banks client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-banks-client"
edition = "2018"
[dependencies]
bincode = "1.3.1"
borsh = "0.8.1"
borsh-derive = "0.8.1"
futures = "0.3"
mio = "0.7.6"
solana-banks-interface = { path = "../banks-interface", version = "=1.7.2" }
solana-program = { path = "../sdk/program", version = "=1.7.2" }
solana-sdk = { path = "../sdk", version = "=1.7.2" }
tarpc = { version = "0.24.1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tokio-serde = { version = "0.8", features = ["bincode"] }
solana-banks-interface = { path = "../banks-interface", version = "1.5.2" }
solana-sdk = { path = "../sdk", version = "1.5.2" }
tarpc = { version = "0.23.0", features = ["full"] }
tokio = { version = "0.3.5", features = ["full"] }
tokio-serde = { version = "0.6", features = ["bincode"] }
[dev-dependencies]
solana-runtime = { path = "../runtime", version = "=1.7.2" }
solana-banks-server = { path = "../banks-server", version = "=1.7.2" }
solana-runtime = { path = "../runtime", version = "1.5.2" }
solana-banks-server = { path = "../banks-server", version = "1.5.2" }
[lib]
crate-type = ["lib"]

View File

@@ -5,18 +5,19 @@
//! but they are undocumented, may change over time, and are generally more
//! cumbersome to use.
use borsh::BorshDeserialize;
use futures::{future::join_all, Future, FutureExt};
pub use solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus};
use solana_banks_interface::{BanksRequest, BanksResponse};
use solana_program::{
clock::Slot, fee_calculator::FeeCalculator, hash::Hash, program_pack::Pack, pubkey::Pubkey,
rent::Rent, sysvar,
};
use solana_sdk::{
account::{from_account, Account},
clock::Slot,
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
rent::Rent,
signature::Signature,
sysvar,
transaction::{self, Transaction},
transport,
};
@@ -121,7 +122,7 @@ impl BanksClient {
pub fn get_fees(
&mut self,
) -> impl Future<Output = io::Result<(FeeCalculator, Hash, Slot)>> + '_ {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::default())
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::Root)
}
/// Return the cluster rent
@@ -129,7 +130,7 @@ impl BanksClient {
self.get_account(sysvar::rent::id()).map(|result| {
let rent_sysvar = result?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Rent sysvar not present"))?;
from_account::<Rent, _>(&rent_sysvar).ok_or_else(|| {
from_account::<Rent>(&rent_sysvar).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
})
})
@@ -195,7 +196,7 @@ impl BanksClient {
/// Return the most recent rooted slot height. All transactions at or below this height
/// are said to be finalized. The cluster will not fork to a higher slot height.
pub fn get_root_slot(&mut self) -> impl Future<Output = io::Result<Slot>> + '_ {
self.get_slot_with_context(context::current(), CommitmentLevel::default())
self.get_slot_with_context(context::current(), CommitmentLevel::Root)
}
/// Return the account at the given address at the slot corresponding to the given
@@ -217,33 +218,6 @@ impl BanksClient {
self.get_account_with_commitment(address, CommitmentLevel::default())
}
/// Return the unpacked account data at the given address
/// If the account is not found, an error is returned
pub fn get_packed_account_data<T: Pack>(
&mut self,
address: Pubkey,
) -> impl Future<Output = io::Result<T>> + '_ {
self.get_account(address).map(|result| {
let account =
result?.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Account not found"))?;
T::unpack_from_slice(&account.data)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Failed to deserialize account"))
})
}
/// Return the unpacked account data at the given address
/// If the account is not found, an error is returned
pub fn get_account_data_with_borsh<T: BorshDeserialize>(
&mut self,
address: Pubkey,
) -> impl Future<Output = io::Result<T>> + '_ {
self.get_account(address).map(|result| {
let account =
result?.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "account not found"))?;
T::try_from_slice(&account.data)
})
}
/// Return the balance in lamports of an account at the given address at the slot
/// corresponding to the given commitment level.
pub fn get_balance_with_commitment(
@@ -315,10 +289,7 @@ pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> io::Result<BanksClie
mod tests {
use super::*;
use solana_banks_server::banks_server::start_local_server;
use solana_runtime::{
bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache,
genesis_utils::create_genesis_config,
};
use solana_runtime::{bank::Bank, bank_forks::BankForks, genesis_utils::create_genesis_config};
use solana_sdk::{message::Message, signature::Signer, system_instruction};
use std::sync::{Arc, RwLock};
use tarpc::transport;
@@ -337,12 +308,9 @@ mod tests {
// `runtime.block_on()` just once, to run all the async code.
let genesis = create_genesis_config(10);
let bank = Bank::new(&genesis.genesis_config);
let slot = bank.slot();
let block_commitment_cache = Arc::new(RwLock::new(
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
));
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(
&genesis.genesis_config,
))));
let bob_pubkey = solana_sdk::pubkey::new_rand();
let mint_pubkey = genesis.mint_keypair.pubkey();
@@ -350,7 +318,7 @@ mod tests {
let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(bank_forks, block_commitment_cache).await;
let client_transport = start_local_server(&bank_forks).await;
let mut banks_client = start_client(client_transport).await?;
let recent_blockhash = banks_client.get_recent_blockhash().await?;
@@ -368,12 +336,9 @@ mod tests {
// server-side functionality is available to the client.
let genesis = create_genesis_config(10);
let bank = Bank::new(&genesis.genesis_config);
let slot = bank.slot();
let block_commitment_cache = Arc::new(RwLock::new(
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
));
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(
&genesis.genesis_config,
))));
let mint_pubkey = &genesis.mint_keypair.pubkey();
let bob_pubkey = solana_sdk::pubkey::new_rand();
@@ -381,7 +346,7 @@ mod tests {
let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(bank_forks, block_commitment_cache).await;
let client_transport = start_local_server(&bank_forks).await;
let mut banks_client = start_client(client_transport).await?;
let (_, recent_blockhash, last_valid_slot) = banks_client.get_fees().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);

View File

@@ -1,22 +1,21 @@
[package]
name = "solana-banks-interface"
version = "1.7.2"
version = "1.5.2"
description = "Solana banks RPC interface"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-banks-interface"
edition = "2018"
[dependencies]
mio = "0.7.6"
serde = { version = "1.0.122", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "=1.7.2" }
tarpc = { version = "0.24.1", features = ["full"] }
serde = { version = "1.0.112", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "1.5.2" }
tarpc = { version = "0.23.0", features = ["full"] }
[dev-dependencies]
tokio = { version = "1", features = ["full"] }
tokio = { version = "0.3.5", features = ["full"] }
[lib]
crate-type = ["lib"]

View File

@@ -10,19 +10,11 @@ use solana_sdk::{
transaction::{self, Transaction, TransactionError},
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TransactionConfirmationStatus {
Processed,
Confirmed,
Finalized,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TransactionStatus {
pub slot: Slot,
pub confirmations: Option<usize>, // None = rooted
pub err: Option<TransactionError>,
pub confirmation_status: Option<TransactionConfirmationStatus>,
}
#[tarpc::service]

View File

@@ -1,12 +1,11 @@
[package]
name = "solana-banks-server"
version = "1.7.2"
version = "1.5.2"
description = "Solana banks server"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-banks-server"
edition = "2018"
[dependencies]
@@ -14,14 +13,13 @@ bincode = "1.3.1"
futures = "0.3"
log = "0.4.11"
mio = "0.7.6"
solana-banks-interface = { path = "../banks-interface", version = "=1.7.2" }
solana-runtime = { path = "../runtime", version = "=1.7.2" }
solana-sdk = { path = "../sdk", version = "=1.7.2" }
solana-metrics = { path = "../metrics", version = "=1.7.2" }
tarpc = { version = "0.24.1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tokio-serde = { version = "0.8", features = ["bincode"] }
tokio-stream = "0.1"
solana-banks-interface = { path = "../banks-interface", version = "1.5.2" }
solana-runtime = { path = "../runtime", version = "1.5.2" }
solana-sdk = { path = "../sdk", version = "1.5.2" }
solana-metrics = { path = "../metrics", version = "1.5.2" }
tarpc = { version = "0.23.0", features = ["full"] }
tokio = { version = "0.3", features = ["full"] }
tokio-serde = { version = "0.6", features = ["bincode"] }
[lib]
crate-type = ["lib"]

View File

@@ -4,9 +4,7 @@ use futures::{
future,
prelude::stream::{self, StreamExt},
};
use solana_banks_interface::{
Banks, BanksRequest, BanksResponse, TransactionConfirmationStatus, TransactionStatus,
};
use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus};
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache};
use solana_sdk::{
account::Account,
@@ -62,7 +60,7 @@ impl BanksServer {
}
}
fn run(bank_forks: Arc<RwLock<BankForks>>, transaction_receiver: Receiver<TransactionInfo>) {
fn run(bank: &Bank, transaction_receiver: Receiver<TransactionInfo>) {
while let Ok(info) = transaction_receiver.recv() {
let mut transaction_infos = vec![info];
while let Ok(info) = transaction_receiver.try_recv() {
@@ -72,28 +70,21 @@ impl BanksServer {
.into_iter()
.map(|info| deserialize(&info.wire_transaction).unwrap())
.collect();
let bank = bank_forks.read().unwrap().working_bank();
let _ = bank.process_transactions(&transactions);
}
}
/// Useful for unit-testing
fn new_loopback(
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) -> Self {
fn new_loopback(bank_forks: Arc<RwLock<BankForks>>) -> Self {
let (transaction_sender, transaction_receiver) = channel();
let bank = bank_forks.read().unwrap().working_bank();
let slot = bank.slot();
{
// ensure that the commitment cache and bank are synced
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
w_block_commitment_cache.set_all_slots(slot, slot);
}
let server_bank_forks = bank_forks.clone();
let block_commitment_cache = Arc::new(RwLock::new(
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
));
Builder::new()
.name("solana-bank-forks-client".to_string())
.spawn(move || Self::run(server_bank_forks, transaction_receiver))
.spawn(move || Self::run(&bank, transaction_receiver))
.unwrap();
Self::new(bank_forks, block_commitment_cache, transaction_sender)
}
@@ -174,17 +165,11 @@ impl Banks for BanksServer {
_: Context,
signature: Signature,
) -> Option<TransactionStatus> {
let bank = self.bank(CommitmentLevel::Processed);
let bank = self.bank(CommitmentLevel::Recent);
let (slot, status) = bank.get_signature_status_slot(&signature)?;
let r_block_commitment_cache = self.block_commitment_cache.read().unwrap();
let optimistically_confirmed_bank = self.bank(CommitmentLevel::Confirmed);
let optimistically_confirmed =
optimistically_confirmed_bank.get_signature_status_slot(&signature);
let confirmations = if r_block_commitment_cache.root() >= slot
&& r_block_commitment_cache.highest_confirmed_root() >= slot
{
let confirmations = if r_block_commitment_cache.root() >= slot {
None
} else {
r_block_commitment_cache
@@ -195,13 +180,6 @@ impl Banks for BanksServer {
slot,
confirmations,
err: status.err(),
confirmation_status: if confirmations.is_none() {
Some(TransactionConfirmationStatus::Finalized)
} else if optimistically_confirmed.is_some() {
Some(TransactionConfirmationStatus::Confirmed)
} else {
Some(TransactionConfirmationStatus::Processed)
},
})
}
@@ -242,15 +220,14 @@ impl Banks for BanksServer {
commitment: CommitmentLevel,
) -> Option<Account> {
let bank = self.bank(commitment);
bank.get_account(&address).map(Account::from)
bank.get_account(&address)
}
}
pub async fn start_local_server(
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
bank_forks: &Arc<RwLock<BankForks>>,
) -> UnboundedChannel<Response<BanksResponse>, ClientMessage<BanksRequest>> {
let banks_server = BanksServer::new_loopback(bank_forks, block_commitment_cache);
let banks_server = BanksServer::new_loopback(bank_forks.clone());
let (client_transport, server_transport) = transport::channel::unbounded();
let server = server::new(server::Config::default())
.incoming(stream::once(future::ready(server_transport)))

View File

@@ -1,4 +1,3 @@
#![allow(clippy::integer_arithmetic)]
pub mod banks_server;
pub mod rpc_banks_service;
pub mod send_transaction_service;

View File

@@ -15,7 +15,6 @@ use tokio::{
runtime::Runtime,
time::{self, Duration},
};
use tokio_stream::wrappers::IntervalStream;
pub struct RpcBanksService {
thread_hdl: JoinHandle<()>,
@@ -36,7 +35,7 @@ async fn start_abortable_tcp_server(
block_commitment_cache.clone(),
)
.fuse();
let interval = IntervalStream::new(time::interval(Duration::from_millis(100))).fuse();
let interval = time::interval(Duration::from_millis(100)).fuse();
pin_mut!(server, interval);
loop {
select! {

View File

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

View File

@@ -1,5 +1,4 @@
#![allow(clippy::useless_attribute)]
#![allow(clippy::integer_arithmetic)]
use crate::order_book::*;
use itertools::izip;
@@ -391,7 +390,7 @@ fn swapper<T>(
while client
.get_balance_with_commitment(
&trade_infos[trade_index].trade_account,
CommitmentConfig::processed(),
CommitmentConfig::recent(),
)
.unwrap_or(0)
== 0
@@ -446,7 +445,7 @@ fn swapper<T>(
account_group = (account_group + 1) % account_groups as usize;
let (blockhash, _fee_calculator, _last_valid_slot) = client
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.expect("Failed to get blockhash");
let to_swap_txs: Vec<_> = to_swap
.par_iter()
@@ -572,7 +571,7 @@ fn trader<T>(
account_group = (account_group + 1) % account_groups as usize;
let (blockhash, _fee_calculator, _last_valid_slot) = client
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.expect("Failed to get blockhash");
trades.chunks(chunk_size).for_each(|chunk| {
@@ -659,7 +658,7 @@ where
{
for s in &tx.signatures {
if let Ok(Some(r)) =
sync_client.get_signature_status_with_commitment(s, CommitmentConfig::processed())
sync_client.get_signature_status_with_commitment(s, CommitmentConfig::recent())
{
match r {
Ok(_) => {
@@ -682,7 +681,7 @@ fn verify_funding_transfer<T: SyncClient + ?Sized>(
if verify_transaction(client, tx) {
for a in &tx.message().account_keys[1..] {
if client
.get_balance_with_commitment(a, CommitmentConfig::processed())
.get_balance_with_commitment(a, CommitmentConfig::recent())
.unwrap_or(0)
>= amount
{
@@ -765,7 +764,7 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
);
let (blockhash, _fee_calculator, _last_valid_slot) = client
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.expect("blockhash");
to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
tx.sign(&[*k], blockhash);
@@ -804,7 +803,7 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
funded.append(&mut new_funded);
funded.retain(|(k, b)| {
client
.get_balance_with_commitment(&k.pubkey(), CommitmentConfig::processed())
.get_balance_with_commitment(&k.pubkey(), CommitmentConfig::recent())
.unwrap_or(0)
> lamports
&& *b > lamports
@@ -858,7 +857,7 @@ pub fn create_token_accounts<T: Client>(
let mut retries = 0;
while !to_create_txs.is_empty() {
let (blockhash, _fee_calculator, _last_valid_slot) = client
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.expect("Failed to get blockhash");
to_create_txs
.par_iter_mut()
@@ -904,7 +903,7 @@ pub fn create_token_accounts<T: Client>(
let mut new_notfunded: Vec<(&Arc<Keypair>, &Keypair)> = vec![];
for f in &notfunded {
if client
.get_balance_with_commitment(&f.1.pubkey(), CommitmentConfig::processed())
.get_balance_with_commitment(&f.1.pubkey(), CommitmentConfig::recent())
.unwrap_or(0)
== 0
{
@@ -969,7 +968,7 @@ pub fn airdrop_lamports<T: Client>(
id: &Keypair,
amount: u64,
) {
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::processed());
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
let balance = balance.unwrap_or(0);
if balance >= amount {
return;
@@ -987,7 +986,7 @@ pub fn airdrop_lamports<T: Client>(
let mut tries = 0;
loop {
let (blockhash, _fee_calculator, _last_valid_slot) = client
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.expect("Failed to get blockhash");
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
Ok(transaction) => {
@@ -996,14 +995,14 @@ pub fn airdrop_lamports<T: Client>(
for _ in 0..30 {
if let Ok(Some(_)) = client.get_signature_status_with_commitment(
&signature,
CommitmentConfig::processed(),
CommitmentConfig::recent(),
) {
break;
}
sleep(Duration::from_millis(100));
}
if client
.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::processed())
.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent())
.unwrap_or(0)
>= amount
{

View File

@@ -1,11 +1,10 @@
#![allow(clippy::integer_arithmetic)]
pub mod bench;
mod cli;
pub mod order_book;
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
use log::*;
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana_sdk::signature::Signer;
fn main() {

View File

@@ -1,21 +1,19 @@
use log::*;
use solana_bench_exchange::bench::{airdrop_lamports, do_bench_exchange, Config};
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana_core::validator::ValidatorConfig;
use solana_exchange_program::{
exchange_processor::process_instruction, id, solana_exchange_program,
};
use solana_faucet::faucet::run_local_faucet_with_port;
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
use solana_local_cluster::{
local_cluster::{ClusterConfig, LocalCluster},
validator_configs::make_identical_validator_configs,
};
use solana_runtime::{bank::Bank, bank_client::BankClient};
use solana_sdk::{
genesis_config::create_genesis_config,
signature::{Keypair, Signer},
};
use std::{process::exit, sync::mpsc::channel, time::Duration};
use solana_exchange_program::exchange_processor::process_instruction;
use solana_exchange_program::id;
use solana_exchange_program::solana_exchange_program;
use solana_faucet::faucet::run_local_faucet;
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, Signer};
use std::process::exit;
use std::sync::mpsc::channel;
use std::time::Duration;
#[test]
#[ignore]
@@ -46,7 +44,7 @@ fn test_exchange_local_cluster() {
let cluster = LocalCluster::new(&mut ClusterConfig {
node_stakes: vec![100_000; NUM_NODES],
cluster_lamports: 100_000_000_000_000,
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
native_instruction_processors: [solana_exchange_program!()].to_vec(),
..ClusterConfig::default()
});
@@ -59,11 +57,8 @@ fn test_exchange_local_cluster() {
);
let (addr_sender, addr_receiver) = channel();
run_local_faucet_with_port(faucet_keypair, addr_sender, Some(1_000_000_000_000), 0);
let faucet_addr = addr_receiver
.recv_timeout(Duration::from_secs(2))
.expect("run_local_faucet")
.expect("faucet_addr");
run_local_faucet(faucet_keypair, addr_sender, Some(1_000_000_000_000));
let faucet_addr = addr_receiver.recv_timeout(Duration::from_secs(2)).unwrap();
info!("Connecting to the cluster");
let nodes =

View File

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

View File

@@ -1,4 +1,3 @@
#![allow(clippy::integer_arithmetic)]
use clap::{crate_description, crate_name, App, Arg};
use solana_streamer::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
use solana_streamer::streamer::{receiver, PacketReceiver};
@@ -91,8 +90,6 @@ fn main() -> Result<()> {
s_reader,
recycler.clone(),
"bench-streamer-test",
1,
true,
));
}

View File

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

View File

@@ -8,7 +8,7 @@ use solana_measure::measure::Measure;
use solana_metrics::{self, datapoint_info};
use solana_sdk::{
client::Client,
clock::{DEFAULT_S_PER_SLOT, MAX_PROCESSING_AGE},
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
commitment_config::CommitmentConfig,
fee_calculator::FeeCalculator,
hash::Hash,
@@ -32,7 +32,8 @@ use std::{
};
// The point at which transactions become "too old", in seconds.
const MAX_TX_QUEUE_AGE: u64 = (MAX_PROCESSING_AGE as f64 * DEFAULT_S_PER_SLOT) as u64;
const MAX_TX_QUEUE_AGE: u64 =
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND;
pub const MAX_SPENDS_PER_TX: u64 = 4;
@@ -47,7 +48,7 @@ pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<(Transaction, u64)>>>>;
fn get_recent_blockhash<T: Client>(client: &T) -> (Hash, FeeCalculator) {
loop {
match client.get_recent_blockhash_with_commitment(CommitmentConfig::processed()) {
match client.get_recent_blockhash_with_commitment(CommitmentConfig::recent()) {
Ok((blockhash, fee_calculator, _last_valid_slot)) => {
return (blockhash, fee_calculator)
}
@@ -496,7 +497,7 @@ fn do_tx_transfers<T: Client>(
fn verify_funding_transfer<T: Client>(client: &Arc<T>, tx: &Transaction, amount: u64) -> bool {
for a in &tx.message().account_keys[1..] {
match client.get_balance_with_commitment(a, CommitmentConfig::processed()) {
match client.get_balance_with_commitment(a, CommitmentConfig::recent()) {
Ok(balance) => return balance >= amount,
Err(err) => error!("failed to get balance {:?}", err),
}
@@ -761,7 +762,7 @@ pub fn airdrop_lamports<T: Client>(
};
let current_balance = client
.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::processed())
.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent())
.unwrap_or_else(|e| {
info!("airdrop error {}", e);
starting_balance
@@ -966,7 +967,7 @@ mod tests {
for kp in &keypairs {
assert_eq!(
client
.get_balance_with_commitment(&kp.pubkey(), CommitmentConfig::processed())
.get_balance_with_commitment(&kp.pubkey(), CommitmentConfig::recent())
.unwrap(),
lamports
);

View File

@@ -1,3 +1,2 @@
#![allow(clippy::integer_arithmetic)]
pub mod bench;
pub mod cli;

View File

@@ -1,9 +1,8 @@
#![allow(clippy::integer_arithmetic)]
use log::*;
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate_keypairs};
use solana_bench_tps::cli;
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
use solana_genesis::Base64Account;
use solana_gossip::gossip_service::{discover_cluster, get_client, get_multi_client};
use solana_sdk::fee_calculator::FeeRateGovernor;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_program;

View File

@@ -1,22 +1,14 @@
#![allow(clippy::integer_arithmetic)]
use serial_test::serial;
use solana_bench_tps::{
bench::{do_bench_tps, generate_and_fund_keypairs},
cli::Config,
};
use serial_test_derive::serial;
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs};
use solana_bench_tps::cli::Config;
use solana_client::thin_client::create_client;
use solana_core::cluster_info::VALIDATOR_PORT_RANGE;
use solana_core::validator::ValidatorConfig;
use solana_faucet::faucet::run_local_faucet_with_port;
use solana_gossip::cluster_info::VALIDATOR_PORT_RANGE;
use solana_local_cluster::{
local_cluster::{ClusterConfig, LocalCluster},
validator_configs::make_identical_validator_configs,
};
use solana_faucet::faucet::run_local_faucet;
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
use solana_sdk::signature::{Keypair, Signer};
use std::{
sync::{mpsc::channel, Arc},
time::Duration,
};
use std::sync::{mpsc::channel, Arc};
use std::time::Duration;
fn test_bench_tps_local_cluster(config: Config) {
let native_instruction_processors = vec![];
@@ -26,7 +18,7 @@ fn test_bench_tps_local_cluster(config: Config) {
let cluster = LocalCluster::new(&mut ClusterConfig {
node_stakes: vec![999_990; NUM_NODES],
cluster_lamports: 200_000_000,
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
native_instruction_processors,
..ClusterConfig::default()
});
@@ -44,11 +36,8 @@ fn test_bench_tps_local_cluster(config: Config) {
));
let (addr_sender, addr_receiver) = channel();
run_local_faucet_with_port(faucet_keypair, addr_sender, None, 0);
let faucet_addr = addr_receiver
.recv_timeout(Duration::from_secs(2))
.expect("run_local_faucet")
.expect("faucet_addr");
run_local_faucet(faucet_keypair, addr_sender, None);
let faucet_addr = addr_receiver.recv_timeout(Duration::from_secs(2)).unwrap();
let lamports_per_account = 100;

9
cargo
View File

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

View File

@@ -5,6 +5,9 @@ steps:
- command: "ci/publish-tarball.sh"
timeout_in_minutes: 60
name: "publish tarball"
- command: "ci/publish-bpf-sdk.sh"
timeout_in_minutes: 5
name: "publish bpf sdk"
- wait
- command: "sdk/docker-solana/build.sh"
timeout_in_minutes: 60

View File

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

View File

@@ -1,43 +0,0 @@
#!/usr/bin/env bash
set -e
here="$(dirname "$0")"
src_root="$(readlink -f "${here}/..")"
cd "${src_root}"
cargo_audit_ignores=(
# failure is officially deprecated/unmaintained
#
# Blocked on multiple upstream crates removing their `failure` dependency.
--ignore RUSTSEC-2020-0036
# `net2` crate has been deprecated; use `socket2` instead
#
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
--ignore RUSTSEC-2020-0016
# stdweb is unmaintained
#
# Blocked on multiple upstream crates removing their `stdweb` dependency.
--ignore RUSTSEC-2020-0056
# Potential segfault in the time crate
#
# Blocked on multiple crates updating `time` to >= 0.2.23
--ignore RUSTSEC-2020-0071
# difference is unmaintained
#
# Blocked on predicates v1.0.6 removing its dependency on `difference`
--ignore RUSTSEC-2020-0095
# generic-array: arr! macro erases lifetimes
#
# Blocked on libsecp256k1 releasing with upgraded dependencies
# https://github.com/paritytech/libsecp256k1/issues/66
--ignore RUSTSEC-2020-0146
)
scripts/cargo-for-all-lock-files.sh stable audit "${cargo_audit_ignores[@]}"

View File

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

View File

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

View File

@@ -70,7 +70,7 @@ done
source ci/upload-ci-artifact.sh
source scripts/configure-metrics.sh
source multinode-demo/common.sh --prebuild
source multinode-demo/common.sh
nodes=(
"multinode-demo/bootstrap-validator.sh \
@@ -78,6 +78,7 @@ nodes=(
--init-complete-file init-complete-node0.log \
--dynamic-port-range 8000-8050"
"multinode-demo/validator.sh \
--enable-rpc-exit \
--no-restart \
--dynamic-port-range 8050-8100
--init-complete-file init-complete-node1.log \
@@ -200,10 +201,17 @@ killNodes() {
[[ ${#pids[@]} -gt 0 ]] || return
# Try to use the RPC exit API to cleanly exit the first two nodes
# (dynamic nodes, -x, are just killed)
# (dynamic nodes, -x, are just killed since their RPC port is not known)
echo "--- RPC exit"
$solana_validator --ledger "$SOLANA_CONFIG_DIR"/bootstrap-validator exit --force || true
$solana_validator --ledger "$SOLANA_CONFIG_DIR"/validator exit --force || true
for port in 8899 18899; do
(
set -x
curl --retry 5 --retry-delay 2 --retry-connrefused \
-X POST -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' \
http://localhost:$port
)
done
# Give the nodes a splash of time to cleanly exit before killing them
sleep 2

View File

@@ -19,7 +19,6 @@ declare prints=(
# Parts of the tree that are expected to be print free
declare print_free_tree=(
':core/src/**.rs'
':^core/src/validator.rs'
':faucet/src/**.rs'
':ledger/src/**.rs'
':metrics/src/**.rs'

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,16 +12,6 @@ cargo="$(readlink -f "./cargo")"
scripts/increment-cargo-version.sh check
# Disallow uncommitted Cargo.lock changes
(
_ scripts/cargo-for-all-lock-files.sh tree
set +e
if ! _ git diff --exit-code; then
echo -e "\nError: Uncommitted Cargo.lock changes" 1>&2
exit 1
fi
)
echo --- build environment
(
set -x
@@ -45,7 +35,7 @@ export RUSTFLAGS="-D warnings -A incomplete_features"
# Only force up-to-date lock files on edge
if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
# Exclude --benches as it's not available in rust stable yet
if _ scripts/cargo-for-all-lock-files.sh stable check --locked --tests --bins --examples; then
if _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" check --locked --tests --bins --examples; then
true
else
check_status=$?
@@ -56,29 +46,57 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
fi
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh nightly check --locked --all-targets
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
else
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
fi
_ ci/order-crates-for-publishing.py
_ "$cargo" stable fmt --all -- --check
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
_ "$cargo" stable fmt --all -- --check
cargo_audit_ignores=(
# failure is officially deprecated/unmaintained
#
# Blocked on multiple upstream crates removing their `failure` dependency.
--ignore RUSTSEC-2020-0036
_ ci/do-audit.sh
# `net2` crate has been deprecated; use `socket2` instead
#
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
--ignore RUSTSEC-2020-0016
# stdweb is unmaintained
#
# Blocked on multiple upstream crates removing their `stdweb` dependency.
--ignore RUSTSEC-2020-0056
# Potential segfault in the time crate
#
# Blocked on multiple crates updating `time` to >= 0.2.23
--ignore RUSTSEC-2020-0071
# difference is unmaintained
#
# Blocked on predicates v1.0.6 removing its dependency on `difference`
--ignore RUSTSEC-2020-0095
)
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
{
cd programs/bpf
_ "$cargo" stable audit
for project in rust/*/ ; do
echo "+++ do_bpf_checks $project"
(
cd "$project"
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
_ "$cargo" stable fmt -- --check
_ "$cargo" nightly test
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
)
done
}

View File

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

View File

@@ -25,6 +25,9 @@ source scripts/ulimit-n.sh
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# Clear the BPF sysroot files, they are not automatically rebuilt
rm -rf target/xargo # Issue #3105
# Limit compiler jobs to reduce memory usage
# on machines with 2gb/thread of memory
NPROC=$(nproc)
@@ -36,18 +39,10 @@ test-stable)
_ "$cargo" stable test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
;;
test-stable-perf)
# solana-keygen required when building C programs
_ "$cargo" build --manifest-path=keygen/Cargo.toml
export PATH="$PWD/target/debug":$PATH
# BPF solana-sdk legacy compile test
./cargo-build-bpf --manifest-path sdk/Cargo.toml
# BPF Program unit tests
"$cargo" test --manifest-path programs/bpf/Cargo.toml
cargo-build-bpf --manifest-path programs/bpf/Cargo.toml --bpf-sdk sdk/bpf
# BPF program system tests
# BPF program tests
_ make -C programs/bpf/c tests
_ "$cargo" stable test \
--manifest-path programs/bpf/Cargo.toml \

View File

@@ -1,28 +1,23 @@
[package]
name = "solana-clap-utils"
version = "1.7.2"
version = "1.5.2"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-clap-utils"
edition = "2018"
[dependencies]
clap = "2.33.0"
rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.2" }
solana-sdk = { path = "../sdk", version = "=1.7.2" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.2" }
solana-sdk = { path = "../sdk", version = "1.5.2" }
thiserror = "1.0.21"
tiny-bip39 = "0.8.0"
uriparse = "0.6.3"
tiny-bip39 = "0.7.0"
url = "2.1.0"
chrono = "0.4"
[dev-dependencies]
tempfile = "3.1.0"
[lib]
name = "solana_clap_utils"

View File

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

View File

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

View File

@@ -1,24 +1,19 @@
use {
crate::keypair::{
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
},
chrono::DateTime,
clap::ArgMatches,
solana_remote_wallet::remote_wallet::RemoteWalletManager,
solana_sdk::{
clock::UnixTimestamp,
commitment_config::CommitmentConfig,
genesis_config::ClusterType,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer},
},
std::{str::FromStr, sync::Arc},
use crate::keypair::{
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
};
// Sentinel value used to indicate to write to screen instead of file
pub const STDOUT_OUTFILE_TOKEN: &str = "-";
use chrono::DateTime;
use clap::ArgMatches;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
clock::UnixTimestamp,
commitment_config::CommitmentConfig,
genesis_config::ClusterType,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer},
};
use std::{str::FromStr, sync::Arc};
// Return parsed values from matches at `name`
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
@@ -60,7 +55,7 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
if let Some(value) = matches.value_of(name) {
if value == ASK_KEYWORD {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
keypair_from_seed_phrase(name, skip_validation, true).ok()
} else {
read_keypair_file(value).ok()
}
@@ -75,7 +70,7 @@ pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>>
.filter_map(|value| {
if value == ASK_KEYWORD {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
keypair_from_seed_phrase(name, skip_validation, true).ok()
} else {
read_keypair_file(value).ok()
}
@@ -172,12 +167,12 @@ pub fn resolve_signer(
name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
resolve_signer_from_path(
Ok(resolve_signer_from_path(
matches,
matches.value_of(name).unwrap(),
name,
wallet_manager,
)
)?)
}
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
@@ -189,9 +184,14 @@ pub fn cluster_type_of(matches: &ArgMatches<'_>, name: &str) -> Option<ClusterTy
}
pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
matches
.value_of(name)
.map(|value| CommitmentConfig::from_str(value).unwrap_or_default())
matches.value_of(name).map(|value| match value {
"max" => CommitmentConfig::max(),
"recent" => CommitmentConfig::recent(),
"root" => CommitmentConfig::root(),
"single" => CommitmentConfig::single(),
"singleGossip" => CommitmentConfig::single_gossip(),
_ => CommitmentConfig::default(),
})
}
#[cfg(test)]

View File

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

View File

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

View File

@@ -23,10 +23,10 @@ impl std::fmt::Debug for DisplayError {
}
}
pub mod commitment;
pub mod fee_payer;
pub mod input_parsers;
pub mod input_validators;
pub mod keypair;
pub mod memo;
pub mod nonce;
pub mod offline;

View File

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

View File

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

View File

@@ -1,7 +1,5 @@
use {
crate::{input_validators::*, ArgConstant},
clap::{App, Arg},
};
use crate::{input_validators::*, ArgConstant};
use clap::{App, Arg};
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash",
@@ -21,12 +19,6 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
help: "Provide a public-key/signature pair for the transaction",
};
pub const DUMP_TRANSACTION_MESSAGE: ArgConstant<'static> = ArgConstant {
name: "dump_transaction_message",
long: "dump-transaction-message",
help: "Display the base64 encoded binary transaction message in sign-only mode",
};
pub fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(BLOCKHASH_ARG.name)
.long(BLOCKHASH_ARG.long)
@@ -55,14 +47,6 @@ fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
.help(SIGNER_ARG.help)
}
pub fn dump_transaction_message<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(DUMP_TRANSACTION_MESSAGE.name)
.long(DUMP_TRANSACTION_MESSAGE.long)
.takes_value(false)
.requires(SIGN_ONLY_ARG.name)
.help(DUMP_TRANSACTION_MESSAGE.help)
}
pub trait ArgsConfig {
fn blockhash_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
arg
@@ -73,9 +57,6 @@ pub trait ArgsConfig {
fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
arg
}
fn dump_transaction_message_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
arg
}
}
pub trait OfflineArgs {
@@ -88,7 +69,6 @@ impl OfflineArgs for App<'_, '_> {
self.arg(config.blockhash_arg(blockhash_arg()))
.arg(config.sign_only_arg(sign_only_arg()))
.arg(config.signer_arg(signer_arg()))
.arg(config.dump_transaction_message_arg(dump_transaction_message()))
}
fn offline_args(self) -> Self {
struct NullArgsConfig {}

View File

@@ -3,16 +3,15 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale"
version = "1.7.2"
version = "1.5.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-cli-config"
[dependencies]
dirs-next = "2.0.0"
lazy_static = "1.4.0"
serde = "1.0.122"
serde = "1.0.112"
serde_derive = "1.0.103"
serde_yaml = "0.8.13"
url = "2.1.1"

View File

@@ -17,10 +17,9 @@ pub struct Config {
pub json_rpc_url: String,
pub websocket_url: String,
pub keypair_path: String,
#[serde(default)]
pub address_labels: HashMap<String, String>,
#[serde(default)]
pub commitment: String,
}
impl Default for Config {
@@ -42,14 +41,11 @@ impl Default for Config {
"System Program".to_string(),
);
let commitment = "confirmed".to_string();
Self {
json_rpc_url,
websocket_url,
keypair_path,
address_labels,
commitment,
}
}
}
@@ -75,8 +71,7 @@ impl Config {
.set_scheme(if is_secure { "wss" } else { "ws" })
.expect("unable to set scheme");
if let Some(port) = json_rpc_url.port() {
let port = port.checked_add(1).expect("port out of range");
ws_url.set_port(Some(port)).expect("unable to set port");
ws_url.set_port(Some(port + 1)).expect("unable to set port");
}
ws_url.to_string()
}
@@ -107,13 +102,13 @@ mod test {
#[test]
fn compute_websocket_url() {
assert_eq!(
Config::compute_websocket_url(&"http://api.devnet.solana.com"),
"ws://api.devnet.solana.com/".to_string()
Config::compute_websocket_url(&"http://devnet.solana.com"),
"ws://devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://api.devnet.solana.com"),
"wss://api.devnet.solana.com/".to_string()
Config::compute_websocket_url(&"https://devnet.solana.com"),
"wss://devnet.solana.com/".to_string()
);
assert_eq!(

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +1,28 @@
use {
crate::cli_output::CliSignatureVerificationStatus,
chrono::{DateTime, Local, NaiveDateTime, SecondsFormat, TimeZone, Utc},
console::style,
indicatif::{ProgressBar, ProgressStyle},
solana_sdk::{
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
program_utils::limited_deserialize, pubkey::Pubkey, stake, transaction::Transaction,
},
solana_transaction_status::UiTransactionStatusMeta,
spl_memo::id as spl_memo_id,
spl_memo::v1::id as spl_memo_v1_id,
std::{collections::HashMap, fmt, io},
use console::style;
use indicatif::{ProgressBar, ProgressStyle};
use solana_sdk::{
hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize,
transaction::Transaction,
};
use solana_transaction_status::UiTransactionStatusMeta;
use std::{collections::HashMap, fmt, io};
#[derive(Clone, Debug)]
pub struct BuildBalanceMessageConfig {
pub use_lamports_unit: bool,
pub show_unit: bool,
pub trim_trailing_zeros: bool,
}
impl Default for BuildBalanceMessageConfig {
fn default() -> Self {
Self {
use_lamports_unit: false,
show_unit: true,
trim_trailing_zeros: true,
}
}
}
fn is_memo_program(k: &Pubkey) -> bool {
let k_str = k.to_string();
(k_str == spl_memo_v1_id().to_string()) || (k_str == spl_memo_id().to_string())
}
pub fn build_balance_message_with_config(
lamports: u64,
config: &BuildBalanceMessageConfig,
) -> String {
let value = if config.use_lamports_unit {
lamports.to_string()
pub fn build_balance_message(lamports: u64, use_lamports_unit: bool, show_unit: bool) -> String {
if use_lamports_unit {
let ess = if lamports == 1 { "" } else { "s" };
let unit = if show_unit {
format!(" lamport{}", ess)
} else {
"".to_string()
};
format!("{:?}{}", lamports, unit)
} else {
let sol = lamports_to_sol(lamports);
let sol_str = format!("{:.9}", sol);
if config.trim_trailing_zeros {
sol_str
.trim_end_matches('0')
.trim_end_matches('.')
.to_string()
} else {
sol_str
}
};
let unit = if config.show_unit {
if config.use_lamports_unit {
let ess = if lamports == 1 { "" } else { "s" };
format!(" lamport{}", ess)
} else {
" SOL".to_string()
}
} else {
"".to_string()
};
format!("{}{}", value, unit)
}
pub fn build_balance_message(lamports: u64, use_lamports_unit: bool, show_unit: bool) -> String {
build_balance_message_with_config(
lamports,
&BuildBalanceMessageConfig {
use_lamports_unit,
show_unit,
..BuildBalanceMessageConfig::default()
},
)
let pretty_sol = sol_str.trim_end_matches('0').trim_end_matches('.');
let unit = if show_unit { " SOL" } else { "" };
format!("{}{}", pretty_sol, unit)
}
}
// Pretty print a "name value"
@@ -87,7 +35,7 @@ pub fn println_name_value(name: &str, value: &str) {
println!("{} {}", style(name).bold(), styled_value);
}
pub fn writeln_name_value(f: &mut dyn fmt::Write, name: &str, value: &str) -> fmt::Result {
pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fmt::Result {
let styled_value = if value.is_empty() {
style("(not set)").italic()
} else {
@@ -132,98 +80,35 @@ pub fn println_signers(
println!();
}
fn format_account_mode(message: &Message, index: usize) -> String {
format!(
"{}r{}{}", // accounts are always readable...
if message.is_signer(index) {
"s" // stands for signer
} else {
"-"
},
if message.is_writable(index, /*demote_sysvar_write_locks=*/ true) {
"w" // comment for consistent rust fmt (no joking; lol)
} else {
"-"
},
// account may be executable on-chain while not being
// designated as a program-id in the message
if message.maybe_executable(index) {
"x"
} else {
// programs to be executed via CPI cannot be identified as
// executable from the message
"-"
},
)
}
pub fn write_transaction<W: io::Write>(
w: &mut W,
transaction: &Transaction,
transaction_status: &Option<UiTransactionStatusMeta>,
prefix: &str,
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
block_time: Option<UnixTimestamp>,
) -> io::Result<()> {
let message = &transaction.message;
if let Some(block_time) = block_time {
writeln!(
w,
"{}Block Time: {:?}",
prefix,
Local.timestamp(block_time, 0)
)?;
}
writeln!(
w,
"{}Recent Blockhash: {:?}",
prefix, message.recent_blockhash
)?;
let sigverify_statuses = if let Some(sigverify_status) = sigverify_status {
sigverify_status
.iter()
.map(|s| format!(" ({})", s))
.collect()
} else {
vec!["".to_string(); transaction.signatures.len()]
};
for (signature_index, (signature, sigverify_status)) in transaction
.signatures
.iter()
.zip(&sigverify_statuses)
.enumerate()
{
for (signature_index, signature) in transaction.signatures.iter().enumerate() {
writeln!(
w,
"{}Signature {}: {:?}{}",
prefix, signature_index, signature, sigverify_status,
"{}Signature {}: {:?}",
prefix, signature_index, signature
)?;
}
let mut fee_payer_index = None;
writeln!(w, "{}{:?}", prefix, message.header)?;
for (account_index, account) in message.account_keys.iter().enumerate() {
if fee_payer_index.is_none() && message.is_non_loader_key(account, account_index) {
fee_payer_index = Some(account_index)
}
writeln!(
w,
"{}Account {}: {} {}{}",
prefix,
account_index,
format_account_mode(message, account_index),
account,
if Some(account_index) == fee_payer_index {
" (fee payer)"
} else {
""
},
)?;
writeln!(w, "{}Account {}: {:?}", prefix, account_index, account)?;
}
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let program_pubkey = message.account_keys[instruction.program_id_index as usize];
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
writeln!(
w,
"{} Program: {} ({})",
"{} Program: {} ({})",
prefix, program_pubkey, instruction.program_id_index
)?;
for (account_index, account) in instruction.accounts.iter().enumerate() {
@@ -244,9 +129,10 @@ pub fn write_transaction<W: io::Write>(
writeln!(w, "{} {:?}", prefix, vote_instruction)?;
raw = false;
}
} else if program_pubkey == stake::program::id() {
if let Ok(stake_instruction) =
limited_deserialize::<stake::instruction::StakeInstruction>(&instruction.data)
} else if program_pubkey == solana_stake_program::id() {
if let Ok(stake_instruction) = limited_deserialize::<
solana_stake_program::stake_instruction::StakeInstruction,
>(&instruction.data)
{
writeln!(w, "{} {:?}", prefix, stake_instruction)?;
raw = false;
@@ -259,11 +145,6 @@ pub fn write_transaction<W: io::Write>(
writeln!(w, "{} {:?}", prefix, system_instruction)?;
raw = false;
}
} else if is_memo_program(&program_pubkey) {
if let Ok(s) = std::str::from_utf8(&instruction.data) {
writeln!(w, "{} Data: \"{}\"", prefix, s)?;
raw = false;
}
}
if raw {
@@ -321,38 +202,7 @@ pub fn write_transaction<W: io::Write>(
if !log_messages.is_empty() {
writeln!(w, "{}Log Messages:", prefix,)?;
for log_message in log_messages {
writeln!(w, "{} {}", prefix, log_message)?;
}
}
}
if let Some(rewards) = &transaction_status.rewards {
if !rewards.is_empty() {
writeln!(w, "{}Rewards:", prefix,)?;
writeln!(
w,
"{} {:<44} {:^15} {:<15} {:<20}",
prefix, "Address", "Type", "Amount", "New Balance"
)?;
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
writeln!(
w,
"{} {:<44} {:^15} {:<15} {}",
prefix,
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!(
"{}{:<14.9}",
sign,
lamports_to_sol(reward.lamports.abs() as u64)
),
format!("{:<18.9}", lamports_to_sol(reward.post_balance),)
)?;
writeln!(w, "{} {}", prefix, log_message,)?;
}
}
}
@@ -367,52 +217,15 @@ pub fn println_transaction(
transaction: &Transaction,
transaction_status: &Option<UiTransactionStatusMeta>,
prefix: &str,
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
block_time: Option<UnixTimestamp>,
) {
let mut w = Vec::new();
if write_transaction(
&mut w,
transaction,
transaction_status,
prefix,
sigverify_status,
block_time,
)
.is_ok()
{
if write_transaction(&mut w, transaction, transaction_status, prefix).is_ok() {
if let Ok(s) = String::from_utf8(w) {
print!("{}", s);
}
}
}
pub fn writeln_transaction(
f: &mut dyn fmt::Write,
transaction: &Transaction,
transaction_status: &Option<UiTransactionStatusMeta>,
prefix: &str,
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
block_time: Option<UnixTimestamp>,
) -> fmt::Result {
let mut w = Vec::new();
if write_transaction(
&mut w,
transaction,
transaction_status,
prefix,
sigverify_status,
block_time,
)
.is_ok()
{
if let Ok(s) = String::from_utf8(w) {
write!(f, "{}", s)?;
}
}
Ok(())
}
/// Creates a new process bar for processing that will take an unknown amount of time
pub fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
@@ -422,13 +235,6 @@ pub fn new_spinner_progress_bar() -> ProgressBar {
progress_bar
}
pub fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
Some(ndt) => DateTime::<Utc>::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true),
None => format!("UnixTimestamp {}", unix_timestamp),
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@@ -1,4 +1,3 @@
#![allow(clippy::integer_arithmetic)]
mod cli_output;
pub mod display;
pub use cli_output::*;

View File

@@ -3,11 +3,10 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "1.7.2"
version = "1.5.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-cli"
[dependencies]
bincode = "1.3.1"
@@ -16,7 +15,7 @@ chrono = { version = "0.4.11", features = ["serde"] }
clap = "2.33.1"
criterion-stats = "0.3.0"
ctrlc = { version = "3.1.5", features = ["termination"] }
console = "0.14.1"
console = "0.11.3"
dirs-next = "2.0.0"
log = "0.4.11"
Inflector = "0.11.4"
@@ -24,33 +23,33 @@ indicatif = "0.15.0"
humantime = "2.0.1"
num-traits = "0.2"
pretty-hex = "0.2.1"
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde = "1.0.122"
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.2" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.2" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
solana-cli-config = { path = "../cli-config", version = "=1.7.2" }
solana-cli-output = { path = "../cli-output", version = "=1.7.2" }
solana-client = { path = "../client", version = "=1.7.2" }
solana-config-program = { path = "../programs/config", version = "=1.7.2" }
solana-faucet = { path = "../faucet", version = "=1.7.2" }
solana-logger = { path = "../logger", version = "=1.7.2" }
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
solana_rbpf = "=0.2.11"
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.2" }
solana-sdk = { path = "../sdk", version = "=1.7.2" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.2" }
solana-version = { path = "../version", version = "=1.7.2" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
solana-account-decoder = { path = "../account-decoder", version = "1.5.2" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.5.2" }
solana-clap-utils = { path = "../clap-utils", version = "1.5.2" }
solana-cli-config = { path = "../cli-config", version = "1.5.2" }
solana-cli-output = { path = "../cli-output", version = "1.5.2" }
solana-client = { path = "../client", version = "1.5.2" }
solana-config-program = { path = "../programs/config", version = "1.5.2" }
solana-faucet = { path = "../faucet", version = "1.5.2" }
solana-logger = { path = "../logger", version = "1.5.2" }
solana-net-utils = { path = "../net-utils", version = "1.5.2" }
solana_rbpf = "=0.2.2"
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.2" }
solana-sdk = { path = "../sdk", version = "1.5.2" }
solana-stake-program = { path = "../programs/stake", version = "1.5.2" }
solana-transaction-status = { path = "../transaction-status", version = "1.5.2" }
solana-version = { path = "../version", version = "1.5.2" }
solana-vote-program = { path = "../programs/vote", version = "1.5.2" }
thiserror = "1.0.21"
tiny-bip39 = "0.7.0"
url = "2.1.1"
[dev-dependencies]
solana-core = { path = "../core", version = "=1.7.2" }
solana-core = { path = "../core", version = "1.5.2" }
tempfile = "3.1.0"
[[bin]]

View File

@@ -86,13 +86,9 @@ pub fn check_account_for_spend_multiple_fees_with_commitment(
return Err(CliError::InsufficientFundsForSpendAndFee(
lamports_to_sol(balance),
lamports_to_sol(fee),
*account_pubkey,
));
} else {
return Err(CliError::InsufficientFundsForFee(
lamports_to_sol(fee),
*account_pubkey,
));
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
}
}
Ok(())
@@ -231,9 +227,18 @@ mod tests {
mocks.insert(RpcRequest::GetBalance, account_balance_response);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert!(check_account_for_balance(&rpc_client, &pubkey, 1).unwrap());
assert!(check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap());
assert!(!check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap());
assert_eq!(
check_account_for_balance(&rpc_client, &pubkey, 1).unwrap(),
true
);
assert_eq!(
check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap(),
true
);
assert_eq!(
check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap(),
false
);
}
#[test]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -19,22 +19,10 @@ use solana_sdk::{
};
use std::{collections::HashMap, fmt, sync::Arc};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ForceActivation {
No,
Almost,
Yes,
}
#[derive(Debug, PartialEq)]
pub enum FeatureCliCommand {
Status {
features: Vec<Pubkey>,
},
Activate {
feature: Pubkey,
force: ForceActivation,
},
Status { features: Vec<Pubkey> },
Activate { feature: Pubkey },
}
#[derive(Serialize, Deserialize)]
@@ -70,8 +58,8 @@ impl fmt::Display for CliFeatures {
f,
"{}",
style(format!(
"{:<44} | {:<27} | {}",
"Feature", "Status", "Description"
"{:<44} {:<40} {}",
"Feature", "Description", "Status"
))
.bold()
)?;
@@ -79,15 +67,15 @@ impl fmt::Display for CliFeatures {
for feature in &self.features {
writeln!(
f,
"{:<44} | {:<27} | {}",
"{:<44} {:<40} {}",
feature.id,
feature.description,
match feature.status {
CliFeatureStatus::Inactive => style("inactive".to_string()).red(),
CliFeatureStatus::Pending => style("activation pending".to_string()).yellow(),
CliFeatureStatus::Active(activation_slot) =>
style(format!("active since slot {}", activation_slot)).green(),
},
feature.description,
}
)?;
}
if self.inactive && !self.feature_activation_allowed {
@@ -138,13 +126,6 @@ impl FeatureSubCommands for App<'_, '_> {
.index(1)
.required(true)
.help("The signer for the feature to activate"),
)
.arg(
Arg::with_name("force")
.long("yolo")
.hidden(true)
.multiple(true)
.help("Override activation sanity checks. Don't use this flag"),
),
),
)
@@ -171,20 +152,13 @@ pub fn parse_feature_subcommand(
("activate", Some(matches)) => {
let (feature_signer, feature) = signer_of(matches, "feature", wallet_manager)?;
let mut signers = vec![default_signer.signer_from_path(matches, wallet_manager)?];
let force = match matches.occurrences_of("force") {
2 => ForceActivation::Yes,
1 => ForceActivation::Almost,
_ => ForceActivation::No,
};
signers.push(feature_signer.unwrap());
let feature = feature.unwrap();
known_feature(&feature)?;
CliCommandInfo {
command: CliCommand::Feature(FeatureCliCommand::Activate { feature, force }),
command: CliCommand::Feature(FeatureCliCommand::Activate { feature }),
signers,
}
}
@@ -215,13 +189,11 @@ pub fn process_feature_subcommand(
) -> ProcessResult {
match feature_subcommand {
FeatureCliCommand::Status { features } => process_status(rpc_client, config, features),
FeatureCliCommand::Activate { feature, force } => {
process_activate(rpc_client, config, *feature, *force)
}
FeatureCliCommand::Activate { feature } => process_activate(rpc_client, config, *feature),
}
}
fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f64>, ClientError> {
fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, u64>, ClientError> {
// Validator identity -> feature set
let feature_set_map = rpc_client
.get_cluster_nodes()?
@@ -239,7 +211,7 @@ fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f6
.sum();
// Sum all active stake by feature set
let mut active_stake_by_feature_set: HashMap<u32, u64> = HashMap::new();
let mut active_stake_by_feature_set = HashMap::new();
for vote_account in vote_accounts.current {
if let Some(Some(feature_set)) = feature_set_map.get(&vote_account.node_pubkey) {
*active_stake_by_feature_set.entry(*feature_set).or_default() +=
@@ -251,15 +223,11 @@ fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f6
}
}
Ok(active_stake_by_feature_set
.into_iter()
.map(|(feature_set, active_stake)| {
(
feature_set,
active_stake as f64 * 100. / total_active_stake as f64,
)
})
.collect())
// Convert active stake to a percentage so the caller doesn't need `total_active_stake`
for (_, val) in active_stake_by_feature_set.iter_mut() {
*val = *val * 100 / total_active_stake;
}
Ok(active_stake_by_feature_set)
}
// Feature activation is only allowed when 95% of the active stake is on the current feature set
@@ -270,7 +238,7 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
let feature_activation_allowed = active_stake_by_feature_set
.get(&my_feature_set)
.map(|percentage| *percentage >= 95.)
.map(|percentage| *percentage >= 95)
.unwrap_or(false);
if !feature_activation_allowed && !quiet {
@@ -287,15 +255,15 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
}
println!(
"{}",
style(format!("Tool Feature Set: {}", my_feature_set)).bold()
style(format!("Tool Feture Set: {}", my_feature_set)).bold()
);
println!("{}", style("Cluster Feature Sets and Stakes:").bold());
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
if *feature_set == 0 {
println!(" unknown - {:.2}%", percentage);
println!("unknown - {}%", percentage);
} else {
println!(
" {:<10} - {:.2}% {}",
"{} - {}% {}",
feature_set,
percentage,
if *feature_set == my_feature_set {
@@ -361,14 +329,12 @@ fn process_activate(
rpc_client: &RpcClient,
config: &CliConfig,
feature_id: Pubkey,
force: ForceActivation,
) -> ProcessResult {
let account = rpc_client
.get_multiple_accounts(&[feature_id])?
.into_iter()
.next()
.unwrap();
if let Some(account) = account {
if feature::from_account(&account).is_some() {
return Err(format!("{} has already been activated", feature_id).into());
@@ -376,13 +342,7 @@ fn process_activate(
}
if !feature_activation_allowed(rpc_client, false)? {
match force {
ForceActivation::Almost =>
return Err("Add force argument once more to override the sanity check to force feature activation ".into()),
ForceActivation::Yes => println!("FEATURE ACTIVATION FORCED"),
ForceActivation::No =>
return Err("Feature activation is not allowed at this time".into()),
}
return Err("Feature activation is not allowed at this time".into());
}
let rent = rpc_client.get_minimum_balance_for_rent_exemption(Feature::size_of())?;

View File

@@ -1,22 +1,14 @@
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
input_parsers::{pubkeys_of, value_of},
input_validators::is_valid_pubkey,
keypair::*,
};
use solana_cli_output::{
CliEpochRewardshMetadata, CliInflation, CliKeyedEpochReward, CliKeyedEpochRewards,
};
use clap::{App, ArgMatches, SubCommand};
use console::style;
use solana_clap_utils::keypair::*;
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
use std::sync::Arc;
#[derive(Debug, PartialEq)]
pub enum InflationCliCommand {
Show,
Rewards(Vec<Pubkey>, Option<Epoch>),
}
pub trait InflationSubCommands {
@@ -25,118 +17,73 @@ pub trait InflationSubCommands {
impl InflationSubCommands for App<'_, '_> {
fn inflation_subcommands(self) -> Self {
self.subcommand(
SubCommand::with_name("inflation")
.about("Show inflation information")
.subcommand(
SubCommand::with_name("rewards")
.about("Show inflation rewards for a set of addresses")
.arg(pubkey!(
Arg::with_name("addresses")
.value_name("ADDRESS")
.index(1)
.multiple(true)
.required(true),
"Address of account to query for rewards. "
))
.arg(
Arg::with_name("rewards_epoch")
.long("rewards-epoch")
.takes_value(true)
.value_name("EPOCH")
.help("Display rewards for specific epoch [default: latest epoch]"),
),
),
)
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
}
}
pub fn parse_inflation_subcommand(
matches: &ArgMatches<'_>,
_matches: &ArgMatches<'_>,
_default_signer: &DefaultSigner,
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let command = match matches.subcommand() {
("rewards", Some(matches)) => {
let addresses = pubkeys_of(matches, "addresses").unwrap();
let rewards_epoch = value_of(matches, "rewards_epoch");
InflationCliCommand::Rewards(addresses, rewards_epoch)
}
_ => InflationCliCommand::Show,
};
Ok(CliCommandInfo {
command: CliCommand::Inflation(command),
command: CliCommand::Inflation(InflationCliCommand::Show),
signers: vec![],
})
}
pub fn process_inflation_subcommand(
rpc_client: &RpcClient,
config: &CliConfig,
_config: &CliConfig,
inflation_subcommand: &InflationCliCommand,
) -> ProcessResult {
match inflation_subcommand {
InflationCliCommand::Show => process_show(rpc_client, config),
InflationCliCommand::Rewards(ref addresses, rewards_epoch) => {
process_rewards(rpc_client, config, addresses, *rewards_epoch)
}
}
}
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
let governor = rpc_client.get_inflation_governor()?;
let current_rate = rpc_client.get_inflation_rate()?;
let current_inflation_rate = rpc_client.get_inflation_rate()?;
let inflation = CliInflation {
governor,
current_rate,
};
Ok(config.output_format.formatted_string(&inflation))
}
fn process_rewards(
rpc_client: &RpcClient,
config: &CliConfig,
addresses: &[Pubkey],
rewards_epoch: Option<Epoch>,
) -> ProcessResult {
let rewards = rpc_client
.get_inflation_reward(&addresses, rewards_epoch)
.map_err(|err| {
if let Some(epoch) = rewards_epoch {
format!("Rewards not available for epoch {}", epoch)
} else {
format!("Rewards not available {}", err)
}
})?;
let epoch_schedule = rpc_client.get_epoch_schedule()?;
let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
let (epoch_start_time, epoch_end_time) =
crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
for (reward, address) in rewards.iter().zip(addresses) {
let cli_reward = reward.as_ref().and_then(|reward| {
crate::stake::make_cli_reward(reward, epoch_start_time, epoch_end_time)
});
epoch_rewards.push(CliKeyedEpochReward {
address: address.to_string(),
reward: cli_reward,
});
}
let block_time = rpc_client.get_block_time(first_reward.effective_slot)?;
Some(CliEpochRewardshMetadata {
epoch: first_reward.epoch,
effective_slot: first_reward.effective_slot,
block_time,
})
println!("{}", style("Inflation Governor:").bold());
if (governor.initial - governor.terminal).abs() < f64::EPSILON {
println!(
"Fixed APR: {:>5.2}%",
governor.terminal * 100.
);
} else {
None
};
let cli_rewards = CliKeyedEpochRewards {
epoch_metadata,
rewards: epoch_rewards,
};
Ok(config.output_format.formatted_string(&cli_rewards))
println!("Initial APR: {:>5.2}%", governor.initial * 100.);
println!(
"Terminal APR: {:>5.2}%",
governor.terminal * 100.
);
println!("Rate reduction per year: {:>5.2}%", governor.taper * 100.);
}
if governor.foundation_term > 0. {
println!("Foundation percentage: {:>5.2}%", governor.foundation);
println!(
"Foundation term: {:.1} years",
governor.foundation_term
);
}
println!(
"\n{}",
style(format!(
"Inflation for Epoch {}:",
current_inflation_rate.epoch
))
.bold()
);
println!(
"Total APR: {:>5.2}%",
current_inflation_rate.total * 100.
);
println!(
"Staking APR: {:>5.2}%",
current_inflation_rate.validator * 100.
);
println!(
"Foundation APR: {:>5.2}%",
current_inflation_rate.foundation * 100.
);
Ok("".to_string())
}

View File

@@ -1,4 +1,3 @@
#![allow(clippy::integer_arithmetic)]
macro_rules! ACCOUNT_STRING {
() => {
r#", one of:
@@ -10,6 +9,7 @@ macro_rules! ACCOUNT_STRING {
};
}
#[macro_use]
macro_rules! pubkey {
($arg:expr, $help:expr) => {
$arg.takes_value(true)
@@ -25,9 +25,9 @@ pub mod cli;
pub mod cluster_query;
pub mod feature;
pub mod inflation;
pub mod memo;
pub mod nonce;
pub mod program;
pub mod send_tpu;
pub mod spend_utils;
pub mod stake;
pub mod test_utils;

View File

@@ -3,8 +3,11 @@ use clap::{
SubCommand,
};
use console::style;
use solana_clap_utils::{
input_validators::{is_url, is_url_or_moniker, normalize_to_url_if_moniker},
commitment::COMMITMENT_ARG,
input_parsers::commitment_of,
input_validators::{is_url, is_url_or_moniker},
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
DisplayError,
};
@@ -60,19 +63,12 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
);
let (keypair_setting_type, keypair_path) =
CliConfig::compute_keypair_path_setting("", &config.keypair_path);
let (commitment_setting_type, commitment) =
CliConfig::compute_commitment_config("", &config.commitment);
if let Some(field) = subcommand_matches.value_of("specific_setting") {
let (field_name, value, setting_type) = match field {
"json_rpc_url" => ("RPC URL", json_rpc_url, url_setting_type),
"websocket_url" => ("WebSocket URL", websocket_url, ws_setting_type),
"keypair" => ("Key Path", keypair_path, keypair_setting_type),
"commitment" => (
"Commitment",
commitment.commitment.to_string(),
commitment_setting_type,
),
_ => unreachable!(),
};
println_name_value_or(&format!("{}:", field_name), &value, setting_type);
@@ -81,16 +77,11 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
println_name_value_or(
"Commitment:",
&commitment.commitment.to_string(),
commitment_setting_type,
);
}
}
("set", Some(subcommand_matches)) => {
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
config.json_rpc_url = normalize_to_url_if_moniker(url);
config.json_rpc_url = url.to_string();
// Revert to a computed `websocket_url` value when `json_rpc_url` is
// changed
config.websocket_url = "".to_string();
@@ -101,9 +92,6 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
if let Some(keypair) = subcommand_matches.value_of("keypair") {
config.keypair_path = keypair.to_string();
}
if let Some(commitment) = subcommand_matches.value_of("commitment") {
config.commitment = commitment.to_string();
}
config.save(config_file)?;
@@ -117,18 +105,11 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
);
let (keypair_setting_type, keypair_path) =
CliConfig::compute_keypair_path_setting("", &config.keypair_path);
let (commitment_setting_type, commitment) =
CliConfig::compute_commitment_config("", &config.commitment);
println_name_value("Config File:", config_file);
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
println_name_value_or(
"Commitment:",
&commitment.commitment.to_string(),
commitment_setting_type,
);
}
("import-address-labels", Some(subcommand_matches)) => {
let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf);
@@ -179,20 +160,13 @@ pub fn parse_args<'a>(
&config.keypair_path,
);
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
let default_signer = DefaultSigner {
arg_name: default_signer_arg_name,
path: default_signer_path.clone(),
};
let CliCommandInfo {
command,
mut signers,
} = parse_command(&matches, &default_signer, &mut wallet_manager)?;
if signers.is_empty() {
if let Ok(signer_info) =
default_signer.generate_unique_signers(vec![None], matches, &mut wallet_manager)
{
signers.extend(signer_info.signers);
}
}
let CliCommandInfo { command, signers } =
parse_command(&matches, &default_signer, &mut wallet_manager)?;
let verbose = matches.is_present("verbose");
let output_format = matches
@@ -208,10 +182,16 @@ pub fn parse_args<'a>(
OutputFormat::Display
});
let (_, commitment) = CliConfig::compute_commitment_config(
matches.value_of("commitment").unwrap_or(""),
&config.commitment,
);
let commitment = {
let mut sub_matches = matches;
while let Some(subcommand_name) = sub_matches.subcommand_name() {
sub_matches = sub_matches
.subcommand_matches(subcommand_name)
.expect("subcommand_matches");
}
commitment_of(sub_matches, COMMITMENT_ARG.long)
}
.unwrap_or_default();
let address_labels = if matches.is_present("no_address_labels") {
HashMap::new()
@@ -242,7 +222,7 @@ pub fn parse_args<'a>(
}
fn main() -> Result<(), Box<dyn error::Error>> {
solana_logger::setup_with_default("off");
solana_logger::setup();
let matches = app(
crate_name!(),
crate_description!(),
@@ -293,25 +273,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true)
.help("Filepath or URL to a keypair"),
)
.arg(
Arg::with_name("commitment")
.long("commitment")
.takes_value(true)
.possible_values(&[
"processed",
"confirmed",
"finalized",
"recent", // Deprecated as of v1.5.5
"single", // Deprecated as of v1.5.5
"singleGossip", // Deprecated as of v1.5.5
"root", // Deprecated as of v1.5.5
"max", // Deprecated as of v1.5.5
])
.value_name("COMMITMENT_LEVEL")
.hide_possible_values(true)
.global(true)
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
)
.arg(
Arg::with_name("verbose")
.long("verbose")
@@ -363,12 +324,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.index(1)
.value_name("CONFIG_FIELD")
.takes_value(true)
.possible_values(&[
"json_rpc_url",
"websocket_url",
"keypair",
"commitment",
])
.possible_values(&["json_rpc_url", "websocket_url", "keypair"])
.help("Return a specific config setting"),
),
)
@@ -377,7 +333,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.about("Set a config setting")
.group(
ArgGroup::with_name("config_settings")
.args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
.args(&["json_rpc_url", "websocket_url", "keypair"])
.multiple(true)
.required(true),
),

View File

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

View File

@@ -4,7 +4,6 @@ use crate::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
},
memo::WithMemo,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
};
use clap::{App, Arg, ArgMatches, SubCommand};
@@ -12,7 +11,6 @@ use solana_clap_utils::{
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
memo::MEMO_ARG,
nonce::*,
};
use solana_cli_output::CliNonceAccount;
@@ -173,7 +171,6 @@ pub fn parse_authorize_nonce_account(
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let new_authority = pubkey_of_signer(matches, "new_authority", wallet_manager)?.unwrap();
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
@@ -188,7 +185,6 @@ pub fn parse_authorize_nonce_account(
command: CliCommand::AuthorizeNonceAccount {
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
new_authority,
},
signers: signer_info.signers,
@@ -205,7 +201,6 @@ pub fn parse_nonce_create_account(
let seed = matches.value_of("seed").map(|s| s.to_string());
let amount = SpendAmount::new_from_matches(matches, "amount");
let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let payer_provided = None;
let signer_info = default_signer.generate_unique_signers(
@@ -219,7 +214,6 @@ pub fn parse_nonce_create_account(
nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
seed,
nonce_authority,
memo,
amount,
},
signers: signer_info.signers,
@@ -245,7 +239,6 @@ pub fn parse_new_nonce(
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
@@ -260,7 +253,6 @@ pub fn parse_new_nonce(
command: CliCommand::NewNonce {
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
},
signers: signer_info.signers,
})
@@ -292,7 +284,6 @@ pub fn parse_withdraw_from_nonce_account(
let destination_account_pubkey =
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
let lamports = lamports_of_sol(matches, "amount").unwrap();
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
@@ -307,7 +298,6 @@ pub fn parse_withdraw_from_nonce_account(
command: CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
destination_account_pubkey,
lamports,
},
@@ -340,19 +330,15 @@ pub fn process_authorize_nonce_account(
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: SignerIndex,
memo: Option<&String>,
new_authority: &Pubkey,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![authorize_nonce_account(
nonce_account,
&nonce_authority.pubkey(),
new_authority,
)]
.with_memo(memo);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
@@ -363,7 +349,11 @@ pub fn process_authorize_nonce_account(
&tx.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<NonceError>(result, &config)
}
@@ -373,7 +363,6 @@ pub fn process_create_nonce_account(
nonce_account: SignerIndex,
seed: Option<String>,
nonce_authority: Option<Pubkey>,
memo: Option<&String>,
amount: SpendAmount,
) -> ProcessResult {
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
@@ -400,7 +389,6 @@ pub fn process_create_nonce_account(
&nonce_authority,
lamports,
)
.with_memo(memo)
} else {
create_nonce_account(
&config.signers[0].pubkey(),
@@ -408,12 +396,13 @@ pub fn process_create_nonce_account(
&nonce_authority,
lamports,
)
.with_memo(memo)
};
Message::new(&ixs, Some(&config.signers[0].pubkey()))
};
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let (message, lamports) = resolve_spend_tx_and_check_account_balance(
rpc_client,
@@ -425,7 +414,9 @@ pub fn process_create_nonce_account(
config.commitment,
)?;
if let Ok(nonce_account) = get_account(rpc_client, &nonce_account_address) {
if let Ok(nonce_account) =
get_account_with_commitment(rpc_client, &nonce_account_address, config.commitment)
{
let err_msg = if state_from_account(&nonce_account).is_ok() {
format!("Nonce account {} already exists", nonce_account_address)
} else {
@@ -448,7 +439,11 @@ pub fn process_create_nonce_account(
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -470,29 +465,27 @@ pub fn process_new_nonce(
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: SignerIndex,
memo: Option<&String>,
) -> ProcessResult {
check_unique_pubkeys(
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(&nonce_account, "nonce_account_pubkey".to_string()),
)?;
if let Err(err) = rpc_client.get_account(&nonce_account) {
return Err(CliError::BadParameter(format!(
"Unable to advance nonce account {}. error: {}",
nonce_account, err
))
let nonce_account_check =
rpc_client.get_account_with_commitment(&nonce_account, config.commitment);
if nonce_account_check.is_err() || nonce_account_check.unwrap().value.is_none() {
return Err(CliError::BadParameter(
"Unable to create new nonce, no nonce account found".to_string(),
)
.into());
}
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![advance_nonce_account(
&nonce_account,
&nonce_authority.pubkey(),
)]
.with_memo(memo);
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee_with_commitment(
@@ -502,7 +495,11 @@ pub fn process_new_nonce(
&tx.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -541,21 +538,21 @@ pub fn process_withdraw_from_nonce_account(
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: SignerIndex,
memo: Option<&String>,
destination_account_pubkey: &Pubkey,
lamports: u64,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![withdraw_nonce_account(
let ix = withdraw_nonce_account(
nonce_account,
&nonce_authority.pubkey(),
destination_account_pubkey,
lamports,
)]
.with_memo(memo);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
);
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee_with_commitment(
@@ -565,7 +562,11 @@ pub fn process_withdraw_from_nonce_account(
&tx.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<NonceError>(result, &config)
}
@@ -596,7 +597,10 @@ mod tests {
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new("", &default_keypair_file);
let default_signer = DefaultSigner {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let (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();
@@ -620,7 +624,6 @@ mod tests {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey,
nonce_authority: 0,
memo: None,
new_authority: Pubkey::default(),
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -642,7 +645,6 @@ mod tests {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: 1,
memo: None,
new_authority: Pubkey::default(),
},
signers: vec![
@@ -666,7 +668,6 @@ mod tests {
nonce_account: 1,
seed: None,
nonce_authority: None,
memo: None,
amount: SpendAmount::Some(50_000_000_000),
},
signers: vec![
@@ -692,7 +693,6 @@ mod tests {
nonce_account: 1,
seed: None,
nonce_authority: Some(nonce_authority_keypair.pubkey()),
memo: None,
amount: SpendAmount::Some(50_000_000_000),
},
signers: vec![
@@ -728,7 +728,6 @@ mod tests {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
nonce_authority: 0,
memo: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -749,7 +748,6 @@ mod tests {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
nonce_authority: 1,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -794,7 +792,6 @@ mod tests {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: 0,
memo: None,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42_000_000_000
},
@@ -823,7 +820,6 @@ mod tests {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: 1,
memo: None,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42_000_000_000
},

File diff suppressed because it is too large Load Diff

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

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

View File

@@ -107,22 +107,15 @@ where
return Err(CliError::InsufficientFundsForSpendAndFee(
lamports_to_sol(spend),
lamports_to_sol(fee),
*from_pubkey,
));
}
} else {
if from_balance < spend {
return Err(CliError::InsufficientFundsForSpend(
lamports_to_sol(spend),
*from_pubkey,
));
return Err(CliError::InsufficientFundsForSpend(lamports_to_sol(spend)));
}
if !check_account_for_balance_with_commitment(rpc_client, fee_pubkey, fee, commitment)?
{
return Err(CliError::InsufficientFundsForFee(
lamports_to_sol(fee),
*fee_pubkey,
));
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
}
}
Ok((message, spend))

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ use std::{thread::sleep, time::Duration};
pub fn check_recent_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| {
let balance = client
.get_balance_with_commitment(pubkey, CommitmentConfig::processed())
.get_balance_with_commitment(pubkey, CommitmentConfig::recent())
.unwrap()
.value;
if balance == expected_balance {
@@ -20,7 +20,7 @@ pub fn check_recent_balance(expected_balance: u64, client: &RpcClient, pubkey: &
pub fn check_ready(rpc_client: &RpcClient) {
while rpc_client
.get_slot_with_commitment(CommitmentConfig::processed())
.get_slot_with_commitment(CommitmentConfig::recent())
.unwrap()
< 5
{

View File

@@ -20,6 +20,7 @@ use solana_config_program::{config_instruction, get_config_data, ConfigKeys, Con
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
commitment_config::CommitmentConfig,
message::Message,
pubkey::Pubkey,
signature::{Keypair, Signer},
@@ -287,7 +288,9 @@ pub fn process_set_validator_info(
};
// Check existence of validator-info account
let balance = rpc_client.get_balance(&info_pubkey).unwrap_or(0);
let balance = rpc_client
.poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default())
.unwrap_or(0);
let lamports =
rpc_client.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;

View File

@@ -4,15 +4,14 @@ use crate::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
},
memo::WithMemo,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
};
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
commitment::commitment_arg,
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
memo::{memo_arg, MEMO_ARG},
};
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
use solana_client::rpc_client::RpcClient;
@@ -81,8 +80,7 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("STRING")
.takes_value(true)
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the VOTE ACCOUNT pubkey")
)
.arg(memo_arg())
),
)
.subcommand(
SubCommand::with_name("vote-authorize-voter")
@@ -108,8 +106,7 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("NEW_AUTHORIZED_PUBKEY")
.required(true),
"New authorized vote signer. "),
)
.arg(memo_arg())
),
)
.subcommand(
SubCommand::with_name("vote-authorize-withdrawer")
@@ -135,8 +132,7 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("AUTHORIZED_PUBKEY")
.required(true),
"New authorized withdrawer. "),
)
.arg(memo_arg())
),
)
.subcommand(
SubCommand::with_name("vote-update-validator")
@@ -166,7 +162,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-update-commission")
@@ -196,7 +191,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-account")
@@ -215,22 +209,7 @@ impl VoteSubCommands for App<'_, '_> {
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
)
.arg(
Arg::with_name("with_rewards")
.long("with-rewards")
.takes_value(false)
.help("Display inflation rewards"),
)
.arg(
Arg::with_name("num_rewards_epochs")
.long("num-rewards-epochs")
.takes_value(true)
.value_name("NUM")
.validator(|s| is_within_range(s, 1, 10))
.default_value_if("with_rewards", None, "1")
.requires("with_rewards")
.help("Display rewards for NUM recent epochs, max 10 [default: latest epoch only]"),
),
.arg(commitment_arg()),
)
.subcommand(
SubCommand::with_name("withdraw-from-vote-account")
@@ -266,7 +245,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer [default: cli config keypair]"),
)
.arg(memo_arg())
)
}
}
@@ -283,7 +261,6 @@ pub fn parse_create_vote_account(
let commission = value_t_or_exit!(matches, "commission", u8);
let authorized_voter = pubkey_of_signer(matches, "authorized_voter", wallet_manager)?;
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let payer_provided = None;
let signer_info = default_signer.generate_unique_signers(
@@ -300,7 +277,6 @@ pub fn parse_create_vote_account(
authorized_voter,
authorized_withdrawer,
commission,
memo,
},
signers: signer_info.signers,
})
@@ -324,14 +300,12 @@ pub fn parse_vote_authorize(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey,
vote_authorize,
memo,
},
signers: signer_info.signers,
})
@@ -355,14 +329,12 @@ pub fn parse_vote_update_validator(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey,
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
memo,
},
signers: signer_info.signers,
})
@@ -385,14 +357,12 @@ pub fn parse_vote_update_commission(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::VoteUpdateCommission {
vote_account_pubkey,
commission,
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
memo,
},
signers: signer_info.signers,
})
@@ -405,16 +375,10 @@ pub fn parse_vote_get_account_command(
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let use_lamports_unit = matches.is_present("lamports");
let with_rewards = if matches.is_present("with_rewards") {
Some(value_of(matches, "num_rewards_epochs").unwrap())
} else {
None
};
Ok(CliCommandInfo {
command: CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey,
use_lamports_unit,
with_rewards,
},
signers: vec![],
})
@@ -440,7 +404,6 @@ pub fn parse_withdraw_from_vote_account(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount {
@@ -448,7 +411,6 @@ pub fn parse_withdraw_from_vote_account(
destination_account_pubkey,
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
withdraw_amount,
memo,
},
signers: signer_info.signers,
})
@@ -463,7 +425,6 @@ pub fn process_create_vote_account(
authorized_voter: &Option<Pubkey>,
authorized_withdrawer: &Option<Pubkey>,
commission: u8,
memo: Option<&String>,
) -> ProcessResult {
let vote_account = config.signers[vote_account];
let vote_account_pubkey = vote_account.pubkey();
@@ -506,7 +467,6 @@ pub fn process_create_vote_account(
&vote_init,
lamports,
)
.with_memo(memo)
} else {
vote_instruction::create_account(
&config.signers[0].pubkey(),
@@ -514,7 +474,6 @@ pub fn process_create_vote_account(
&vote_init,
lamports,
)
.with_memo(memo)
};
Message::new(&ixs, Some(&config.signers[0].pubkey()))
};
@@ -535,7 +494,9 @@ pub fn process_create_vote_account(
}
}
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let (message, _) = resolve_spend_tx_and_check_account_balance(
rpc_client,
@@ -548,7 +509,11 @@ pub fn process_create_vote_account(
)?;
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -558,7 +523,6 @@ pub fn process_vote_authorize(
vote_account_pubkey: &Pubkey,
new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize,
memo: Option<&String>,
) -> ProcessResult {
// If the `authorized_account` is also the fee payer, `config.signers` will only have one
// keypair in it
@@ -572,14 +536,15 @@ pub fn process_vote_authorize(
(&authorized.pubkey(), "authorized_account".to_string()),
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
)?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let ixs = vec![vote_instruction::authorize(
vote_account_pubkey, // vote account to update
&authorized.pubkey(), // current authorized
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)]
.with_memo(memo);
)];
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -591,7 +556,11 @@ pub fn process_vote_authorize(
&tx.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<VoteError>(result, &config)
}
@@ -601,7 +570,6 @@ pub fn process_vote_update_validator(
vote_account_pubkey: &Pubkey,
new_identity_account: SignerIndex,
withdraw_authority: SignerIndex,
memo: Option<&String>,
) -> ProcessResult {
let authorized_withdrawer = config.signers[withdraw_authority];
let new_identity_account = config.signers[new_identity_account];
@@ -610,13 +578,14 @@ pub fn process_vote_update_validator(
(vote_account_pubkey, "vote_account_pubkey".to_string()),
(&new_identity_pubkey, "new_identity_account".to_string()),
)?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let ixs = vec![vote_instruction::update_validator_identity(
vote_account_pubkey,
&authorized_withdrawer.pubkey(),
&new_identity_pubkey,
)]
.with_memo(memo);
)];
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -628,7 +597,11 @@ pub fn process_vote_update_validator(
&tx.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<VoteError>(result, &config)
}
@@ -638,16 +611,16 @@ pub fn process_vote_update_commission(
vote_account_pubkey: &Pubkey,
commission: u8,
withdraw_authority: SignerIndex,
memo: Option<&String>,
) -> ProcessResult {
let authorized_withdrawer = config.signers[withdraw_authority];
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let ixs = vec![vote_instruction::update_commission(
vote_account_pubkey,
&authorized_withdrawer.pubkey(),
commission,
)]
.with_memo(memo);
)];
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -659,7 +632,11 @@ pub fn process_vote_update_commission(
&tx.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<VoteError>(result, &config)
}
@@ -696,7 +673,6 @@ pub fn process_show_vote_account(
config: &CliConfig,
vote_account_address: &Pubkey,
use_lamports_unit: bool,
with_rewards: Option<usize>,
) -> ProcessResult {
let (vote_account, vote_state) =
get_vote_account(rpc_client, vote_account_address, config.commitment)?;
@@ -722,16 +698,14 @@ pub fn process_show_vote_account(
}
}
let epoch_rewards =
with_rewards.and_then(|num_epochs| {
match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, num_epochs) {
Ok(rewards) => Some(rewards),
Err(error) => {
eprintln!("Failed to fetch epoch rewards: {:?}", error);
None
}
}
});
let epoch_rewards = match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, 1)
{
Ok(rewards) => Some(rewards),
Err(error) => {
eprintln!("Failed to fetch epoch rewards: {:?}", error);
None
}
};
let vote_account_data = CliVoteAccount {
account_balance: vote_account.lamports,
@@ -758,12 +732,15 @@ pub fn process_withdraw_from_vote_account(
withdraw_authority: SignerIndex,
withdraw_amount: SpendAmount,
destination_account_pubkey: &Pubkey,
memo: Option<&String>,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let (recent_blockhash, fee_calculator, _) = rpc_client
.get_recent_blockhash_with_commitment(config.commitment)?
.value;
let withdraw_authority = config.signers[withdraw_authority];
let current_balance = rpc_client.get_balance(&vote_account_pubkey)?;
let current_balance = rpc_client
.get_balance_with_commitment(&vote_account_pubkey, config.commitment)?
.value;
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?;
let lamports = match withdraw_amount {
@@ -779,15 +756,14 @@ pub fn process_withdraw_from_vote_account(
}
};
let ixs = vec![withdraw(
let ix = withdraw(
vote_account_pubkey,
&withdraw_authority.pubkey(),
lamports,
destination_account_pubkey,
)]
.with_memo(memo);
);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut transaction = Transaction::new_unsigned(message);
transaction.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee_with_commitment(
@@ -797,7 +773,11 @@ pub fn process_withdraw_from_vote_account(
&transaction.message,
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&transaction,
config.commitment,
config.send_transaction_config,
);
log_instruction_custom_error::<VoteError>(result, &config)
}
@@ -826,7 +806,10 @@ mod tests {
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new("", &default_keypair_file);
let default_signer = DefaultSigner {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
@@ -841,8 +824,7 @@ mod tests {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter,
memo: None,
vote_authorize: VoteAuthorize::Voter
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -865,8 +847,7 @@ mod tests {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter,
memo: None,
vote_authorize: VoteAuthorize::Voter
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -900,7 +881,6 @@ mod tests {
authorized_voter: None,
authorized_withdrawer: None,
commission: 10,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -930,7 +910,6 @@ mod tests {
authorized_voter: None,
authorized_withdrawer: None,
commission: 100,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -963,8 +942,7 @@ mod tests {
identity_account: 2,
authorized_voter: Some(authed),
authorized_withdrawer: None,
commission: 100,
memo: None,
commission: 100
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -995,8 +973,7 @@ mod tests {
identity_account: 2,
authorized_voter: None,
authorized_withdrawer: Some(authed),
commission: 100,
memo: None,
commission: 100
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1020,7 +997,6 @@ mod tests {
vote_account_pubkey: pubkey,
new_identity_account: 2,
withdraw_authority: 1,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1044,7 +1020,6 @@ mod tests {
vote_account_pubkey: pubkey,
commission: 42,
withdraw_authority: 1,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1069,7 +1044,6 @@ mod tests {
destination_account_pubkey: pubkey,
withdraw_authority: 0,
withdraw_amount: SpendAmount::Some(42_000_000_000),
memo: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -1091,7 +1065,6 @@ mod tests {
destination_account_pubkey: pubkey,
withdraw_authority: 0,
withdraw_amount: SpendAmount::All,
memo: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -1118,7 +1091,6 @@ mod tests {
destination_account_pubkey: pubkey,
withdraw_authority: 1,
withdraw_amount: SpendAmount::Some(42_000_000_000),
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),

View File

@@ -18,44 +18,52 @@ use solana_sdk::{
signature::{keypair_from_seed, Keypair, Signer},
system_program,
};
use std::sync::mpsc::channel;
#[test]
fn test_nonce() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
full_battery_tests(test_validator, None, false);
full_battery_tests(
TestValidator::with_no_fees(mint_keypair.pubkey()),
mint_keypair,
None,
false,
);
}
#[test]
fn test_nonce_with_seed() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
full_battery_tests(test_validator, Some(String::from("seed")), false);
full_battery_tests(
TestValidator::with_no_fees(mint_keypair.pubkey()),
mint_keypair,
Some(String::from("seed")),
false,
);
}
#[test]
fn test_nonce_with_authority() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
full_battery_tests(test_validator, None, true);
full_battery_tests(
TestValidator::with_no_fees(mint_keypair.pubkey()),
mint_keypair,
None,
true,
);
}
fn full_battery_tests(
test_validator: TestValidator,
mint_keypair: Keypair,
seed: Option<String>,
use_nonce_authority: bool,
) {
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url());
let json_rpc_url = test_validator.rpc_url();
let mut config_payer = CliConfig::recent_for_tests();
@@ -65,9 +73,10 @@ fn full_battery_tests(
request_and_confirm_airdrop(
&rpc_client,
&config_payer,
&faucet_addr,
&config_payer.signers[0].pubkey(),
2000,
&config_payer,
)
.unwrap();
check_recent_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
@@ -101,7 +110,6 @@ fn full_battery_tests(
nonce_account: 1,
seed,
nonce_authority: optional_authority,
memo: None,
amount: SpendAmount::Some(1000),
};
@@ -135,7 +143,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: index,
memo: None,
};
process_command(&config_payer).unwrap();
@@ -153,7 +160,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: index,
memo: None,
destination_account_pubkey: payee_pubkey,
lamports: 100,
};
@@ -174,7 +180,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::AuthorizeNonceAccount {
nonce_account,
nonce_authority: index,
memo: None,
new_authority: new_authority.pubkey(),
};
process_command(&config_payer).unwrap();
@@ -183,7 +188,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: index,
memo: None,
};
process_command(&config_payer).unwrap_err();
@@ -192,7 +196,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: 1,
memo: None,
};
process_command(&config_payer).unwrap();
@@ -200,7 +203,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: 1,
memo: None,
destination_account_pubkey: payee_pubkey,
lamports: 100,
};
@@ -214,29 +216,33 @@ fn full_battery_tests(
fn test_create_account_with_seed() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
let to_address = Pubkey::new(&[3u8; 32]);
let config = CliConfig::recent_for_tests();
// Setup accounts
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let rpc_client = RpcClient::new(test_validator.rpc_url());
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&offline_nonce_authority_signer.pubkey(),
42,
&config,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&online_nonce_creator_signer.pubkey(),
4242,
&config,
)
.unwrap();
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
@@ -260,7 +266,6 @@ fn test_create_account_with_seed() {
nonce_account: 0,
seed: Some(seed),
nonce_authority: Some(authority_pubkey),
memo: None,
amount: SpendAmount::Some(241),
};
process_command(&creator_config).unwrap();
@@ -273,7 +278,7 @@ fn test_create_account_with_seed() {
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_address,
CommitmentConfig::processed(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
@@ -291,16 +296,11 @@ fn test_create_account_with_seed() {
to: to_address,
from: 0,
sign_only: true,
dump_transaction_message: true,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_address),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
authority_config.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&authority_config).unwrap();
@@ -317,8 +317,6 @@ fn test_create_account_with_seed() {
to: to_address,
from: 0,
sign_only: false,
dump_transaction_message: true,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_address),
@@ -326,10 +324,7 @@ fn test_create_account_with_seed() {
),
nonce_account: Some(nonce_address),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&submit_config).unwrap();
check_recent_balance(241, &rpc_client, &nonce_address);

File diff suppressed because it is too large Load Diff

View File

@@ -6,17 +6,22 @@ use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{Keypair, Signer},
};
use std::sync::mpsc::channel;
#[test]
fn test_cli_request_airdrop() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let mut bob_config = CliConfig::recent_for_tests();
bob_config.json_rpc_url = test_validator.rpc_url();
bob_config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 50,
};
@@ -26,11 +31,11 @@ fn test_cli_request_airdrop() {
let sig_response = process_command(&bob_config);
sig_response.unwrap();
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let rpc_client = RpcClient::new(test_validator.rpc_url());
let balance = rpc_client
.get_balance(&bob_config.signers[0].pubkey())
.unwrap();
.get_balance_with_commitment(&bob_config.signers[0].pubkey(), CommitmentConfig::recent())
.unwrap()
.value;
assert_eq!(balance, 50);
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,19 +16,20 @@ use solana_sdk::{
nonce::State as NonceState,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
stake,
};
use std::sync::mpsc::channel;
#[test]
fn test_transfer() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url());
let default_signer = Keypair::new();
let default_offline_signer = Keypair::new();
@@ -40,7 +41,8 @@ fn test_transfer() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
.unwrap();
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
@@ -52,16 +54,11 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&config).unwrap();
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
@@ -73,16 +70,11 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
assert!(process_command(&config).is_err());
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
@@ -96,26 +88,24 @@ fn test_transfer() {
process_command(&offline).unwrap_err();
let offline_pubkey = offline.signers[0].pubkey();
request_and_confirm_airdrop(&rpc_client, &offline, &offline_pubkey, 50).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
check_recent_balance(50, &rpc_client, &offline_pubkey);
// Offline transfer
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
let (blockhash, _, _) = rpc_client
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.unwrap()
.value;
offline.command = CliCommand::Transfer {
amount: SpendAmount::Some(10),
to: recipient_pubkey,
from: 0,
sign_only: true,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
offline.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&offline).unwrap();
@@ -128,16 +118,11 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&config).unwrap();
check_recent_balance(39, &rpc_client, &offline_pubkey);
@@ -153,7 +138,6 @@ fn test_transfer() {
nonce_account: 1,
seed: None,
nonce_authority: None,
memo: None,
amount: SpendAmount::Some(minimum_nonce_balance),
};
process_command(&config).unwrap();
@@ -163,7 +147,7 @@ fn test_transfer() {
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::processed(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
@@ -176,8 +160,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
@@ -185,10 +167,7 @@ fn test_transfer() {
),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&config).unwrap();
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
@@ -196,7 +175,7 @@ fn test_transfer() {
let new_nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::processed(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
@@ -208,7 +187,6 @@ fn test_transfer() {
config.command = CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account.pubkey(),
nonce_authority: 0,
memo: None,
new_authority: offline_pubkey,
};
process_command(&config).unwrap();
@@ -218,7 +196,7 @@ fn test_transfer() {
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::processed(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
@@ -231,16 +209,11 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: true,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
let sign_only_reply = process_command(&offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_only_reply);
@@ -252,8 +225,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
@@ -261,10 +232,7 @@ fn test_transfer() {
),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&config).unwrap();
check_recent_balance(28, &rpc_client, &offline_pubkey);
@@ -275,30 +243,34 @@ fn test_transfer() {
fn test_transfer_multisession_signing() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let to_pubkey = Pubkey::new(&[1u8; 32]);
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
let config = CliConfig::recent_for_tests();
// Setup accounts
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let rpc_client = RpcClient::new(test_validator.rpc_url());
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&offline_from_signer.pubkey(),
43,
&config,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&offline_fee_payer_signer.pubkey(),
3,
&config,
)
.unwrap();
check_recent_balance(43, &rpc_client, &offline_from_signer.pubkey());
@@ -307,7 +279,10 @@ fn test_transfer_multisession_signing() {
check_ready(&rpc_client);
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
let (blockhash, _, _) = rpc_client
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
.unwrap()
.value;
// Offline fee-payer signs first
let mut fee_payer_config = CliConfig::recent_for_tests();
@@ -321,16 +296,11 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: true,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
fee_payer_config.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&fee_payer_config).unwrap();
@@ -352,16 +322,11 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: true,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
from_config.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&from_config).unwrap();
@@ -380,16 +345,11 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&config).unwrap();
@@ -402,12 +362,13 @@ fn test_transfer_multisession_signing() {
fn test_transfer_all() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
let faucet_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new(test_validator.rpc_url());
let default_signer = Keypair::new();
@@ -418,7 +379,8 @@ fn test_transfer_all() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
.unwrap();
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
@@ -430,125 +392,13 @@ fn test_transfer_all() {
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
process_command(&config).unwrap();
check_recent_balance(0, &rpc_client, &sender_pubkey);
check_recent_balance(49_999, &rpc_client, &recipient_pubkey);
}
#[test]
fn test_transfer_unfunded_recipient() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let default_signer = Keypair::new();
let mut config = CliConfig::recent_for_tests();
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&default_signer];
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
check_ready(&rpc_client);
// Plain ole transfer
config.command = CliCommand::Transfer {
amount: SpendAmount::All,
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: false,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
};
// Expect failure due to unfunded recipient and the lack of the `allow_unfunded_recipient` flag
process_command(&config).unwrap_err();
}
#[test]
fn test_transfer_with_seed() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let default_signer = Keypair::new();
let mut config = CliConfig::recent_for_tests();
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&default_signer];
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
let derived_address_seed = "seed".to_string();
let derived_address_program_id = stake::program::id();
let derived_address = Pubkey::create_with_seed(
&sender_pubkey,
&derived_address_seed,
&derived_address_program_id,
)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 1).unwrap();
request_and_confirm_airdrop(&rpc_client, &config, &derived_address, 50_000).unwrap();
check_recent_balance(1, &rpc_client, &sender_pubkey);
check_recent_balance(50_000, &rpc_client, &derived_address);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
check_ready(&rpc_client);
// Transfer with seed
config.command = CliCommand::Transfer {
amount: SpendAmount::Some(50_000),
to: recipient_pubkey,
from: 0,
sign_only: false,
dump_transaction_message: false,
allow_unfunded_recipient: true,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: Some(derived_address_seed),
derived_address_program_id: Some(derived_address_program_id),
};
process_command(&config).unwrap();
check_recent_balance(0, &rpc_client, &sender_pubkey);
check_recent_balance(50_000, &rpc_client, &recipient_pubkey);
check_recent_balance(0, &rpc_client, &derived_address);
}

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