Compare commits

...

510 Commits

Author SHA1 Message Date
333685dcfe Remove lock files from programs/bpf/rust (#14150) 2020-12-16 02:14:16 -08:00
119815f2da Ignore RUSTSEC-2020-0077 until next 1.4 release 2020-12-11 20:44:34 -07:00
8c3a8a1e0f Override yanked arc-swap crate to 0.4.8 2020-12-11 20:44:34 -07:00
f20371d0a7 Override yanked miow crate to 0.2.2 2020-12-11 20:44:34 -07:00
2b7ef2cf18 Override yanked net2 0.2.37 2020-12-11 20:44:34 -07:00
717b36bddd Override yanked mio crate to 0.7.6 2020-12-11 20:44:34 -07:00
ec9f9bd5d8 Switch to memmap2 fork 2020-12-11 20:44:34 -07:00
a645b9481d Bump socket2 crate to 0.3.17 2020-12-11 20:44:34 -07:00
6812828ba8 adds the instance token to crds-labels for node-instance crds-values (bp #14037) (#14039)
* adds the instance token to crds-labels for node-instance crds-values (#14037)

If a node "a" receives instance-info from node "b1" it will override any
instance-info associated with "b1" pubkey in its crds table. This makes
it less likely that when "b1" receives crds values from "a" (either
through pull or push), it sees other instances of itself (because node
"a" discarded them when it received "b1" instance info).

In order for the crds table to contain all instance-info associated with
the same pubkey at the same time, we need to add the instance tokens to
the keys in the crds table (i.e. the CrdsValueLabel).

(cherry picked from commit 409fe3bca1)

# Conflicts:
#	core/src/cluster_info.rs
#	core/src/crds_value.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-10 18:25:52 +00:00
fa70b0f2ff checks for duplicate validator instances using gossip (bp #14018) (#14027)
* checks for duplicate validator instances using gossip

(cherry picked from commit 8cd5eb9863)

# Conflicts:
#	core/src/cluster_info.rs
#	core/src/crds_value.rs
#	core/src/result.rs

* pushes node-instance along with version early in gossip

(cherry picked from commit 542198180a)

# Conflicts:
#	core/src/cluster_info.rs

* removes RwLock on ClusterInfo.instance

(cherry picked from commit 895d7d6a65)

# Conflicts:
#	core/src/cluster_info.rs

* std::process::exit to kill all threads

(cherry picked from commit 1d267eae6b)

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-12-09 20:26:14 +00:00
ffbe558a2a Increase snapshot file size limits (#14024) (#14025)
* Increase snapshot file size limits

* Fix test

* Reorder code a bit

(cherry picked from commit 14e241be35)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-09 18:59:52 +00:00
8d915aa5b0 v1.3: rpc getConfirmedBlock backports (#13993)
* Check SlotNotRooted if confirmed block not found in blockstore or bigtable (#13776)

* Separate blockstore checks for not (yet) rooted and cleaned up (#13814)

* Add logging in check_blockstore_max_root (#13887)
2020-12-07 18:11:44 +00:00
eb0f51e487 Don't show confusing message for blacklist expires (#13983) (#13985)
(cherry picked from commit 2f374df494)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-12-07 14:41:41 +00:00
b410c57f66 Report highest_confirmed_root and _slot in commitment metric (#13964) (#13967)
(cherry picked from commit ca35bb3ac8)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-12-05 02:22:30 +00:00
fe0297983f Translate data length and owner as writable (bp #13914) (#13917)
* Translate data length and owner as writable (#13914)

(cherry picked from commit 85bec37be4)

# Conflicts:
#	programs/bpf/Cargo.toml
#	programs/bpf/build.rs
#	programs/bpf_loader/src/syscalls.rs

* Resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-12-02 19:24:01 +00:00
f1265b544c Check that the program was granted access to program_id (bp #13890) (#13892)
* Check that the program was granted access to program_id (#13890)

(cherry picked from commit 733fcbaa6c)

# Conflicts:
#	programs/bpf/tests/programs.rs

* Resolve conflicts

* nudge

* nudge

Co-authored-by: Jack May <jack@solana.com>
2020-12-01 18:42:34 +00:00
3318772bfe Bump to rbpf v0.1.34 (bp #13874) (#13878)
* Bump to rbpf v0.1.34 (#13874)

(cherry picked from commit 7890957250)

# Conflicts:
#	Cargo.lock
#	cli/Cargo.toml
#	programs/bpf/Cargo.toml
#	programs/bpf_loader/Cargo.toml

* Resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-12-01 08:14:21 +00:00
141d6d1fc9 Strengthen EpochSlots sanitization (#13872)
(cherry picked from commit 90d557d916)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-11-30 23:52:29 +00:00
0cd6838c23 Bump version to v1.3.24 (#13851) 2020-11-29 18:11:35 +00:00
ab235b8160 Add extra checks to verify_and_update (bp #13848) (#13849)
* Add extra checks to verify_and_update (#13848)

* Add extra checks to verify_and_update

* nudge

(cherry picked from commit ce4304cc9a)

# Conflicts:
#	runtime/src/message_processor.rs

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-11-29 11:27:30 +00:00
51e8872804 Fix avx check with newest nightly compiler (#13465) (#13809)
(cherry picked from commit c644b05c54)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-11-25 10:50:36 +00:00
5dbec42394 Bump version to 1.3.23 2020-11-25 03:02:13 -07:00
c48298128d Remove spl-token-cli from 1.3.22 build 2020-11-25 01:18:50 -07:00
db3f154b3f Fix nonces (#13800)
Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-24 23:54:02 -08:00
b0e5da40a9 Add stake calculation tests with inflation/slashing (#13605) (#13797)
* Add stake calculation tests with inflation/slashing

* Clean up the test

(cherry picked from commit 42421e77a9)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-25 04:59:20 +00:00
0c2433d796 Cap split stake at source stake when splitting entire balance (#13754) (#13765)
(cherry picked from commit f0f99ffc7e)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-23 18:23:58 +00:00
de03a5092d ledger-tool cap: output credits_observed (#13746) (#13747)
(cherry picked from commit 3bc7d85986)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-21 17:31:03 +00:00
fd39a09eae stake: Don't pay out rewards for epochs where inflation was not enabled (#13744)
(cherry picked from commit 13aa38d307)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-21 05:28:08 +00:00
0ad7b64961 sanitizes bloom filters to avoid division by zero (#13714) (#13717)
Pull requests received over the wire can cause a validator to panic
because of division by zero in bloom filters:
https://github.com/solana-labs/solana/blob/af08ba93e/runtime/src/bloom.rs#L86-L88

(cherry picked from commit a8c29505f0)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-11-20 00:53:23 +00:00
c9d6fde7cf Allow GNUSparse for genesis.bin (#13704) (#13706)
(cherry picked from commit 397cf726fc)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-19 21:47:13 +00:00
4462eabd8c Check for overflow in rent partition calculation (#13569) (#13695)
Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit 110acd20dc)

Co-authored-by: carllin <wumu727@gmail.com>
2020-11-19 13:28:16 +00:00
e675ef85ce RPC: Demote missing block error to warning (#13684)
It frightens the tourists

(cherry picked from commit f2a1a0ac5c)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-19 06:57:08 +00:00
1f693c5925 audit: Ignore RUSTSEC-2020-0071, potential SEGV in time 2020-11-18 22:30:28 -07:00
fc20597cbc Update Initialized split rent-exempt value (#13646) (#13652)
(cherry picked from commit 39932d7664)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-17 20:34:07 +00:00
0aab403cbc Remove overflow opportunities (#13649) (#13650)
(cherry picked from commit a7bed62af0)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-17 20:15:43 +00:00
b0523dc236 Fix assertion failure (#13626) (#13630)
Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit afc1b59475)

Co-authored-by: carllin <wumu727@gmail.com>
2020-11-17 09:33:02 +00:00
02d36d0be0 Quiet notification logs when no subscriptions (#13629) (#13636)
(cherry picked from commit 3e4acba72f)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-17 08:22:12 +00:00
b1e8e8a966 Improve TestValidator instantiation (bp #13627) (#13634)
* Improve TestValidator instantiation (#13627)

* Add TestValidator::new_with_fees constructor, and warning for low bootstrap_validator_lamports

* Add logging to solana-tokens integration test to help catch low bootstrap_validator_lamports in the future

* Reasonable TestValidator mint_lamports

(cherry picked from commit ef99689592)

# Conflicts:
#	tokens/Cargo.toml
#	tokens/tests/commands.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-11-17 01:08:36 -07:00
e12cb457fb Reject faked stake/vote accounts in stake mgmt. (bp #13615) (#13620)
* Reject faked stake/vote accounts in stake mgmt. (#13615)

* Reject faked stake/vote accounts in stake mgmt.

* Use clearer name

(cherry picked from commit 2b3faa1947)

# Conflicts:
#	programs/stake/src/stake_instruction.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-16 22:05:31 +00:00
29ac8f5164 ledger-tool cap: delegation owner and stake v2 flag (bp #13602) (#13606)
* ledger-tool cap: delegation owner and stake v2 flag (#13602)

* Output delegation owner as well

* Add --enable-stake-program-v2

* Small cleanup and add sanity assertion

* Fix typo...

(cherry picked from commit bcd303a447)

* Fix compilation error

* rustfmt

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-16 01:33:04 +00:00
c6818f8faf Disable the PubSub vote subscription by default (#13599)
The --rpc-pubsub-enable-vote-subscription flag may be used to enable it.
The current vote subscription is problematic because it emits a
notification for *every* vote, so hundreds a second in a real cluster.
Critically it's also missing information about *who* is voting,
rendering all those notifications practically useless.

Until these two issues can be resolved, the vote subscription is not
much more than a potential DoS vector.

(cherry picked from commit 5d72e52ad0)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-11-14 21:51:15 +00:00
965e6dfc9a Add counter metrics to rpc-subscriptions (#13596) (#13597)
(cherry picked from commit 88ae321d3f)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-14 20:53:38 +00:00
41eab22117 Send pubsub metrics to metrics server (#13584) (#13585)
(cherry picked from commit 34bf80ba9c)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-13 20:44:59 +00:00
ea2daf0cfa Bump version to 1.3.22 2020-11-13 18:03:30 +00:00
5a61827702 Fix overflow in entry tick verification (bp #13572) (#13580)
* Fix overflow in entry hash count verification

(cherry picked from commit d611337394)

* clippy

(cherry picked from commit 01a4889b53)

Co-authored-by: Justin Starry <justin@solana.com>
2020-11-13 16:33:03 +00:00
a997c723b5 Small cleaning update_epoch_stakes (#13576) (#13577)
(cherry picked from commit c97a7d1105)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-13 12:28:51 +00:00
8b026ba829 ip-echo-server: Don't use framed decoder, it can't be read-limited (#13570)
(cherry picked from commit 6dc735e996)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-13 09:16:06 +00:00
05f2b64a6a --gossip-host may now be specified with --entrypoint (#13566)
(cherry picked from commit 328f59ebef)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-11-13 07:32:16 +00:00
e341a0b0f2 fix bpf lddw check (#13554) (#13557)
(cherry picked from commit 30ef53cb13)

Co-authored-by: Jack May <jack@solana.com>
2020-11-12 22:25:33 +00:00
51a48ae507 Bound ip-echo-server reply read (bp #13543) (#13545)
* ip-echo-server: Name the header length magic number

(cherry picked from commit aab5f24518)

* ip-echo-server: Add helper to compute reply length

(cherry picked from commit 7481ba5618)

* ip-echo-server: Limit socket read to expected reply length

(cherry picked from commit d2cfeb31b9)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-12 07:03:28 +00:00
b34895db37 Fix slow/stuck unstaking due to toggling in epoch (bp #13501) (#13534)
* Fix slow/stuck unstaking due to toggling in epoch (#13501)

* Fix slow/stuck unstaking due to toggling in epoch

* nits

* nits

* Add stake_program_v2 feature status check to cli

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

* Fix conflict

* PartialEq<Vec<T>> is not impl for &[T] on rust v1.45.1

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-11-12 01:20:52 +00:00
3e6398caec Validator: Periodically log what we're waiting for during --wait-for-supermajority (#13530)
(cherry picked from commit 38f15e41b5)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-11 21:18:00 +00:00
b9555116ec Increment version to v1.3.21 (#13528) 2020-11-11 19:30:46 +00:00
81a4769de7 Fix parsing CreateAccountWithSeed instructions (#13513) (#13516)
* Reduce required num_system_accounts and handle 2-account instructions properly

* Update CreateAccountWithSeed account docs to be correct

* Add CreateAccountWithSeed test

(cherry picked from commit 91f4e99b4c)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-11 01:01:38 +00:00
9ee741e021 watchtower: Fix all clear duration message (#13509)
(cherry picked from commit 2a96e722b4)

Co-authored-by: Justin Starry <justin@solana.com>
2020-11-10 19:01:13 +00:00
74c228a9d4 Bump SPL Token version fetched for localnet (bp #13490) (#13505)
* Bump token version fetched for localnet (#13490)

(cherry picked from commit 3282334741)

# Conflicts:
#	fetch-spl.sh

* Fix conflict, ie bump loader to be consistent with devnet/mainnet-beta

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-11-10 18:03:53 +00:00
320140fe8e Fix signature access (#13491) (#13502)
(cherry picked from commit 70c4626efe)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-11-10 18:03:10 +00:00
dfcd0c41ec Make testnet section less ambiguous (#13504) (#13507)
(cherry picked from commit 599dae8f09)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-10 17:12:52 +00:00
d217ea2afc Clean up Delegation::stake_activating_and_deactivating (#13471) (#13472)
(cherry picked from commit 5306eb93cc)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-09 09:52:34 +00:00
553209f64c Clean up Delegation::stake_and_activating (#13460) (#13468)
(cherry picked from commit 737d3e376d)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-08 09:31:14 +00:00
4fcf19e414 Output more inflation calc details in ledger-tool (bp #13345) (#13466)
* Output more inflation calc details in ledger-tool (#13345)

* Output more inflation calc details in ledger-tool

* Fix broken ci...

* Rename confusing variables

* Fix panic by wrapping PointValue with Opiton...

* Minor modifications

* Remove explict needless flush; Drop already does

* Yet another csv field adjustments

* Add data_size and rename epochs to earned_epochs

* Introduce null_tracer

* Unwrap Option in new_from_parent_with_tracer

* Don't shorten identifiers

* Allow irrefutable_let_patterns temporalily

* More null_tracer

* More field adjustments

(cherry picked from commit a81e7e7749)

# Conflicts:
#	runtime/src/bank.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-08 09:03:09 +00:00
e57e632870 Add builtin mem tests (bp #13429) (#13436)
* Add builtin mem tests (#13429)


(cherry picked from commit 84b139cc94)

* resolve crate version

* port to v1.3 conventions

* nudge

Co-authored-by: Jack May <jack@solana.com>
2020-11-07 01:23:14 +00:00
20678cf5ef Fix stake split rent-exempt adjustment (#13357) (#13452)
* Add failing tests

* Fix stake split

* Calculate split rent-exempt-reserve and use

* Add comment in rent.rs

* Add tests for edge cases when splitting to larger accounts, and reject overflow splits

* Reframe InsufficientFunds checks in terms of lamports var

* Test hardening review comments

(cherry picked from commit 4c5f345798)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-11-06 21:55:08 +00:00
0a05bbca2f Fix stake redelegate (bp #13358) (#13449)
* stake: Add redelegation failing test

(cherry picked from commit 491ad59d2e)

* stake: Consider withdraws we redelegating

(cherry picked from commit fe1e08b9ad)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-06 20:27:06 +00:00
ab7dff16a2 Feature-gate stake program (bp #13394) (#13438)
* Feature-gate stake program (#13394)

* Add legacy stake-program handling

* Strip out duplicative legacy code

* Add feature for stake-program-fix

* Feature-deploy new stake program

* Expand comment

(cherry picked from commit 1b1d9f6b0c)

# Conflicts:
#	runtime/src/builtins.rs
#	runtime/src/feature_set.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-11-06 14:00:37 +00:00
5e16e80993 CI: Check monorepo for consistent crate versions (bp #13431) (#13432)
* increment-cargo-version.sh: Add check subcommand

(cherry picked from commit 5d4015358a)

* CI: Check monorepo for consistent crate versions

(cherry picked from commit 7a4e293b3b)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-06 02:27:51 +00:00
a388b5a356 docs: Clarify the commitment levels based on questions (#13387) (#13425)
* Clarify the commitment levels based on questions

Many people have asked about what commitment levels mean, and which to
choose.  This update includes some of the language at
`sdk/src/commitment_config.rs` and a recommendation for different use
cases.

Additionally, the preflight commitment documentation was out of date,
specifying that "max" was always used, and this is no longer the case.

* Update docs/src/developing/clients/jsonrpc-api.md

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

* Update docs/src/developing/clients/jsonrpc-api.md

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

* Update docs/src/developing/clients/jsonrpc-api.md

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

* Fix typo

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

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2020-11-05 19:38:59 +00:00
ef776c0a0e Allow feature builtins to overwrite existing builtins (bp #13403) (#13419)
* Allow feature builtins to overwrite existing builtins (#13403)

* Allow feature builtins to overwrite existing builtins

* Add feature_builtin ActivationType

* Correctly retain idempotent for replacing case

* Fix test

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

# Conflicts:
#	ledger/src/builtins.rs
#	runtime/src/bank.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-11-05 17:53:03 +00:00
a899d75d2d Fix duplicate records of inner instructions (#13380) (#13412)
* Fix duplicate records of inner instructions

* fix tests

* fix clippy

* Remove bad_inner_instructions

(cherry picked from commit c24fbb6f8b)

Co-authored-by: Justin Starry <justin@solana.com>
2020-11-05 08:44:16 +00:00
27733bb4d7 Bump low end validator RAM requirement (#13406) 2020-11-05 15:24:26 +08:00
0b9c87b6ec Docs: Clarify validator disk requirements 2020-11-05 15:24:26 +08:00
df1e62f23f Update docs to latest processors (#11613) 2020-11-05 15:24:26 +08:00
f697a86d1e Comment Stakes::clone_with_epoch (#13388) (#13389)
(cherry picked from commit b0d1ae1d8b)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-04 13:49:48 +00:00
64c76c2f4b Revert "check sysvar id for AccountInfo (#13175) (#13351)"
This reverts commit 290d514051.
2020-11-02 12:14:33 -08:00
290d514051 check sysvar id for AccountInfo (#13175) (#13351)
(cherry picked from commit 322c667655)

# Conflicts:
#	sdk/program/src/sysvar/mod.rs

Co-authored-by: Jack May <jack@solana.com>
2020-11-02 18:18:07 +00:00
958c43f337 Small code cleanup and typo fixes (#13325) (#13340)
* Small code cleanup and typo fixes

* Clean up calculate_points_and_credits

(cherry picked from commit 0e4509c497)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-11-02 02:38:43 +00:00
4513128c75 check sysvar id for AccountInfo (#13175) 2020-11-01 06:58:02 +00:00
d01968ed21 cargo update -p futures-task / cargo update -p futures-util 2020-10-31 19:00:22 +00:00
f9ac24d1f2 Switch to dirs-next 2020-10-31 19:00:22 +00:00
fe5a09b50a Ignore stdweb 2020-10-31 19:00:22 +00:00
f59c70a836 Print the entry type as well when checking archive (#13312) (#13313)
(cherry picked from commit bc7133d752)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-30 17:46:02 +00:00
74bfb00959 Update AccountInfo comments (#13302)
(cherry picked from commit 72d41e5801)
2020-10-30 08:09:42 -07:00
4296bfc728 clarify comment (#13289) (#13291)
(cherry picked from commit b5c8b86e7c)

Co-authored-by: Jack May <jack@solana.com>
2020-10-29 22:28:09 +00:00
ccec111178 more portable install.sh (bp #13114) (#13219)
* more portable install.sh (#13114)

(cherry picked from commit 4e0d1b1d4a)

# Conflicts:
#	sdk/bpf/scripts/install.sh

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-29 21:10:14 +00:00
3a8c6f33a3 adds more parallel processing to gossip packets handling (#12988) (#13287)
(cherry picked from commit 3738611f5c)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-29 18:05:52 +00:00
06067dd823 implements ping-pong packets between nodes (bp #12794) (#13281)
* implements ping-pong packets between nodes (#12794)

https://hackerone.com/reports/991106

> It’s possible to use UDP gossip protocol to amplify DDoS attacks. An attacker
> can spoof IP address in UDP packet when sending PullRequest to the node.
> There's no any validation if provided source IP address is not spoofed and
> the node can send much larger PullResponse to victim's IP. As I checked,
> PullRequest is about 290 bytes, while PullResponse is about 10 kB. It means
> that amplification is about 34x. This way an attacker can easily perform DDoS
> attack both on Solana node and third-party server.
>
> To prevent it, need for example to implement ping-pong mechanism similar as
> in Ethereum: Before accepting requests from remote client needs to validate
> his IP. Local node sends Ping packet to the remote node and it needs to reply
> with Pong packet that contains hash of matching Ping packet. Content of Ping
> packet is unpredictable. If hash from Pong packet matches, local node can
> remember IP where Ping packet was sent as correct and allow further
> communication.
>
> More info:
> https://github.com/ethereum/devp2p/blob/master/discv4.md#endpoint-proof
> https://github.com/ethereum/devp2p/blob/master/discv4.md#wire-protocol

The commit adds a PingCache, which maintains records of remote nodes
which have returned a valid response to a ping message, and on-the-fly
ping messages pending a pong response from the remote node.

When handling pull-requests, those from addresses which have not passed
the ping-pong check are filtered out, and additionally ping packets are
added for addresses which need to be (re)verified.

(cherry picked from commit ae91270961)

# Conflicts:
#	Cargo.lock
#	core/src/cluster_info.rs

* resolves mergify merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-29 16:33:29 +00:00
9f58a0383c Disable eager rent collection for less noise (#13275) (#13279)
(cherry picked from commit 363c148dbe)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-29 15:38:56 +00:00
428cacff88 scans crds table in parallel for finding old labels (bp #13073) (#13277)
* scans crds table in parallel for finding old labels (#13073)

From runtime profiles, the majority time of ClusterInfo::handle_purge
https://github.com/solana-labs/solana/blob/0776fa05c/core/src/cluster_info.rs#L1605-L1626
is spent scanning crds table finding old labels:
https://github.com/solana-labs/solana/blob/0776fa05c/core/src/crds.rs#L175-L197

This can be done in parallel given that gossip thread-pool:
https://github.com/solana-labs/solana/blob/0776fa05c/core/src/cluster_info.rs#L1637-L1641
is idle when handle_purge is invoked:
https://github.com/solana-labs/solana/blob/0776fa05c/core/src/cluster_info.rs#L1681

(cherry picked from commit 37c8842bcb)

# Conflicts:
#	core/tests/crds_gossip.rs

* resolves mergify merge conflict

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-29 15:33:59 +00:00
6a4f89b193 excludes origin from prune set (#13204) (#13278)
On the receiving end, prune messages are ignored if the origin points to
the node itself:
https://github.com/solana-labs/solana/blob/631f029fe/core/src/crds_gossip_push.rs#L285-L295
So to avoid sending these over the wire, the requester can exclude
origin from the prune set.

(cherry picked from commit be80f6d5c5)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-29 15:22:47 +00:00
65ad72cd64 improves threads' utilization in processing gossip packets (#12962) (#13251)
ClusterInfo::process_packets handles incoming packets in a thread_pool:
https://github.com/solana-labs/solana/blob/87311cce7/core/src/cluster_info.rs#L2118-L2134

However, profiling runtime shows that threads are not well utilized and
a lot of the processing is done sequentially.

This commit redistributes the work done in parallel. Testing on a gce
cluster shows 20%+ improvement in processing gossip packets with much
smaller variations.

(cherry picked from commit 75d62ca095)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-29 13:59:12 +00:00
0ddf684376 marks pull request creation time only once per peer (#13113) (#13252)
mark_pull_request_creation time requires an exclusive lock on gossip:
https://github.com/solana-labs/solana/blob/16944e218/core/src/cluster_info.rs#L1547-L1548
Current code is redundantly marking each peer once for each request.
There are at most only 2 unique peers, whereas there are hundreds of
requests per each. So the lock is acquired hundreds of time longer than
necessary.

(cherry picked from commit 4bfda3e766)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-29 13:54:39 +00:00
1376c325b6 Improve final report of ledger-tool capitalization (#13232) (#13235)
(cherry picked from commit 4698ee5e4a)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-28 18:28:23 +00:00
e29a49f107 Use pico inflation for ledger-tool capitalization --enable-inflation (#13215) (#13221)
* Use pico inflation for ledger-tool capitalization --enable-inflation

* rust fmt

(cherry picked from commit 7d2962135d)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-28 20:54:56 +09:00
4e7c096177 Use zstd for create-snapshot (#13214) (#13217)
(cherry picked from commit 6d4c69b7c3)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-28 06:37:28 +00:00
b18fa8deac Fix log (#13207) (#13210)
Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit f96ab5a818)

Co-authored-by: carllin <wumu727@gmail.com>
2020-10-28 03:11:21 +00:00
bba4f3006f Parse vote instructions (#13202) (#13208)
(cherry picked from commit c4962af9eb)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-28 01:53:13 +00:00
802a2cc985 passes through feature-set to gossip requests handling (#12878) (#13205)
* passes through feature-set to down to gossip requests handling
* takes the feature-set from root_bank instead of working_bank

(cherry picked from commit 48283161c3)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-27 23:46:12 +00:00
ad5ef9cc48 Account for forward delay in transaction simulation (#13199) (#13200)
(cherry picked from commit 631f029fe9)

Co-authored-by: Justin Starry <justin@solana.com>
2020-10-27 18:34:41 +00:00
cf1c7c8c00 macos portable rust-bpf (#13176) (#13186)
(cherry picked from commit fc83a666fc)

Co-authored-by: Jack May <jack@solana.com>
2020-10-27 04:45:41 +00:00
a28fb586b0 Add SSH key for buildkite-agent on achille (#13182)
(cherry picked from commit ff4b34202c)

# Conflicts:
#	net/scripts/solana-user-authorized_keys.sh

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-27 04:25:53 +00:00
5737ea448e CLI: Surface deploy transaction errors (#13169)
(cherry picked from commit a82971879f)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-26 22:26:02 +00:00
e54a537015 update call depth docs (#13155) (#13161)
(cherry picked from commit 35f77ccc73)

Co-authored-by: Jack May <jack@solana.com>
2020-10-26 19:45:02 +00:00
39eae50024 implements DataBudget using atomics (#12856) (#13157)
(cherry picked from commit 05cf15a382)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-26 18:27:45 +00:00
736e5c3ec7 Fix BigTable reward type encoding (bp #13142) (#13146)
* Fix reward type encoding

(cherry picked from commit 0a89bb4d3c)

* Don't reuse BPF target build artifacts

(cherry picked from commit 41a56e14fc)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-26 08:18:05 +00:00
f8e310d99d Hide noisy specialization warnings for frozen abi (#13141) (#13143)
(cherry picked from commit 5caf81dbf8)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-26 05:11:12 +00:00
fd7bcfdb4d add precompile verification to simulate_transaction (#13080) (#13125)
(cherry picked from commit 766406fd23)

Co-authored-by: Josh <josh.hundley@gmail.com>
2020-10-24 04:57:12 +00:00
e553c8bb45 Clean up opt conf verifier and vote state tracker (#13081) (#13123)
* Clean up opt conf verifier and vote state tracker

* Update test to follow new message and some knob

* Rename

(cherry picked from commit 0264147d42)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-24 02:39:33 +00:00
1c5f8f51ee Shorten magic install URL (#13121)
(cherry picked from commit b5170b993e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-23 23:20:03 +00:00
5084871e27 Cli: deploy programs via TPU (#13090) (#13110)
* Deploy: send write transactions to leader tpu

* Less apparent stalling during confirmation

* Add EpochInfo mock

* Only get cluster nodes once

* Send deploy writes to next leader

(cherry picked from commit 16944e218f)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-23 17:14:37 +00:00
184a56aae7 shrink debug (#13089) (#13108)
(cherry picked from commit 7d2729f6bd)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-10-23 16:14:09 +00:00
7d66cba7f4 Remove spammy invalid rpc log (#13100) (#13101)
(cherry picked from commit c95f6c4b83)

Co-authored-by: Justin Starry <justin@solana.com>
2020-10-23 08:21:33 +00:00
8f615278d2 Add deploy err if program-account balance is too high (#13091) (#13097)
* Add deploy err if program-account balance is too high

* Review comments

* Add system-program check

* Rename and unhide flag

(cherry picked from commit 4669fa0f98)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-23 05:50:21 +00:00
884d68ddcf Add Pubkey::new_unique()/Hash::new_unique() 2020-10-23 03:19:38 +00:00
b095a52027 Add programming-faq to sidebar (#12586) (#13087)
* Add programming-faq to sidebar

* nudge

* fix path

(cherry picked from commit 22d16c69b7)

Co-authored-by: Jack May <jack@solana.com>
2020-10-22 12:34:22 +00:00
1f5861e107 Add programming faq (#12545) (#13086)
* Add programming faq

* feedback and new content

* nudge

(cherry picked from commit b51c0f3095)

Co-authored-by: Jack May <jack@solana.com>
2020-10-22 12:33:29 +00:00
b90b46fee1 Allow nodes to advertise a different rpc address over gossip (#13053) (#13077)
* Allow nodes to advertise a different rpc address over gossip

* Feedback

(cherry picked from commit 8b0242a5d8)

Co-authored-by: Justin Starry <justin@solana.com>
2020-10-22 04:44:41 +00:00
bff820d549 Remove unused pubkey::Pubkey imports 2020-10-21 20:27:57 -07:00
5ad0ccdfe1 cargo fmt 2020-10-21 20:27:57 -07:00
d8c7d06737 Run codemod --extensions rs Hash::new_rand solana_sdk:#️⃣:new_rand 2020-10-21 20:27:57 -07:00
422bb3c526 Run codemod --extensions rs Pubkey::new_rand solana_sdk::pubkey::new_rand 2020-10-21 20:27:57 -07:00
048a2b982c Add pubkey_new_rand(), mark Pubkey::new_rand() deprecated 2020-10-21 20:27:57 -07:00
76f0557462 Add hash_new_rand(), mark Hash::new_rand() as deprecated 2020-10-21 20:27:57 -07:00
6ebb933302 Bump version to 1.3.20 2020-10-22 00:08:28 +00:00
15a49d7508 RPC: Don't send base64 TXs to old clusters (#13062)
Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-21 21:07:08 +00:00
a86a781fd5 CLI: Print address ephemeral keypair seed phrase to stderr on deploy failure (bp #13046) (#13054)
* CLI: Print address ephemeral keypair seed phrase to stderr on deploy failure

(cherry picked from commit 2905ccc7ec)

# Conflicts:
#	cli/Cargo.toml
#	cli/src/cli.rs

* Fix conflicts

Co-authored-by: Trent Nelson <trent@solana.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-10-21 21:03:00 +00:00
57d8d0ab6e Add ledger-tool dead-slots and improve purge a lot (#13070) 2020-10-21 18:38:37 +00:00
63fe350900 Port various rent fixes to runtime feature (bp #12842) (#13067)
* Port various rent fixes to runtime feature (#12842)

* Port various rent fixes to runtime feature

* Fix CI

* Use more consistent naming...

(cherry picked from commit 608b81b412)

# Conflicts:
#	runtime/src/bank.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-21 14:47:59 +00:00
b384ce9e03 Skip 'Stake by Feature Set' output when showing status of a single feature (#13051)
(cherry picked from commit ad65d4785e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-21 07:01:20 +00:00
d81d4ad8a1 Improve vote-account "Recent Timestamp" output (#12970)
(cherry picked from commit 2cc3d7511a)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-21 05:09:58 +00:00
9ed1d2337b Parse stake and system instructions (bp #13035) (#13044)
* Parse stake and system instructions (#13035)

* Fix token account check

* Add helper to check num accounts

* Add parse_stake

* Add parse_system

* Fix AuthorizeNonce docs

* Remove jsonParsed unstable markers

* Clippy

(cherry picked from commit 46d0019955)

* Fix for older clippy

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-10-21 04:20:02 +00:00
14aaae3485 Support Debug Bank (#13017) (#13042)
(cherry picked from commit c0675968b1)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-21 02:50:12 +00:00
caac786a38 validator: Activate RPC before halting on slot (#13001)
(cherry picked from commit 3b3f7341fa)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-20 23:50:11 +00:00
218a76ed1b Force unset CARGO to use correct version of cargo (#13027) (#13033)
(cherry picked from commit 81d0c8ae7f)

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2020-10-20 22:52:06 +00:00
964f05afa8 Fix secp256k1 instruction indexing and add tests (#13026) (#13031)
(cherry picked from commit 83c53ae4b5)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-10-20 22:24:20 +00:00
a0e2f49263 Remove unsupported metrics tarball from release artifacts
(cherry picked from commit 62f20bc170)
2020-10-20 13:14:46 -07:00
25078d46ba filters out inactive nodes from push options (#12674) (#13022)
* filters out inactive nodes from push options

https://github.com/solana-labs/solana/pull/12620
patched the DDOS issue with nodes which go offline:
https://github.com/solana-labs/solana/issues/12409

However, offline nodes still see (much lesser) traffic spike, likely
because no origins are pruned from their bloom filter in active set:
https://github.com/solana-labs/solana/blob/aaf3790d8/core/src/crds_gossip_push.rs#L276-L286
and so multiple nodes push redundant duplicate messages to them
simultaneously:
https://github.com/solana-labs/solana/blob/aaf3790d8/core/src/crds_gossip_push.rs#L254-L255

This commit will filter out inactive peers from potential push targets
entirely. To mitigate eclipse attacks, staked nodes are retried
periodically.

* uses current timestamp in test/crds_gossip

(cherry picked from commit a5c6a78f6d)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-20 19:44:52 +00:00
840ea326db Remove errant print 2020-10-20 08:58:54 -06:00
b9fc31ec95 Parse bpf loader instructions (#12998) (#13004)
* Add parsing for BpfLoader2 instructions

* Skip info if null

* Return account address in info map

(cherry picked from commit 942e4273ba)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-20 06:21:47 +00:00
9481ee79a8 Ignore more paths in increment-cargo-version.sh (#12996)
(cherry picked from commit c1c69ecc34)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-19 23:57:11 +00:00
dddd0b76f1 removes invalid/outdated pending push messages early (#12555) (#12992)
In CrdsGossipPush::new_push_messages:
https://github.com/solana-labs/solana/blob/972619edb/core/src/crds_gossip_push.rs#L211-L228
we already have paid the cost of looking-up the label in crds table and
checking the hash value and wallclock only to find out that in some
cases the value is invalid or is outdated. So might as well remove the
value here rather than wait for the next call to
purge_old_pending_push_messages:
https://github.com/solana-labs/solana/blob/972619edb/core/src/crds_gossip_push.rs#L372

(cherry picked from commit b5faa11f73)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-19 22:02:18 +00:00
7674a5fea8 Bump version to v1.3.19 (#12986) 2020-10-19 19:13:28 +00:00
78be777b65 Revert "CLI: Put deploy ephemeral keypair behind a flag (#12941)" (#12981)
This reverts commit c2806aa2f9.
2020-10-19 17:31:33 +00:00
9b5c10a6aa Mention monitoring and updating for exchanges (#12953) (#12960)
* Mention monitoring and updating for exchanges

* Fix link syntax...

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* More review comments and word-wrapping

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Trent Nelson <trent.a.b.nelson@gmail.com>
(cherry picked from commit 87311cce7f)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-17 09:50:51 +00:00
8714c14549 keygen: add more mnemonic language support (#12944) (#12956)
(cherry picked from commit 4451042c76)

Co-authored-by: guanqun <guanqun.lu@gmail.com>
2020-10-17 04:01:22 +00:00
1982a7a8e3 Check payer balance for program account rent as needed (#12952) (#12954)
(cherry picked from commit b6bfed64cb)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-16 19:11:13 +00:00
c2806aa2f9 CLI: Put deploy ephemeral keypair behind a flag (#12941)
(cherry picked from commit 5a5b7f39c1)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-16 06:09:40 +00:00
e6521ef44c Report compute budget usage (#12931) (#12933)
(cherry picked from commit b510474dcb)

Co-authored-by: Jack May <jack@solana.com>
2020-10-16 00:19:18 +00:00
03ab8fac29 Update get-block method in get_confirmed_transaction (#12923) (#12929)
* Update get-block method in get_confirmed_transaction

* Remove superfluous into()

(cherry picked from commit 42943ab86d)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-15 21:33:38 +00:00
644304d171 Support arbitrary toolchains with cargo wrapper script (#12925)
(cherry picked from commit 99aecdaf65)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-15 20:15:29 +00:00
782ed192b6 program log pubkey as base58 (bp #12901) (#12910)
* program log pubkey as base58 (#12901)

(cherry picked from commit 3f9e6a600b)

# Conflicts:
#	programs/bpf/benches/bpf_loader.rs
#	programs/bpf/c/src/tuner/tuner.c
#	programs/bpf_loader/src/syscalls.rs
#	runtime/src/process_instruction.rs

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-15 19:21:27 +00:00
0da2f73eb4 Release: Use pinned cargo version to install spl-token-cli (#12915)
(cherry picked from commit bb2f0df9e1)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-15 17:55:15 +00:00
a3fdfea674 RPC: Add metrics for TX encoding (#12879)
(cherry picked from commit c26512255d)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-15 15:41:13 +00:00
43121a56eb Surface 'Program account allocation failed' error details (#12902)
(cherry picked from commit eec3d25ab9)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-15 10:25:26 +00:00
2c0b4f3b4f Respect RefCell when calling invoke (#12858) (#12890)
* Respect RefCell when calling invoke

* nudge

(cherry picked from commit 969f7b015b)

Co-authored-by: Jack May <jack@solana.com>
2020-10-15 02:24:51 +00:00
db30316bb3 Release: Include SPL Token in release tarballs (#12888)
(cherry picked from commit f70762913c)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-15 01:32:29 +00:00
73e4d9d623 Don't report RewardType::Fee when none was awarded (#12876)
(cherry picked from commit 4b04ed86b6)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-14 17:36:17 +00:00
46b864dcb0 Expose program error constants (#12861) (#12870)
(cherry picked from commit d4e953277e)

Co-authored-by: Jack May <jack@solana.com>
2020-10-14 08:48:45 +00:00
4f2e60fea4 Add nop feature set for upcoming ported rent fixes (bp #12841) (#12846)
* Add nop feature set for upcoming ported rent fixes (#12841)

(cherry picked from commit 7de7efe96c)

# Conflicts:
#	runtime/src/feature_set.rs

* Update feature_set.rs

* Add missing comma...

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-14 04:47:07 +00:00
7ce9beacb6 Add log_messages to proto file (#12859) (#12862)
(cherry picked from commit 67ed44c007)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-14 01:37:42 +00:00
9dab79274b terminology update, nonce to bump seed (bp #12840) (#12850)
* terminology update, nonce to bump seed (#12840)

(cherry picked from commit 56211378d3)

# Conflicts:
#	sdk/src/pubkey.rs

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-13 19:24:30 +00:00
df8b07ddbd solana vote-account/solana stake-account now works with RPC servers without --enable-rpc-transaction-history (bp #12826) (#12848)
* Implementation-defined RPC server errors are now accessible to client/ users

(cherry picked from commit 247228ee61)

* Cleanly handle RPC servers that don't have --enable-rpc-transaction-history enabled

(cherry picked from commit 14d793b22c)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-13 18:14:45 +00:00
762423c9a9 Add transaction log messages to |solana confirm -v| output (#12835)
(cherry picked from commit e9dbbdeb81)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-13 07:10:03 +00:00
689aa4d44f Check ELF file for errors before deploy (bp #12741) (#12800)
* Check ELF file for errors before deploy (#12741)

* Check ELF file for errors before deploy

* Update cli/src/cli.rs

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

* Fix formatting

* Bump solana_rbpf

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

# Conflicts:
#	Cargo.lock
#	cli/Cargo.toml
#	cli/src/cli.rs

* rebase

Co-authored-by: Alexandre Esteves <2335822+alexfmpe@users.noreply.github.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-13 05:12:33 +00:00
cff1496e94 Add docs on vote account key rotation (bp #12815) (#12830)
* Add docs on vote account key rotation

(cherry picked from commit 253114ca20)

* Update docs/src/running-validator/vote-accounts.md

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

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-13 04:21:16 +00:00
d1c51950f3 Bump version to v1.3.18 2020-10-13 03:57:13 +00:00
774a12e7b9 CI: Fix crate publication (#12824)
(cherry picked from commit c38021502e)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-12 23:19:31 +00:00
85e8926d81 fix native_loader behavior for invalid accounts (#12814) (#12818)
(cherry picked from commit c24da1ee16)

Co-authored-by: Jack May <jack@solana.com>
2020-10-12 22:09:55 +00:00
a2be9c647f Use latest stable channel release if there's no beta release (#12822)
(cherry picked from commit 65213a1782)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-12 22:03:05 +00:00
744006fe78 RpcClient: Encode TXs as base64 by default (#12816)
(cherry picked from commit efbe37ba20)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-12 21:56:13 +00:00
edf59cccd8 Fix fee mismatch on snapshot deserialize (#12697) (#12753)
Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit c879e7c1ad)

Co-authored-by: carllin <wumu727@gmail.com>
2020-10-12 11:34:13 +00:00
9c72bf871f Move no-0-rent rent dist. behavior under feature (#12804) (#12810)
(cherry picked from commit 2f5bb7e507)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-12 10:42:01 +00:00
033c87e3f1 simulate_transaction_with_config() now passes full config to server (#12802)
(cherry picked from commit b3c2752bb0)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-12 06:13:55 +00:00
d35d853e57 Cargo.lock 2020-10-12 04:21:26 +00:00
03317f6bf7 Switch to tempfile 2020-10-12 04:21:26 +00:00
4f0e928a0a Rework cargo audit ignores 2020-10-12 04:21:26 +00:00
8600d5188d Don't bother paying 0 rent (#12792)
(cherry picked from commit 1fc7c1ecee)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-10 18:12:18 +00:00
8cd7716ee3 Update docs to show support for Nano X (bp #12647) (#12654)
* Update docs to show support for Nano X (#12647)

(cherry picked from commit 0ef3cac1f8)

# Conflicts:
#	docs/src/wallet-guide/ledger-live.md

* Fix merge conflict

Co-authored-by: Dan Albert <dan@solana.com>
Co-authored-by: publish-docs.sh <maintainers@solana.com>
2020-10-10 16:06:41 +00:00
b13385fba6 Update gossip entrypoints 2020-10-10 08:39:54 -07:00
9f82208c29 Store program logs in blockstore / bigtable (TransactionWithStatusMeta) (bp #12678) (#12734)
* Store program logs in blockstore / bigtable (TransactionWithStatusMeta) (#12678)

* introduce store program logs in blockstore / bigtable

* fix test, transaction logs created for successful transactions

* fix test for legacy bincode implementation around log_messages

* only api nodes should record logs

* truncate transaction logs to 100KB

* refactor log truncate for improved coverage

(cherry picked from commit 8f5431551e)

# Conflicts:
#	runtime/src/bank.rs

* Resolve merge conflicts in bank.rs

* rerun cargo fmt

Co-authored-by: Josh <josh.hundley@gmail.com>
2020-10-10 08:55:41 +00:00
596ede864b document program address collisions (bp #12774) (#12781)
* document program address collisions (#12774)

(cherry picked from commit 9ac8db3533)

# Conflicts:
#	sdk/src/pubkey.rs

* Update pubkey.rs

* Update pubkey.rs

Co-authored-by: Jack May <jack@solana.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-10 06:50:58 +00:00
e4bd382472 Expose all rewards (fees, rent, voting and staking) in RPC getConfirmedBlock and the cli (bp #12768) (#12789)
* Expose all rewards (fees, rent, voting and staking) in RPC getConfirmedBlock and the cli

(cherry picked from commit c5c8da1ac0)

# Conflicts:
#	Cargo.lock
#	runtime/src/bank.rs
#	transaction-status/Cargo.toml

* fix: surface full block rewards type

(cherry picked from commit 1b16790325)

* resolve conflicts

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-10 06:25:10 +00:00
a70aa28832 Bump version to 1.3.17 2020-10-10 03:22:14 +00:00
9a63cf51b5 Fix typo (#12780) (#12783)
(cherry picked from commit 5800217998)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-10 02:18:18 +00:00
b31ec0579c Local program allocator (#12679) (#12767)
(cherry picked from commit 630eb3b907)

Co-authored-by: Jack May <jack@solana.com>
2020-10-10 01:44:18 +00:00
ad31768dd9 Add adjustable stack size and call depth (bp #12728) (#12769)
* Add adjustable stack size and call depth (#12728)

(cherry picked from commit c3907be623)

# Conflicts:
#	programs/bpf/Cargo.lock
#	programs/bpf/Cargo.toml
#	programs/bpf/build.rs
#	programs/bpf_loader/Cargo.toml
#	programs/bpf_loader/src/lib.rs
#	runtime/src/feature_set.rs
#	runtime/src/process_instruction.rs

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-09 23:27:59 +00:00
079ea91d6f Add convenience script for working in stability branches (#12765) (#12772)
* Add convenience script for working in stability branches

* Update scripts/curgo.sh

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

* re{locate,name} to /cargo

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

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-09 22:29:47 +00:00
48d08f2010 Bump max invoke depth to 4 (bp #12742) (#12763)
* Bump max invoke depth to 4 (#12742)

(cherry picked from commit 2cd7cd3149)

# Conflicts:
#	programs/bpf/rust/invoked/src/processor.rs
#	runtime/src/feature_set.rs
#	runtime/src/message_processor.rs
#	runtime/src/process_instruction.rs

* fix conflicts

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-09 21:12:35 +00:00
b34ba0fc4e Remove skip-no-mangle entirely (bp #12696) (#12708)
* Remove skip-no-mangle entirely (#12696)

(cherry picked from commit 41ad3dd8f0)

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

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-09 20:44:50 +00:00
41b99b96c0 Ryos compat fixes 1.3 (#12762)
* Fix various ledger-tool error due to no builtins

* Add missing file...

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-09 11:49:15 -06:00
fea6e4d39d Only fetch snapshot if it's newer than local (#12663) (#12751)
* Only fetch snapshot if it's newer than local

* Prefer as_ref over clone

* More nits

* Don't wait forwever for newer snapshot

(cherry picked from commit 81489ccb76)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-09 07:23:06 +00:00
fced68c3f4 Add inflation_kill_switch feature (#12748)
(cherry picked from commit c8807d227a)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-09 05:39:17 +00:00
7f006d810c Add new internal accounts (#12740) (#12746)
Co-authored-by: publish-docs.sh <maintainers@solana.com>
(cherry picked from commit 2c5f83c264)

Co-authored-by: Dan Albert <dan@solana.com>
2020-10-09 02:18:27 +00:00
cab1b102e6 Minor variable name cleanup (#12744)
(cherry picked from commit 3a04026599)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-09 02:03:03 +00:00
448887a469 Fixup wallet docs, manual cherrypick for backport (#12738)
Co-authored-by: publish-docs.sh <maintainers@solana.com>
2020-10-08 21:51:24 +00:00
bded162ed8 fix conflicts (#12733)
Co-authored-by: Jack May <jack@solana.com>
2020-10-08 20:40:59 +00:00
765dd1b775 Nit, short name (bp #12195) (#12732)
* Nit, short name (#12195)

(cherry picked from commit daba17a95c)

# Conflicts:
#	runtime/src/bank.rs

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-08 20:03:15 +00:00
b74fbdf7eb Pipe FeatureSet though InvokeContext (bp #12536) (#12730)
* Pipe FeatureSet though InvokeContext (#12536)

* Pipe FeatureSet though InvokeContext

* gate program size cap

* nit

(cherry picked from commit 74fcb184b2)

# Conflicts:
#	runtime/src/bank.rs
#	runtime/src/feature_set.rs

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-10-08 18:18:51 +00:00
fb0f0adb16 Display vote/stake account epoch rewards 2020-10-08 08:34:40 -07:00
2a3c4d87ce Support multiple connected HW wallets configured with the same seed phrase (bp #12716) (#12719)
* remote-wallet: Select hardware wallets based on host device path

(cherry picked from commit 8e3353d9ef)

* remote-wallet: Append wallet "name" to entries in selector UI

(cherry picked from commit f1a2ad1b7d)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-08 05:46:24 +00:00
eaa6e04e83 Revert "Restore --expected-shred-version argument for mainnet-beta" (#12722)
This reverts commit 9410eab2af.

(cherry picked from commit dadc84fa8c)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-08 04:48:40 +00:00
925973ee60 RPC: Transaction deser can be quite slow (bp #12683) (#12702)
* RPC: Check encoded transaction size before decoding

(cherry picked from commit 7f67d36777)

* RPC: Support base64 encoded transactions

Defaults to base58

(cherry picked from commit e35889542b)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-07 05:50:23 +00:00
2c55319e41 Add env variable for rayon thread counts (#12693) (#12698)
(cherry picked from commit 37222683ee)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-10-07 01:26:18 +00:00
f22a5efde5 Faucet request limiter can overflow (bp #12691) (#12694)
* faucet: Add failing test case

(cherry picked from commit 5ae704d560)

* faucet: Use checked math in request limiter

(cherry picked from commit 87de82ac94)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-10-06 20:02:14 +00:00
add5c5b29f Add systemd and log rotation section to validator start docs (#12675) (#12676)
* Add systemd and log rotation section to validator start docs

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

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

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

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

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

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-05 19:34:44 +00:00
1b53f8b33d Update channel_restriction.sh 2020-10-04 10:28:12 -06:00
34591d2f2f Show commit in --version and ledger-tool's log (#12636) (#12662)
* Show commit in `--version` and ledger-tool's log

* Another handy hidden env var

* Fix test

* Rename to semver!

* Fix syntax error...

(cherry picked from commit 026e7de819)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-03 16:45:27 +00:00
bc1b95d6d0 Bump version to v1.3.16 2020-10-03 04:24:03 +00:00
90d586a4f8 solana stakes now employs server-side filtering if only one vote account is provided (#12657)
(cherry picked from commit 9abaf6ec1d)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-03 02:00:49 +00:00
efcb5cd9f0 Fix zero-len slice translations (#12642) (#12656)
(cherry picked from commit d0aa8a6446)

Co-authored-by: Jack May <jack@solana.com>
2020-10-03 01:58:27 +00:00
ffa0ee69ca Weight push peers by how long we haven't pushed to them (#12620) (#12651)
(cherry picked from commit 71c469c72b)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-10-02 22:27:35 +00:00
a983430ddb Check CPI program is executable (#12644) (#12649)
(cherry picked from commit adeb06e550)

Co-authored-by: Jack May <jack@solana.com>
2020-10-02 22:27:29 +00:00
bd94250fca Improve solana deploy (#12621) (#12646)
* Check program account before attempting to create it

* Use last_valid_slot to timeout status checks

* Include transaction history in RpcClient::get_signature_statuses requests

* Improve solana-deploy send-transactions

* Clippy

* Improve mock deploy test

* Review comments

(cherry picked from commit 19f385db76)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-02 20:44:26 +00:00
db5251a524 solana catchup now retries if the initial RPC connection fails (#12645)
(cherry picked from commit 978b26a9c5)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-02 20:42:51 +00:00
efb665071c limits number of threads in core/tests/crds_gossip.rs (#12615) (#12641)
crds_gossip tests start large networks, which with large thread-pools
will exhaust system resources, causing failures in ci tests:
https://buildkite.com/solana-labs/solana/builds/31953

The commit limits size of thread-pools in the test.

(cherry picked from commit 2c669f65f1)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-02 20:01:49 +00:00
e69ee1ec64 Add GetConfirmedBlocksWithLimit RPC method
(cherry picked from commit 75b621160e)
2020-10-02 08:21:08 -07:00
307686eeba Add --no-port-check to validator (#12245) (#12638)
(cherry picked from commit aa70dbfc62)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-02 12:46:40 +00:00
de1e2f9c0c Add inflation subcommand (#12632)
(cherry picked from commit 42aeead6b4)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-02 07:14:02 +00:00
01f93003d3 Improve block command output (#12631)
(cherry picked from commit 14036ac580)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-02 07:03:29 +00:00
75219afc91 Document postBalance field (#12628)
(cherry picked from commit e03a64ae1b)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-02 05:38:11 +00:00
71526923a6 Expose validator cli arguments for pubsub buffer tuning (#12622)
(cherry picked from commit f41a73d76a)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-10-02 04:43:44 +00:00
29af9d1a36 Avoid overflow when computing rent distribution (bp #12112) (#12607)
* Avoid overflow when computing rent distribution (#12112)

* Avoid overflow when computing rent distribution

* Use assert_eq!....

* Fix tests

* Add test

* Use FeatureSet

* Add comments

* Address review comments

* Tweak a bit.

* Fix fmt

(cherry picked from commit e3773d919c)

# Conflicts:
#	runtime/src/bank.rs
#	runtime/src/feature_set.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-10-02 01:37:47 +00:00
46311181dc Add nonced-tx check to RpcClient (#12600) (#12604)
(cherry picked from commit 8f10e407ee)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-01 07:51:16 +00:00
17de653ce0 Move nonce utils from runtime to sdk (bp #12577) (#12583)
* runtime: Move prepare_if_nonce_account into accounts

(cherry picked from commit caec631344)

* Move nonced tx helpers to SDK

(cherry picked from commit 65b868f4eb)

* Move remaining nonce utils from runtime to SDK

(cherry picked from commit 3c7b9c2938)

# Conflicts:
#	runtime/src/bank.rs

* Fix conflict

Co-authored-by: Trent Nelson <trent@solana.com>
Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-01 06:25:25 +00:00
387ecdf70e Add ci env to travis config (#12608) (#12610)
Co-authored-by: publish-docs.sh <maintainers@solana.com>
(cherry picked from commit a17907b9a2)

Co-authored-by: Dan Albert <dan@solana.com>
2020-10-01 06:03:57 +00:00
fbe5a89e74 retains hash value of outdated responses received from pull requests (#12513) (#12603)
pull_response_fail_inserts has been increasing:
https://cdn.discordapp.com/attachments/478692221441409024/759096187587657778/pull_response_fail_insert.png
but for outdated values which fail to insert:
https://github.com/solana-labs/solana/blob/a5c3fc14b3/core/src/crds_gossip_pull.rs#L332-L344
https://github.com/solana-labs/solana/blob/a5c3fc14b3/core/src/crds.rs#L104-L108
are not recorded anywhere, and so the next pull request may obtain the
same redundant payload again, unnecessary taking bandwidth.

This commit holds on to the hashes of failed-inserts for a while, similar
to purged_values:
https://github.com/solana-labs/solana/blob/a5c3fc14b3/core/src/crds_gossip_pull.rs#L380
and filter them out for the next pull request:
https://github.com/solana-labs/solana/blob/a5c3fc14b3/core/src/crds_gossip_pull.rs#L204

(cherry picked from commit 1866521df6)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-10-01 01:47:20 +00:00
afbdcf3068 Include post balance information for rewards (#12598) (#12602)
* Include post balance information for rewards

* Add post-balance to stored Reward struct

* Handle extended Reward in bigtable

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

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-10-01 01:05:37 +00:00
2d1b995006 Use protobufs to store confirmed blocks in BigTable (#12526) (#12597)
* Use protobufs to store confirmed blocks in BigTable

* Cleanup

* Reorganize proto

* Clean up use statements

* Split out function for unit testing

* s/utils/convert

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

Co-authored-by: Justin Starry <justin@solana.com>
2020-09-30 19:37:02 +00:00
d9d3a95a72 Fix TransactionStatusMeta breakage in blockstore (#12587) (#12596)
* Add helper to facilitate deserializing legacy structs

* Use default_on_eof to fix blockstore vis-a-vis TransactionStatusMeta

* Add should-panic test and comments

(cherry picked from commit 865d01c38d)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-30 19:18:43 +00:00
ea990fd259 Update devnet genesis hash 2020-09-30 11:37:51 -07:00
4f30f9c8cf Modernize python scripts (#12595)
(cherry picked from commit fce3c70b72)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-30 18:24:49 +00:00
d13694d839 Tighten docs publishing flow (#12572) (#12594)
(cherry picked from commit ede19ef33b)

Co-authored-by: Dan Albert <dan@solana.com>
2020-09-30 18:23:49 +00:00
700c8c1ec1 epoch_rewards datapoint now includes the correct rewards epoch (previous epoch) (#12582)
(cherry picked from commit f57af4fec2)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-30 06:56:05 +00:00
33ace54b0f Fix banks RPC port (#12570) (#12574)
* Fix Banks RPC ports

* Add get_account_with_commitment

(cherry picked from commit d158d45051)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-09-30 01:23:13 +00:00
5d2f450b89 Tune the sys-tuner documentation (#12576)
(cherry picked from commit 6156dc300d)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-30 01:18:24 +00:00
55b0e9e9c7 builds crds filters in parallel (bp #12360) (#12571)
* builds crds filters in parallel (#12360)

Based on run-time profiles, the majority time of new_pull_requests is
spent building bloom filters, in hashing and bit-vec ops.

This commit builds crds filters in parallel using rayon constructs. The
added benchmark shows ~5x speedup (4-core machine, 8 threads).

(cherry picked from commit 537bbde22e)

# Conflicts:
#	core/Cargo.toml

* resolves mergify merge conflict

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-30 01:08:16 +00:00
6d1bea7fb4 Include active stake in 'epoch_rewards' datapoint (#12573)
(cherry picked from commit 82848d6c73)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-30 01:08:04 +00:00
d19ed8816e Query BigTable for block time if does not exist in blockstore (#12560) (#12565)
(cherry picked from commit 96a7d4dbd8)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-29 23:26:03 +00:00
af7f48a2fd Track inserted repair shreds (#12455) (#12563)
(cherry picked from commit ce98088457)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-29 22:57:58 +00:00
0965389f41 Enable json output from solana feature status (#12554) (#12559) 2020-09-29 21:00:20 +00:00
24c60cf3db Bump version to v1.3.15 2020-09-29 20:57:08 +00:00
c8f4bfca90 Notify but don't abort on unexpected stake account balance 2020-09-29 11:39:11 -07:00
4a6b65ce53 Switch get_program_accounts to use base64 2020-09-29 18:21:19 +00:00
836ed842d6 Increase rpc pubsub max payload to unblock large account notifications (#12548) (#12551)
(cherry picked from commit 36d55c0667)

Co-authored-by: Justin Starry <justin@solana.com>
2020-09-29 17:20:31 +00:00
966d0f72bb Move process_instruction defs to runtime (#12507) (#12549)
(cherry picked from commit 2ff983647f)

Co-authored-by: Jack May <jack@solana.com>
2020-09-29 15:52:38 +00:00
a07e90516b separates out ClusterInfo::{gossip,listen} thread-pools (#12535) (#12547)
https://github.com/solana-labs/solana/pull/12402
moved gossip-work threads:
https://github.com/solana-labs/solana/blob/afd9bfc45/core/src/cluster_info.rs#L2330-L2334
to ClusterInfo::new as a new field in the ClusterInfo struct:
https://github.com/solana-labs/solana/blob/35208c5ee/core/src/cluster_info.rs#L249
So that they can be shared between listen and gossip threads:
https://github.com/solana-labs/solana/blob/afd9bfc45/core/src/gossip_service.rs#L54-L67

However, in testing https://github.com/solana-labs/solana/pull/12360
it turned out this will cause breakage:
https://buildkite.com/solana-labs/solana/builds/31646
https://buildkite.com/solana-labs/solana/builds/31651
https://buildkite.com/solana-labs/solana/builds/31655
Whereas with separate thread pools all is good. It might be the case
that one thread is slowing down the other by exhausting the thread-pool
whereas with separate thread-pools we get fair scheduling guarantees
from the os.

This commit reverts https://github.com/solana-labs/solana/pull/12402
and instead adds separate thread-pools for listen and gossip threads:
https://github.com/solana-labs/solana/blob/afd9bfc45/core/src/gossip_service.rs#L54-L67

(cherry picked from commit 0d5258b6d3)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-29 11:27:47 +00:00
bd2e09d55a patches bug in Crds::find_old_labels with pubkey specific timeout (#12528) (#12546)
Current code only returns values which are expired based on the default
timeout. Example from the added unit test:
  - value inserted at time 0
  - pubkey specific timeout = 1
  - default timeout = 3
Then at now = 2, the value is expired, but the function fails to return
the value because it compares with the default timeout.

(cherry picked from commit 57ed4e4657)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-29 10:13:13 +00:00
655577f9fe feature subcommand: display active stake by feature id when feature activation is not available (#12543)
(cherry picked from commit 322dbd894f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-29 06:17:31 +00:00
3781ad259b clap-utils: Allow nonce/offline args to be global (bp #12538) 2020-09-29 04:51:33 +00:00
5ad5f8b458 cli-output: Add a path to handling --verbose and --quiet display (bp #12531) 2020-09-29 04:44:59 +00:00
5b322a995f Rpc -> proper optimistic confirmation (#12514) (#12537)
* Add service to track the most recent optimistically confirmed bank

* Plumb service into ClusterInfoVoteListener and ReplayStage

* Clean up test

* Use OptimisticallyConfirmedBank in RPC

* Remove superfluous notifications from RpcSubscriptions

* Use crossbeam to avoid mpsc recv_timeout panic

* Review comments

* Remove superfluous last_checked_slots, but pass in OptimisticallyConfirmedBank for complete correctness

(cherry picked from commit 89621adca7)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-29 03:49:18 +00:00
63d9f32bb4 purges old pending push messages more efficiently (#12522) (#12533)
(cherry picked from commit c94fe9236f)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-29 01:34:58 +00:00
a550d82202 Enable commitment arg on solana deploy (#12532) (#12534)
(cherry picked from commit 35208c5ee7)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-28 23:32:50 +00:00
4cf69365b2 Port BPFLoader2 activation to FeatureSet (bp #12490) (#12530)
* Cargo.lock

(cherry picked from commit 6071d0d206)

# Conflicts:
#	Cargo.lock

* Port BPFLoader2 activation to FeatureSet and rework built-in program activation

(cherry picked from commit 31696a1d72)

# Conflicts:
#	core/Cargo.toml
#	genesis-programs/Cargo.toml
#	genesis/Cargo.toml
#	ledger/Cargo.toml
#	local-cluster/Cargo.toml
#	runtime/src/bank.rs

* Add Builtin AbiExample

(cherry picked from commit 833ad20b01)

* Rebase

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-28 23:12:05 +00:00
873b4ee830 Add a couple feature tests (#12529)
(cherry picked from commit 2956cc5aed)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-28 20:31:09 +00:00
672d9c9f62 Add feature to resolve spl-token v2 multisig bug (#12525)
(cherry picked from commit f9a74b51ef)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-28 18:14:20 +00:00
4bd29c1b32 Add pico-inflation feature
(cherry picked from commit aa5c008fa8)
2020-09-28 09:34:35 -07:00
72c082f55a Add precompile verification to preflight (#12486) (#12516)
(cherry picked from commit 6583c8cffe)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-28 06:37:52 +00:00
d712a908c2 Port fix_recent_blockhashes_sysvar_delay to FeatureSet (#12503)
(cherry picked from commit 5d6410c1cb)

# Conflicts:
#	runtime/src/feature_set.rs

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-26 20:26:58 +00:00
e3ca1a81b4 Add copy-on-write executor cache (bp #12502) (#12511)
* Add copy-on-write executor cache (#12502)

* Add copy-on-write executor cache

* Add remove_executor function to the bank

(cherry picked from commit 965f653471)

# Conflicts:
#	runtime/src/bank.rs

* rebase

Co-authored-by: Jack May <jack@solana.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-26 20:26:50 +00:00
16bce553e4 Nit: bpf test cleanup (#12401) (#12508)
(cherry picked from commit 7c4822efb1)

Co-authored-by: Jack May <jack@solana.com>
2020-09-26 17:53:27 +00:00
bc3aa53e02 Runtime feature activation framework (bp #12376) (#12497)
* Runtime feature activation framework

(cherry picked from commit 93259f0bae)

# Conflicts:
#	runtime/src/bank.rs

* Add feature set identifier to gossiped version information

(cherry picked from commit 35f5f9fc7b)

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

* Port instructions sysvar and secp256k1 program activation to FeatureSet

(cherry picked from commit c10da16d7b)

# Conflicts:
#	runtime/src/bank.rs
#	runtime/src/message_processor.rs

* Add feature management commands

(cherry picked from commit 93ed0ab2bb)

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

* Make test_process_rest_api less fragile

(cherry picked from commit 7526bb96f3)

* Remove id field

(cherry picked from commit cc6ba1e131)

* FeatureSet test

(cherry picked from commit 92406cf9a0)

* cargo fmt

(cherry picked from commit 199940d683)

* cli review feedback

(cherry picked from commit 3a2b8c5e5b)

* Rename active() to is_active()

(cherry picked from commit e39fac9f01)

* Resolve merge conflicts

* Remove continues from compute_active_feature_set()

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-26 17:49:53 +00:00
6a698af235 Deerror 2020-09-25 22:19:09 -07:00
7ec38bd71c Improve 'Failed to create snapshot archive' warning message
(cherry picked from commit 5dcf348098)
2020-09-25 21:06:05 -07:00
8e3882287a Add epoch rewards metric datapoint (bp #12505) (#12509)
* Add epoch rewards metric datapoint

(cherry picked from commit e50386f928)

# Conflicts:
#	runtime/src/bank.rs

* Update bank.rs

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-26 04:02:23 +00:00
0c4074049b Cleanup names, fix line dependent test (#12477) (#12482)
(cherry picked from commit b8c4b88188)

Co-authored-by: Jack May <jack@solana.com>
2020-09-26 01:08:55 +00:00
250d2ba74a Pre-construct cpi instruction recorders before message processing (#12467) (#12504)
(cherry picked from commit 1c970bb39f)

Co-authored-by: Justin Starry <justin@solana.com>
2020-09-26 00:40:32 +00:00
b96e0e3d27 Drain the entire compute budget (bp #12478) (#12492)
* Drain the entire compute budget (#12478)


(cherry picked from commit d00453f747)

* fix conflict

Co-authored-by: Jack May <jack@solana.com>
2020-09-25 23:22:19 +00:00
99b513d905 Bump rust-bpf to v0.2.4 (#12361) (#12501)
(cherry picked from commit 65049bd112)

Co-authored-by: Jack May <jack@solana.com>
2020-09-25 22:12:39 +00:00
e85c792f70 Add RPC notify and banking keys debug (bp #12396) (#12452)
* Add RPC notify and banking keys debug (#12396)

(cherry picked from commit 68e5a2ef56)

# Conflicts:
#	core/src/validator.rs

* Rebase

Co-authored-by: sakridge <sakridge@gmail.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-25 21:05:21 +00:00
b65a764593 Bump jsonrpc crates to 15.0.0 (bp #12491) 2020-09-25 19:49:10 +00:00
a514b0e77b Add ComputeBudget tuner (bp #12476) (#12483)
* Add ComputeBudget tuner (#12476)

(cherry picked from commit d326512121)

# Conflicts:
#	programs/bpf/Cargo.toml

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-09-25 19:13:07 +00:00
179dd6ee59 Ignore cargo audit RUSTSEC-2020-0008 (#12489)
(cherry picked from commit cd5c7f30d5)

Co-authored-by: Jack May <jack@solana.com>
2020-09-25 10:01:23 -07:00
21ba2bad24 Add Signers impl for Vec<Box<dyn Signer>> (#12470)
(cherry picked from commit 07dfa37cce)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-25 10:45:29 +00:00
64b6372f9c cli-output: Add CliTokenAccount type (bp #12466) (#12468)
* account-decoder: Add string format helpers to UiTokenAmount

(cherry picked from commit bb144bf758)

* cli-output: Add CliTokenAccount type

(cherry picked from commit d95bce2600)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-25 06:05:19 +00:00
495ea7cd2f introduce RpcPerfSample and modify getPerformanceSamples output (#12434) (#12464)
* introduce RpcPerfSample and modify getPerformanceSamples output

* camelCase test results

(cherry picked from commit 1d04c1db94)

Co-authored-by: Josh <josh.hundley@gmail.com>
2020-09-24 22:45:05 +00:00
bb12d65102 Remove legacy inflation activation code (#12460)
(cherry picked from commit c4aee8c0a0)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-24 20:38:41 +00:00
72365bb9d2 moves gossip-work thread pool cons to ClusterInfo::new (#12402) (#12458)
(cherry picked from commit 42f1ef8acb)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-24 20:01:03 +00:00
c44f6981b1 adds an atomic variant of the bloom filter (#12422) (#12459)
For crds_gossip_pull, we want to parallelize build_crds_filters, which
requires concurrent writes to bloom filters.

This commit implements a variant of the bloom filter which uses atomics
for its bits vector and so is thread-safe.

(cherry picked from commit bb183938d9)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-24 19:49:10 +00:00
0213016999 Use publish=false (#12447) (#12453)
(cherry picked from commit a5c3fc14b3)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-24 16:58:18 +00:00
4e9e05f311 shards crds values based on their hash prefix (bp #12187) (#12312)
* shards crds values based on their hash prefix (#12187)

filter_crds_values checks every crds filter against every hash value:
https://github.com/solana-labs/solana/blob/ee646aa7/core/src/crds_gossip_pull.rs#L432
which can be inefficient if the filter's bit-mask only matches small
portion of the entire crds table.

This commit shards crds values into separate tables based on shard_bits
first bits of their hash prefix. Given a (mask, mask_bits) filter,
filtering crds can be done by inspecting only relevant shards.

If CrdsFilter.mask_bits <= shard_bits, then precisely only the crds
values which match (mask, mask_bits) bit pattern are traversed.
If CrdsFilter.mask_bits > shard_bits, then approximately only
1/2^shard_bits of crds values are inspected.

Benchmarking on a gce cluster of 20 nodes, I see ~10% improvement in
generate_pull_responses metric, but with larger clusters, crds table and
2^mask_bits are both larger, so the impact should be more significant.

(cherry picked from commit 9b866d79fb)

* bumps indexmap to 1.6.0

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-24 16:10:53 +00:00
7212bb12ea Record and store invoked instructions in transaction meta (#12311) (#12449)
* Record invoked instructions and store in transaction meta

* Enable cpi recording if transaction sender is some

* Rename invoked to innerInstructions

(cherry picked from commit 6601ec8f26)

Co-authored-by: Justin Starry <justin@solana.com>
2020-09-24 15:42:34 +00:00
9ff2378948 Remove transaction encoding from storage layer (bp #12404) (#12440)
* Remove transaction encoding from storage layer (#12404)

(cherry picked from commit 731a943239)

* Bump

Co-authored-by: Justin Starry <justin@solana.com>
2020-09-24 10:11:27 +00:00
ec4938a9f3 Bump version to 1.3.14 (#12444) 2020-09-24 07:42:54 +00:00
41b45ca281 Allow publishing of secp256k1 program 2020-09-24 00:05:11 -06:00
838aaee144 CLI: Factor out offline helpers (bp #12382) 2020-09-24 04:41:30 +00:00
c0e44b624e Docs: Set realistic stake warm-up expectations for validators (#12436)
(cherry picked from commit 215bbe85d8)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-24 03:01:38 +00:00
41ca59ea0e Move dropping AppendVecs outside lock (#12408) (#12429)
* Move drop outside lock

Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit 55be8d4016)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-23 23:03:44 +00:00
a76e175fd0 RpcClient::get_multiple_accounts() now works (#12427)
(cherry picked from commit ff890c173c)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-23 21:56:23 +00:00
35a1ab981c v1.3: Backport block time updates (#12423)
* Submit a vote timestamp every vote (#10630)

* Submit a timestamp for every vote

* Submit at most one vote timestamp per second

* Submit a timestamp for every new vote

Co-authored-by: Tyera Eulberg <tyera@solana.com>

* Timestamp first vote (#11856)

* Cache block time in Blockstore (#11955)

* Add blockstore column to cache block times

* Add method to cache block time

* Add service to cache block time

* Update rpc getBlockTime to use new method, and refactor blockstore slightly

* Return block_time with confirmed block, if available

* Add measure and warning to cache-block-time

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-23 13:54:49 -06:00
0f3a555af5 Document getConfirmedSignaturesForAddress2 until param (#12424) (#12425)
(cherry picked from commit a713e3c92d)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-23 19:47:00 +00:00
5366a3c887 Fix solana-tokens check_payer_balances for distribute-stake (bp #12380) (#12403)
* Fix solana-tokens check_payer_balances for distribute-stake (#12380)

* Handle distribute-stakes properly

* Remove dry-run gating for balance checks

* Reword and simplify InsufficientFunds errors

* Split up test and add helpers

* Rename sol_for_fees -> unlocked_sol

* Refactor distribute_allocations to collect Messages

* Clippy

* Clean up dangling bids

(cherry picked from commit 6563726f22)

# Conflicts:
#	tokens/src/commands.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-23 05:27:01 +00:00
486b657fca validator: Add --require-tower stub arg 2020-09-23 05:01:12 +00:00
e545bdcb51 Bump spl-token (bp #12395) (#12400)
* Bump spl-token (#12395)

(cherry picked from commit e1a212fb79)

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

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-23 01:08:20 +00:00
65e4aac306 Add blockstore column to store performance sampling data (bp #12251) (#12393)
* Add blockstore column to store performance sampling data (#12251)

* Add blockstore column to store performance sampling data

* introduce timer and write performance metrics to blockstore

* introduce getRecentPerformanceSamples rpc

* only run on rpc nodes enabled with transaction history

* add unit tests for get_recent_performance_samples

* remove RpcResponse from rpc call

* refactor to use Instant::now and elapsed for timer

* switch to root bank and ensure not negative subraction

* Add PerfSamples to purge/compaction

* refactor to use Instant::now and elapsed for timer

* switch to root bank and ensure not negative subraction

* remove duplicate constants

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

# Conflicts:
#	core/src/validator.rs
#	ledger/src/blockstore.rs

* merge cherry pick of 65a6bfad0

Co-authored-by: Josh <josh.hundley@gmail.com>
2020-09-22 22:00:51 +00:00
339e72d8d2 Simplify cli node version output, display semver only by default (#12386)
(cherry picked from commit 4fa443becf)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-22 08:07:28 +00:00
0f3208dece Cleanup and feature gate instruction processing (#12359) (#12384)
(cherry picked from commit 22d8b3c3f8)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-22 06:19:14 +00:00
a85a2839e4 Add way to look at tx instructions (#11943) (#12375)
Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-22 00:59:46 +00:00
4fc9f12d7b CLI: Drop unused runtime dep (#12374)
(cherry picked from commit 6767264aa1)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-21 20:38:44 +00:00
8af90c9c08 Add keccak-secp256k1 instruction (#11839) (#12368)
* Implement keccak-secp256k1 instruction

Verifies eth addreses with ecrecover function

* Move secp256k1 test

Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-21 18:09:05 +00:00
7db0464d1b Bind to correct RPC addresses (#12358)
(cherry picked from commit 65b247a922)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-20 08:33:07 +00:00
bc774e3ea6 Bump version to v1.3.13 2020-09-20 05:40:32 +00:00
64af712723 Use validator_config for RPC address instead of cluster_info for port verification checks 2020-09-20 02:27:35 +00:00
ea2611f3a9 Document that testnet has a faucet 2020-09-19 17:41:13 -07:00
a26e1f62bb validator/ cleanup (bp #12340) (#12352)
* validator/ cleanup

(cherry picked from commit 1a03afccb1)

# Conflicts:
#	core/src/validator.rs

* Move TestValidator into its own module

(cherry picked from commit 208dd1de3a)

# Conflicts:
#	core/src/validator.rs
#	tokens/tests/commands.rs

* Rebase

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-19 22:45:09 +00:00
54b87b34c3 Add get_token_account methods (#12346) (#12349)
(cherry picked from commit 28f2c15597)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-19 03:53:51 +00:00
12327c8683 Improve error message when .config/solana/id.json is not found (#12345)
(cherry picked from commit 0ed7b0561e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-19 00:44:16 +00:00
86554c4945 Docs: Add SPL Token exchange integration (bp #12303) 2020-09-19 00:37:25 +00:00
95ed3641c6 Add blocktime column to blockstore (#12336) 2020-09-18 21:42:45 +00:00
83c775cee8 Unbreak 'Listening for Deposits' section (#12338) (#12339)
(cherry picked from commit 06906413ef)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-18 21:19:15 +00:00
9fbbb7b044 SendTransactionServices now exit their thread on channel drop instead of by a flag (bp #12333) (#12335)
* Give the duplicate send_transaction_service a different thread name

(cherry picked from commit 75c3690ccd)

* SendTransactionServices now exit their thread on channel drop instead of by a flag

(cherry picked from commit c4913e3c9e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-18 18:32:06 +00:00
7656034bd4 Fix blockstore processor squash (#12319) (#12323)
(cherry picked from commit 3533e11786)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-18 05:34:37 +00:00
3ede265ff9 Add Pack and COption to sdk (bp #12294) (#12322)
* Add Pack and COption to sdk (#12294)

* Add COption to sdk

* Add Pack to sdk

* Except program_option from nits check

* No Default::default

(cherry picked from commit 58542cf7f6)

# Conflicts:
#	ci/nits.sh

* Fix conflict

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-18 04:31:09 +00:00
a20e954a16 Restore --expected-shred-version argument for mainnet-beta (#12300)
(cherry picked from commit 9410eab2af)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-17 01:20:25 +00:00
1f69b125ce Remove client resends (#12290) (#12296)
* Remove resends from client send_tx methods

* Retry status queries until blockhash expires

(cherry picked from commit a79790dea6)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-17 01:01:40 +00:00
0196c83846 Fix panic in BanksServer (#12293) (#12295)
Fixes #12167

(cherry picked from commit 3ecb390b10)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-09-17 00:36:30 +00:00
37175d0cdf Fix off-by-one max payload checks
(cherry picked from commit f6cda2579f)
2020-09-16 17:05:09 -07:00
a6e73acfa4 Rework snapshot download logic to be more forgiving when --expected-shred-version is not provided (#12289)
(cherry picked from commit 98cfe92745)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-16 21:37:57 +00:00
f00c504555 RPC: Limit request payload size to 50kB (#12287)
(cherry picked from commit 32dcce0ac1)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-16 21:26:31 +00:00
19eb73d645 docs: Fix 'Description will go into a meta tag in head' meta tag (bp #12277) (#12279)
* Fix 'Description will go into a meta tag in head' meta tag

(cherry picked from commit 5d682d2e05)

* Update index.js

(cherry picked from commit c231bb7154)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-16 16:56:26 +00:00
d35dc79ee7 RPC sendTransaction now returns transaction logs on simulation failure (bp #12267) (#12276)
* RPC sendTransaction now returns transaction logs on simulation failure

(cherry picked from commit 749208fa32)

* Remove stale comment

(cherry picked from commit c6eea94edc)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-16 16:45:09 +00:00
2316846c53 solana-tokens: Add capability to perform the same transfer to a batch of recipients (bp #12259) (#12266)
* solana-tokens: Add capability to perform the same transfer to a batch of recipients (#12259)

* Add transfer-amount argument, use simplified input-csv

* Add transfer-amount to readme

(cherry picked from commit a48cc073cf)

# Conflicts:
#	tokens/src/commands.rs
#	tokens/tests/commands.rs

* Fix build

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-16 06:21:40 +00:00
c77fe54629 CLI: Use Base58 encoding rather than deprecated Binary for TX decode (#12265)
(cherry picked from commit 83f93fed02)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-16 05:43:38 +00:00
953c40a9e3 Improve solana-tokens UX (#12253) (#12260)
* Fix computed banks port

* Readme incorrect

* Return error if csv cannot be read

* Move column headers over columns

* Add dry-run check for sender/fee-payer balances

* Use clap requires method for paired args

* Write transaction-log anytime outfile is specified

* Replace campaign-name with required db-path

* Remove bids

* Exclude new_stake_account_address from logs for non-stake distributions

* Fix readme

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-16 04:50:55 +00:00
c10f14d60b Add lockups via solana-tokens (bp #11782) (#12263)
* Add lockups via solana-tokens (#11782)

* Allow stake distributions to update lockups

* Reorg

* Add lockup test

* Fix clippy warning

(cherry picked from commit 5553732ae2)

# Conflicts:
#	tokens/Cargo.toml
#	tokens/src/commands.rs
#	tokens/src/main.rs

* Fix build

Co-authored-by: Greg Fitzgerald <greg@solana.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-16 03:33:39 +00:00
c3c3872f8d validator-info get/set no longer crash on invalid account data (#12258)
(cherry picked from commit 56282f0c01)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-16 01:17:45 +00:00
c539526f1e Add memory allocation support for C programs (#12254) (#12256)
(cherry picked from commit 5ab4109b7e)

Co-authored-by: Jack May <jack@solana.com>
2020-09-16 00:49:36 +00:00
8ea4c1c2c0 Friendlier error message for mapping failures (#12213) (#12255)
(cherry picked from commit 3d4b9bb00d)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-16 00:21:11 +00:00
557fee8183 Make noop a real noop (bp #12196) (#12247)
* Make noop a real noop (#12196)

* Make noop a real noop

* nudge

(cherry picked from commit 555252f435)

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-09-15 23:16:15 +00:00
1957e960ac Add BPF test program instruction monitoring (bp #11984) (#12248)
* Add BPF test program instruction monitoring (#11984)


(cherry picked from commit fab2d44abd)

* fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-09-15 23:16:01 +00:00
11ff80c64b Cache re-usable work performed by the loader (bp #12135) (#12216)
* Cache re-usable work performed by the loader (#12135)

(cherry picked from commit 3278d78f08)

# Conflicts:
#	programs/bpf/Cargo.toml
#	programs/bpf/tests/programs.rs
#	programs/bpf_loader/Cargo.toml

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-09-15 21:25:32 +00:00
7267257073 Bump version to v1.3.12 (#12249) 2020-09-15 20:15:03 +00:00
f107b9b423 Really skip private rpc port reachable checks (#12239) (#12241)
(cherry picked from commit b85e8497b5)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-15 17:43:23 +00:00
1354a0c1a3 Drop the recommendation that --expected-shred-version be set by validators (#12244)
`--expected-shred-version` is another knob for users to get wrong and is
documentation that can get stale due to cluster restarts.  Turns out
it's also generally not required anymore either because:
1. The cluster entrypoint can always be expected to be using the correct
   shred version, and that shred version will be adopted by the new node
   (earlier this was not the case when the `solana-gossip spy` node on
   mainnet-beta.solana.com:8001 ran with shred version 0)
2. On a cluster restart, `--expected-bank-hash` is a much stronger
   assertion that the validator is starting from the correct place (and
   didn't exist when `--expected-shred-version` was first recommended)

(cherry picked from commit 4ada4d43f2)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-15 17:41:36 +00:00
52ee9155b5 Bump version to v1.3.11 (#12238) 2020-09-15 10:25:22 +00:00
65a1884c7b Enable stricter check on rent-exempt accounts on testnet (#12224) (#12226)
(cherry picked from commit 241e6f1ecf)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-15 08:07:23 +00:00
c1a9c826f3 Enable retirement of rent collect in Bank::deposit() on testnet (#12223) (#12227)
(cherry picked from commit 629572831b)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-15 07:53:36 +00:00
d9d8ec480a Enable eager-rent-collect-across-gapped-epochs bugfix (#12219) (#12222)
(cherry picked from commit 7d48339b7c)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-15 06:14:35 +00:00
b5c7ad3a9b Add new validator options for running in more restrictive environments (bp #12191) (#12218)
* Add --restricted-repair-only-mode flag

(cherry picked from commit 63a67f415e)

* Add --gossip-validator argument

(cherry picked from commit daae638781)

* Documenet how to reduce validator port exposure

(cherry picked from commit c8f03c7f6d)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-15 04:26:23 +00:00
771ff65fb4 Faucet: Improve error handling (#12215)
(cherry picked from commit af2262cbba)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-15 01:31:02 +00:00
a5bf59b92a patches default impl for crds filter (#12199) (#12200)
In CrdsFilter.mask all bits after mask_bits are set to 1:
https://github.com/solana-labs/solana/blob/555252f4/core/src/crds_gossip_pull.rs#L65
However the default implementation, sets both mask and mask_bits to zero
which is inconsistent with CrdsFilter::compute_mask for a mask_bits of
zero.

This commit changes the default implementation by setting mask to
`!0u64` (i.e all bits set to one). As a result, for the default crds
filter, `test_mask` will always return true, whereas previously it was
always returning false.
https://github.com/solana-labs/solana/blob/555252f4/core/src/crds_gossip_pull.rs#L85

This is only used in tests and benchmarks, but causes some benchmarks to
be misleading by short circuiting in this line:
https://github.com/solana-labs/solana/blob/555252f4/core/src/crds_gossip_pull.rs#L429

(cherry picked from commit d6ec03f13c)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-13 14:10:42 +00:00
8dc019ae98 Gate pointer alignment enforcement (bp #12176) (#12188)
* Gate pointer alignment enforcement (#12176)

(cherry picked from commit ae7b15f062)

# Conflicts:
#	programs/bpf/tests/programs.rs

* Fix conflicts

Co-authored-by: Jack May <jack@solana.com>
2020-09-12 00:06:39 +00:00
61dcab8c07 Update commitment options (#12173) (#12189)
(cherry picked from commit 3c69cd6d61)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-11 19:24:16 +00:00
640bf7015f Check bank capitalization (bp #11927) (#12184)
* Check bank capitalization (#11927)

* Check bank capitalization

* Simplify and unify capitalization calculation

* Improve and add tests

* Avoid overflow and inhibit automatic restart

* Fix test

* Tweak checked sum for cap. and add tests

* Fix broken build after merge conflicts..

* Rename to ClusterType

* Rename confusing method

* Clarify comment

* Verify cap. in rent and inflation tests

Co-authored-by: Stephen Akridge <sakridge@gmail.com>
(cherry picked from commit de4a613610)

# Conflicts:
#	Cargo.lock
#	accounts-bench/Cargo.toml

* Fix conflict 1/2

* Fix conflict 2/2

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-11 18:04:10 +00:00
db1f57162a Fix propagation on startup from snapshot (#12177) (#12182)
(cherry picked from commit 9c490e06b0)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-11 10:04:55 +00:00
bbddffa805 solana-validator --rpc-bind-address argument now works as expected (bp #12168) (#12174)
* `solana-validator --rpc-bind-address` argument now works as expected

(cherry picked from commit 6f325d4594)

* Update bootstrap-validator.sh

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-10 22:02:50 +00:00
61cf432477 Update commitment options (#12171) (#12172)
(cherry picked from commit 361e5322e4)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-10 13:55:48 -06:00
9396618c12 Calc size ahead of time to alloc once (#12154) (#12169)
(cherry picked from commit fd47d38e59)

Co-authored-by: Jack May <jack@solana.com>
2020-09-10 19:17:20 +00:00
398f12dcc5 Program subscriptions now properly check results len and token program id (#12139) (#12141)
(cherry picked from commit 4431080066)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-09 22:23:11 +00:00
796624adf9 uses rust intrinsics to convert hashes to u64 (#12097) (#12133)
(cherry picked from commit 28f2fa3fd5)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-09 16:33:15 +00:00
f6e266eff0 Activate new bpf loader on devnet (#12124)
(cherry picked from commit f54941fa15)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-09 05:48:18 +00:00
ed237af4d8 Prevent unbound memory growth by blockstore_processor (#12110) (#12122)
* Prevent unbound memory growth by blockstore_processor

* Promote log to info! considering infrequency

* Exclude the time of freeing from interval...

* Skip not-shrinkable slots even if forced

* Add comment

(cherry picked from commit c274e26eb8)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-09 04:09:27 +00:00
415c80c204 Bump version to v1.3.10 2020-09-09 01:29:39 +00:00
e45f1df5dd Reduce cap by rent's leftover as temporary measure (#12111) (#12118)
* Reduce cap by rent's leftover as temporary measure

* Reset testnet cap. on start and more logs

(cherry picked from commit 5b2442d54e)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-08 20:02:46 +00:00
d26a809f1f getMinimumBalanceForRentExemption now only responds to valid account lengths (#12116)
(cherry picked from commit 9e96180ce4)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-08 18:50:22 +00:00
d1fdc96969 Add support for deprecated loader (bp #11946) (#12114)
* Add support for deprecated loader (#11946)


(cherry picked from commit ae0fd3043a)

* fix version

Co-authored-by: Jack May <jack@solana.com>
2020-09-08 18:41:29 +00:00
a5cad340ed Forward transactions to the expected leader instead of your own TPU port (bp #12004) (#12108)
* Forward transactions to the expected leader instead of your own TPU port (#12004)

* Use PoHRecorder to send to the right leader

* cleanup

* fmt

* clippy

* Cleanup, fix bug

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

# Conflicts:
#	banks-server/Cargo.toml

* Update Cargo.toml

Co-authored-by: anatoly yakovenko <anatoly@solana.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-08 16:51:33 +00:00
2341394e8b Rename to ClusterType and restore devnet compat. (manual bp) (#12069)
* Rename to ClusterType and restore devnet compat.

* De-duplicate parse code and add comments

* Adjust default Devnet genesis & reduce it in tests
2020-09-08 23:54:54 +09:00
180224114a Fix RPC transaction method configs serialization (#12100) (#12102)
(cherry picked from commit 9940870c89)

Co-authored-by: Justin Starry <justin@solana.com>
2020-09-08 06:12:26 +00:00
58312655b4 Fix signature subscription panic (#12077) (#12092) 2020-09-07 10:31:25 +00:00
f3f86f43ee Compress snapshot archive within the validator to reduce system dependencies, and default to zstd compression (bp #12085) (#12087)
* Compress snapshot archive within the validator to reduce system dependencies

(cherry picked from commit d3750b47d2)

* Default snapshot compression to zstd instead of bzip2 for quicker snapshot generation

(cherry picked from commit 9ade73841f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-07 06:55:38 +00:00
aac38516da cli: add block and first-available-block commands (bp #12083) (#12084)
* Add first-available-block command

(cherry picked from commit 6677996369)

* Add block command

(cherry picked from commit 27752c4e4d)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-06 20:53:44 +00:00
cd139b8185 Fix bad predicate with malformed gossip votes (#12072) (#12078)
(cherry picked from commit eabc63cdcd)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-06 05:35:00 +00:00
6a74bc1297 validator: Add --enable-bigtable-ledger-upload flag (bp #12040) (#12057)
* Add --enable-bigtable-ledger-upload flag

(cherry picked from commit d8e2038dda)

* Relocate BigTable uploader to ledger/ crate

(cherry picked from commit 91a56caed2)

# Conflicts:
#	ledger/Cargo.toml

* Cargo.lock

(cherry picked from commit 2b8a521562)

* Add BigTableUploadService

(cherry picked from commit bc7731b969)

* Add BigTableUploadService

(cherry picked from commit bafdcf24f5)

* Add exit flag for bigtable upload operations

(cherry picked from commit d3611f74c8)

* Remove dead code

(cherry picked from commit cd3c134b58)

* Request correct access

(cherry picked from commit 4ba43c29ce)

* Add LARGEST_CONFIRMED_ROOT_UPLOAD_DELAY

(cherry picked from commit b64fb295a1)

* Resolve merge conflicts

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-05 17:09:32 +00:00
e9a2dd0bc1 Add --show-transactions flag to transaction-history command (#12071)
(cherry picked from commit 2332dd774f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-05 16:52:38 +00:00
feed960ef3 Bigtable bug fixes (#12058) (#12060)
* Accommodate stricted get_bincode_cell in get_confirmed_signatures_for_address

* Sort signatures newest-oldest, even within slot

(cherry picked from commit 879c98efeb)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-05 03:31:35 +00:00
ae8acada8c Add unlock epochs for blake3 (#12054) (#12056)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit a13efc52b3)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-04 23:33:53 +00:00
357a341db5 Bump getMultipleAccounts input limit (#12050) (#12052)
(cherry picked from commit 954b017f85)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-04 18:58:45 +00:00
d0f715cb23 adds new CrdsFilterSet type for Vec<CrdsFilter> (#12029) (#12048)
(cherry picked from commit 114c211b66)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-04 14:10:12 +00:00
0fc0378347 fix solana-install syntax (#12043)
```
solana-install info
solana-install deploy
solana-install update
solana-install run
```

(cherry picked from commit 38f36a7a7a)

Co-authored-by: pk <4514654+pkrasam@users.noreply.github.com>
2020-09-04 05:40:47 +00:00
aa3bdd3730 Revert signature-notification format change (bp #12032) (#12038)
* Revert signature-notification format change (#12032)

* Use untagged RpcSignatureResult enum to avoid breaking downstream consumers of current signature subscriptions

* Clean up client duplication

* Clippy

(cherry picked from commit 39246f9dd7)

# Conflicts:
#	core/src/rpc_pubsub.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-04 01:34:49 +00:00
962aed0961 Mark a withdraw authority as non-circulating (#12033) (#12036)
(cherry picked from commit 2c091e4fca)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-09-04 00:46:10 +00:00
bd76810623 Bump version to 1.3.9 (#12034) 2020-09-03 16:55:24 -07:00
58fe45f3e5 Don't query modern Ledger wallet app version with deprecated payload size (#12031)
(cherry picked from commit dff8242887)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-09-03 21:55:55 +00:00
01a9360dbe builds crds filters without looping over filters (#11998) (#12028)
(cherry picked from commit bc7adb97ed)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2020-09-03 21:44:00 +00:00
f44d9d770a Rpc: add getMultipleAccounts endpoint (bp #12005) (#12024)
* Rpc: add getMultipleAccounts endpoint (#12005)

* Add rpc endpoint to return the state of multiple accounts from the same bank

* Add docs

* Review comments: Dedupe account code, default to base64, add max const

* Add get_multiple_accounts to rpc-client

(cherry picked from commit b22de369b7)

* Use new_response for consistency

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-09-03 19:52:46 +00:00
79d9d28e7d Update token amounts in parsed instructions to retain full precision (#12020) (#12022)
(cherry picked from commit b940da4040)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-03 19:22:24 +00:00
d273d1acb2 Fix forwarding calculation (#12014) (#12019)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 3f39ab1e04)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-03 10:36:56 +00:00
991f188f23 Fix test (#12013) (#12017)
(cherry picked from commit 36a294aae0)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-03 17:16:59 +09:00
8f02fdcc11 Purge storage rewards from accounts db for testnet (#11996) (#12011)
* Purge storage rewards from accounts db for testnet

* Fix test failing only on stable

(cherry picked from commit fb71ee60aa)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-03 08:01:37 +00:00
3e78850331 Move forward token2 native mint testnet epoch (#12007) (#12010)
(cherry picked from commit 4b1cb51a3e)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-03 07:48:55 +00:00
f29a741582 Clarify comments and names in inflation code (#11977) (#12008)
(cherry picked from commit 89bca6110a)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-03 06:47:13 +00:00
3e6052faca Bigtable method to return a single row of data (#11999) (#12002)
(cherry picked from commit b041afe1be)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-02 21:01:58 +00:00
0b0710d522 Use conventional special self notation (#11990) (#11997)
(cherry picked from commit 46aac4819a)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-02 11:59:16 +00:00
2f3fced8a8 Switch account hashing to blake3 (#11969) (#11992)
* Switch account hashing to blake3

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

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-02 08:40:07 +00:00
a5832366a7 Detect and notify when deserializable shreds are available (bp #11816) (#11988)
* Detect and notify when deserializable shreds are available (#11816)

* Add logic to check for complete data ranges

* Add RPC signature notification

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

# Conflicts:
#	accounts-bench/Cargo.toml
#	core/src/rpc_pubsub.rs

* Fix conflicts

Co-authored-by: carllin <wumu727@gmail.com>
Co-authored-by: Carl <carl@solana.com>
2020-09-02 06:38:10 +00:00
f26ff6d6b2 Docs.rs version replacement (#11981) (#11982)
(cherry picked from commit b720921c83)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-09-01 13:52:57 -06:00
57f149e415 Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. (#11974)
Workaround for https://github.com/solana-labs/solana-program-library/issues/374 until spl-token 3 is shipped

(cherry picked from commit 7341e60043)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-09-01 18:16:22 +00:00
8ece3847f9 Add tests for the Debug and activation Vecs (#11926) (#11968)
* Add tests for the Debug and activation Vecs

* Rename a bit

(cherry picked from commit 11ac4eb21d)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-09-01 09:54:27 +00:00
0d23ad00b1 Update to rayon 1.4.0 (#11898) (#11902)
(cherry picked from commit c4253dc0f9)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-09-01 04:53:39 +00:00
d9684f99c3 Remove log (#11949) (#11961)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 7641b60a2b)

Co-authored-by: carllin <wumu727@gmail.com>
2020-09-01 01:05:20 +00:00
6ea9c249d7 Add missing backslash to solana-validator command (#11958)
(cherry picked from commit a19f696a42)

Co-authored-by: Richard Ayotte <rich.ayotte@gmail.com>
2020-08-31 23:43:22 +00:00
1e02069f86 Increase message_processor logging to error level (#11945) (#11948)
(cherry picked from commit 9b9d559312)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-31 21:26:35 +00:00
b179ed0b90 Remove encrypted secrets not required by the main public CI (bp #11942) (#11944)
* Remove unused GEOLOCATION_API_KEY

(cherry picked from commit f78594dfc1)

* Remove secrets not required by the main public CI

(cherry picked from commit 278f2fe078)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-31 18:59:52 +00:00
eb65ff750e Simplify get_programs(), specify a real Preview activation epoch for new BPFLoader (#11930)
(cherry picked from commit f385af25e5)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-31 17:21:07 +00:00
663dc9959f Bump version to v1.3.8 2020-08-31 10:18:42 -07:00
53ed6a2298 Fix use-deprecated-loader arg (#11921) (#11929)
(cherry picked from commit 6234909373)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-31 09:53:34 +00:00
47962f3e80 Use DNS for devnet/testnet entrypoints (#11922)
(cherry picked from commit e4d7e1fe3f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-31 07:53:02 +00:00
d3a16e4e13 Bump RPC banks up 1 port to avoid web3.js wss port conflict
(cherry picked from commit f8bb93a0f4)
2020-08-30 23:56:19 -07:00
250f5a8196 Add new trusted validator for testnet 2020-08-30 22:51:42 -07:00
83a17acc17 Add methods to adjust rent burn percentage, and hashes per tick 2020-08-30 16:50:43 -07:00
ed34b930e5 modify-genesis now writes elsewhere and produces a full genesis.tar.bz2 2020-08-30 16:47:27 -07:00
95816f7db9 Add ability to fork a local cluster from the latest mainnet-beta snapshot 2020-08-30 16:47:18 -07:00
e652c27142 Add bank-hash subcommand (#11915)
(cherry picked from commit a07980536a)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-30 19:05:04 +00:00
0811cb2966 Fix get_parsed_token_accounts (#11907) (#11909)
(cherry picked from commit 60c7ac6f95)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-29 19:53:05 +00:00
311b419f8a Bump version to v1.3.7 2020-08-29 11:42:14 -07:00
acb992c3be Update to spl-token 2 2020-08-29 09:23:20 -07:00
61aca235a3 Bump spl-token version 2020-08-29 03:24:36 -06:00
bad0709ff1 Update to token pack/unpack changes (bp #11900) (#11903)
* Update to token pack/unpack changes (#11900)

(cherry picked from commit 2eff9a19c3)

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

* Fix conflicts

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

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

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

* Update rpc to spl-token v2.0

* Update getTokenSupply to pull from Mint directly

* Fixup to spl-token v2.0.1

(cherry picked from commit 76be36c9ce)

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

* Fix conflicts

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

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

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

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

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

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

* nudge

(cherry picked from commit ea179ad762)

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

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

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

* Explicit docs

(cherry picked from commit 1988ee9cd6)

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

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

(cherry picked from commit e9b610b8df)

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

* resolve conflicts

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

* Implement Debug for MessageProcessor

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

* Remove dbg!

* Fix clippy

* Clippy

* Add test

* add loader to stable operating mode at proper epoch

* refresh_programs_and_inflation after ancestor setup

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

* Fix test

* Fix test and fix the past history

* Make callback management stricter and cleaner

* Fix test

* Test overwrite and frozen for native programs

* Test epoch callback with genesis-programs

* Add assertions for parent bank

* Add tests and some minor cleaning

* Remove unsteady assertion...

* Fix test...

* Fix DOS

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

* Fix frozen abi implementation...

* Move compute budget constatnt init back into bank

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

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

* Fix conflicts

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

(cherry picked from commit 11951eb009)

* Expand system tuning docs

(cherry picked from commit 5354df8c1c)

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

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

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


(cherry picked from commit c2e5dae7ba)

* resolve conflicts

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

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

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

(cherry picked from commit 46830124f8)

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

* resolve conflicts

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

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

* Align host addresses

* support new program abi

* update epoch rollout

* Enforce aligned pointers in cross-program invocations

(cherry picked from commit 9290e561e1)

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

* resolve conflicts

* nudge

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

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

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

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

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

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

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

(cherry picked from commit 9a366281d3)

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

* resolve conflicts

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

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

(cherry picked from commit 8d362f682b)

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

* Resolve conflicts

* nudge

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

(cherry picked from commit 750e5344f1)

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

* resolve conflicts

* resolve conflicts

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

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

(cherry picked from commit b660704faa)

* rebase

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

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

(cherry picked from commit 7c736f71fe)

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

* resolve conflicts

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

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

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

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

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

* fix: add address_owner

* Add SystemInstruction::TransferWithSeed

* Update ABI hash

* chore: better variable names

* Add AuthorizeWithSeedArgs

* Reorder and rename arguments for clarity

(cherry picked from commit f02a78d8ff)

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

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

* Add blockstore utility

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

* Update docs

(cherry picked from commit 747f8d5877)

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

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

(cherry picked from commit 2fd2aceeb2)

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

* Fix conflicts and toml order

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

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

* Fix test

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

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

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

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

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

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

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

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

(cherry picked from commit b5f3ced860)

* decode-transaction now supports binary64

(cherry picked from commit 2ebc68a9e2)

# Conflicts:
#	cli/src/cli.rs

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

(cherry picked from commit 757e147b3b)

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

* Rename Binary64 to Base64.  Establish Base58 encoding

(cherry picked from commit adc984a225)

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

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

(cherry picked from commit e5281157fa)

* resolve conflicts

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

* Clean up arg names and take cap inputs as SOL

(cherry picked from commit 71d5409b3b)

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

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

* Let's see how the ci goes

* Restore previous code

* Well, almost all new changes are revertable

* Update doc

* Add test and gating

* Fix tests

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

* Fix more tests...

* Fix snapshot restore

* Align to _new_ with better uninitialized detection

(cherry picked from commit 23fa84b322)

# Conflicts:
#	core/src/rpc_subscriptions.rs

* Fix conflicts

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

* Make helper fn to deserialize cell data

* Refactor get_confirmed_signatures_for_address to use get_row_data range

* Add until param to get_confirmed_signatures_for_address

* Add until param to blockstore api

* Plumb until through client/cli

* Simplify client params

(cherry picked from commit 6c5b8f324a)

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

* Pull additional keys to account for filtered records

* Clarify variable name

(cherry picked from commit 820af533a4)

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

(cherry picked from commit 338f66f9aa)

* Factor out ShortU16 deser vistor logic to helper

(cherry picked from commit 6222fbcc66)

* Reimplement decode_len() with ShortU16 vistor helper

(cherry picked from commit 30dbe257cf)

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

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

* Limit to only rooted slots

(cherry picked from commit 99fb36fe45)

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

* Add failing test

* Ensure highest_confirmed_root only grows

(cherry picked from commit 4da1e9833c)

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

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

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

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

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

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

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

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

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

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

* builds

* stats

* revert

* tests

* clippy

* add some jitter

* shorter jitter timer

* update

* fixup! update

* use saturating_sub

* fix filters

(cherry picked from commit 713851b68d)

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

* Adapt RpcClient to recent token method changes

(cherry picked from commit 17645ee20c)

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

* Remove redundant predicate

* Rename

* Start to add tests with some cleanup

* Forgot to add refactor code...

* Enchance test

* Really fix rent timing in deposit with robust test

* Simplify new behavior by disabling rent altogether

(cherry picked from commit 6c242f3fec)

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

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

* Refactor to use current api, eliminating an rpc call

* Review comment

(cherry picked from commit c0d6761f63)

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

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

* Use match instead of if

(cherry picked from commit 88ca04dbdb)

# Conflicts:
#	cli/src/cli.rs

* Fix conflicts

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

* Fix jsonParsed mint

(cherry picked from commit da210ddd51)

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

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

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

* Secondary filter on signature

* Use AddressSignatures iterator instead of manually decrementing slots

* Remove unused method

* Add metrics

* Add transaction-status-index doccumentation

(cherry picked from commit de5fb3ba0e)

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

* Remove default binary conversion

(cherry picked from commit ebc45bd73f)

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

* Pass pubkey in to account-decoder for sysvars

* Decode sysvar accounts

* Decode config accounts; move validator-info lower

* Decode stake accounts

* Review comments

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

(cherry picked from commit a9f76862fb)

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

* Fix conflicts

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

* Decode into binary64

* Reword docs

(cherry picked from commit 068d23f298)

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

* Omit delegate and delegatedAmount when none

(cherry picked from commit 88d8d3d02a)

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

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

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

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

* Return ui_amount, decimals in RPC jsonParsed token accounts

* Fixup docs

* Return ui_amount, decimals in pubsub jsonParsed token accounts

* Remove unnecessary duplicate struct

* StringAmount rename

(cherry picked from commit b7c2681903)

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

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

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

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

* Prevent transaction status match outside of primary-index bounds

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

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

This reverts commit cbad2a9fae.

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

This reverts commit ffaeac0669.

(cherry picked from commit 1061b50665)

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

* cleans up homepage on docs

* adds icon files and tightens margins

* cleans up sidenav, adds top nav items

* fixes a link

* removes icon files

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

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

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

(cherry picked from commit 4222932e08)

* Upgrade help description

(cherry picked from commit 9abb7db5f8)

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

(cherry picked from commit 9d2293bb32)

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

(cherry picked from commit 1617a025ce)

* Add access_token module

(cherry picked from commit 59d266a111)

* Add root_ca_certificate

(cherry picked from commit faa016e4b7)

* Add build-proto

(cherry picked from commit c31e1f5bf0)

* UiTransactionEncoding is now copy

(cherry picked from commit 494968be66)

* Increase timeout

(cherry picked from commit 57dfebc5ba)

* Add build-proto/build.sh output

(cherry picked from commit 54dae6ba2c)

* Supress doctest errors

(cherry picked from commit 019c75797d)

* Add compression

(cherry picked from commit 243e05d59f)

* Add bigtable

(cherry picked from commit 6e0353965a)

* Add configuration info

(cherry picked from commit 98cca1e774)

* Add ledger-tool bigtable subcommands

(cherry picked from commit f9049d6ee4)

# Conflicts:
#	ledger-tool/Cargo.toml

* Make room for tokio 0.2

(cherry picked from commit b876fb84ba)

# Conflicts:
#	core/Cargo.toml

* Setup a tokio 0.2 runtime for RPC usage

(cherry picked from commit 0e02740565)

# Conflicts:
#	core/Cargo.toml

* Plumb Bigtable ledger storage into the RPC subsystem

(cherry picked from commit dfae9a9864)

# Conflicts:
#	core/Cargo.toml

* Add RPC transaction history design

(cherry picked from commit e56ea138c7)

* Simplify access token refreshing

(cherry picked from commit 1f7af14386)

* Report block status more frequently

(cherry picked from commit 22c46ebf96)

* after -> before

(cherry picked from commit 227ea934ff)

* Rebase

* Cargo.lock

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

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

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

(cherry picked from commit 1b2276520b)

* Reimplement transaction-history command with getConfirmedSignaturesForAddress2

(cherry picked from commit 087fd32ce3)

* Rework get_confirmed_signatures_for_address2

(cherry picked from commit a11f137810)

* Rename startAfter to before

(cherry picked from commit 02c0981ecf)

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

View File

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

View File

@ -3,16 +3,19 @@
#
# Save target/ for the next CI build on this machine
#
(
if [[ -z $CARGO_TARGET_CACHE ]]; then
echo "+++ CARGO_TARGET_CACHE not defined" # pre-command should have defined it
else
(
set -x
d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
mkdir -p "$d"
mkdir -p "$CARGO_TARGET_CACHE"
set -x
rsync -a --delete --link-dest="$PWD" target "$d"
du -hs "$d"
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$d")
echo "--- ${cacheSizeInGB}GB: $d"
)
rsync -a --delete --link-dest="$PWD" target "$CARGO_TARGET_CACHE"
du -hs "$CARGO_TARGET_CACHE"
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$CARGO_TARGET_CACHE")
echo "--- ${cacheSizeInGB}GB: $CARGO_TARGET_CACHE"
)
fi
#
# Add job_stats data point

View File

@ -11,23 +11,29 @@ export PS4="++"
#
# Restore target/ from the previous CI build on this machine
#
eval "$(ci/channel-info.sh)"
export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
(
set -x
d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
MAX_CACHE_SIZE=18 # gigabytes
if [[ -d $d ]]; then
du -hs "$d"
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$d")
echo "--- ${cacheSizeInGB}GB: $d"
if [[ -d $CARGO_TARGET_CACHE ]]; then
du -hs "$CARGO_TARGET_CACHE"
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$CARGO_TARGET_CACHE")
echo "--- ${cacheSizeInGB}GB: $CARGO_TARGET_CACHE"
if [[ $cacheSizeInGB -gt $MAX_CACHE_SIZE ]]; then
echo "--- $d is too large, removing it"
rm -rf "$d"
echo "--- $CARGO_TARGET_CACHE is too large, removing it"
rm -rf "$CARGO_TARGET_CACHE"
fi
else
echo "--- $d not present"
echo "--- $CARGO_TARGET_CACHE not present"
fi
mkdir -p "$d"/target
rsync -a --delete --link-dest="$d" "$d"/target .
mkdir -p "$CARGO_TARGET_CACHE"/target
rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target .
# Don't reuse BPF target build artifacts due to incremental build issues with
# `std:
# "found possibly newer version of crate `std` which `xyz` depends on
rm -rf target/bpfel-unknown-unknown
)

View File

@ -67,7 +67,8 @@ jobs:
# explorer pull request
- name: "explorer"
if: type = pull_request
if: type = pull_request AND branch = master
language: node_js
node_js:
- "node"
@ -86,7 +87,8 @@ jobs:
# web3.js pull request
- name: "web3.js"
if: type = pull_request
if: type = pull_request AND branch = master
language: node_js
node_js:
- "lts/*"
@ -122,6 +124,8 @@ jobs:
- ~/.npm
before_install:
- source ci/env.sh
- .travis/channel_restriction.sh edge beta || travis_terminate 0
- .travis/affects.sh docs/ .travis || travis_terminate 0
- cd docs/
- source .travis/before_install.sh

19
.travis/channel_restriction.sh Executable file
View File

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

2108
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,12 @@ members = [
"bench-tps",
"accounts-bench",
"banking-bench",
"banks-client",
"banks-interface",
"banks-server",
"clap-utils",
"cli-config",
"cli-output",
"client",
"core",
"dos",
@ -15,7 +19,6 @@ members = [
"perf",
"validator",
"genesis",
"genesis-programs",
"gossip",
"install",
"keygen",
@ -26,12 +29,14 @@ members = [
"log-analyzer",
"merkle-tree",
"stake-o-matic",
"storage-bigtable",
"streamer",
"measure",
"metrics",
"net-shaper",
"notifier",
"poh-bench",
"programs/secp256k1",
"programs/bpf_loader",
"programs/budget",
"programs/config",

View File

@ -61,8 +61,9 @@ $ cargo test
### Starting a local testnet
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/bench-tps).
### Accessing the remote testnet
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
### Accessing the remote development cluster
* `devnet` - stable public cluster for development accessible via
devnet.solana.com. Runs 24/7. Learn more about the [public clusters](https://docs.solana.com/clusters)
# Benchmarking

View File

@ -1,6 +1,6 @@
[package]
name = "solana-account-decoder"
version = "1.3.0"
version = "1.3.24"
description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@ -9,16 +9,20 @@ license = "Apache-2.0"
edition = "2018"
[dependencies]
base64 = "0.12.3"
bincode = "1.3.1"
bs58 = "0.3.1"
bv = "0.11.1"
Inflector = "0.11.4"
lazy_static = "1.4.0"
solana-sdk = { path = "../sdk", version = "1.3.0" }
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
spl-token-v1-0 = { package = "spl-token", version = "1.0.6", features = ["skip-no-mangle"] }
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-config-program = { path = "../programs/config", version = "1.3.24" }
solana-sdk = { path = "../sdk", version = "1.3.24" }
solana-stake-program = { path = "../programs/stake", version = "1.3.24" }
solana-vote-program = { path = "../programs/vote", version = "1.3.24" }
spl-token-v2-0 = { package = "spl-token", version = "=2.0.6", features = ["skip-no-mangle"] }
thiserror = "1.0"
[package.metadata.docs.rs]

View File

@ -4,14 +4,20 @@ extern crate lazy_static;
extern crate serde_derive;
pub mod parse_account_data;
pub mod parse_config;
pub mod parse_nonce;
pub mod parse_stake;
pub mod parse_sysvar;
pub mod parse_token;
pub mod parse_vote;
pub mod validator_info;
use crate::parse_account_data::{parse_account_data, ParsedAccount};
use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey};
use crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount};
use solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey};
use std::str::FromStr;
pub type StringAmount = String;
/// A duplicate representation of an Account for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
@ -26,32 +32,47 @@ pub struct UiAccount {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)]
pub enum UiAccountData {
Binary(String),
LegacyBinary(String), // Legacy. Retained for RPC backwards compatibility
Json(ParsedAccount),
}
impl From<Vec<u8>> for UiAccountData {
fn from(data: Vec<u8>) -> Self {
Self::Binary(bs58::encode(data).into_string())
}
Binary(String, UiAccountEncoding),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum UiAccountEncoding {
Binary,
Binary, // Legacy. Retained for RPC backwards compatibility
Base58,
Base64,
JsonParsed,
}
impl UiAccount {
pub fn encode(account: Account, encoding: UiAccountEncoding) -> Self {
pub fn encode(
pubkey: &Pubkey,
account: Account,
encoding: UiAccountEncoding,
additional_data: Option<AccountAdditionalData>,
data_slice_config: Option<UiDataSliceConfig>,
) -> Self {
let data = match encoding {
UiAccountEncoding::Binary => account.data.into(),
UiAccountEncoding::Binary => UiAccountData::LegacyBinary(
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
),
UiAccountEncoding::Base58 => UiAccountData::Binary(
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
encoding,
),
UiAccountEncoding::Base64 => UiAccountData::Binary(
base64::encode(slice_data(&account.data, data_slice_config)),
encoding,
),
UiAccountEncoding::JsonParsed => {
if let Ok(parsed_data) = parse_account_data(&account.owner, &account.data) {
if let Ok(parsed_data) =
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
{
UiAccountData::Json(parsed_data)
} else {
account.data.into()
UiAccountData::Binary(base64::encode(&account.data), UiAccountEncoding::Base64)
}
}
};
@ -67,7 +88,12 @@ impl UiAccount {
pub fn decode(&self) -> Option<Account> {
let data = match &self.data {
UiAccountData::Json(_) => None,
UiAccountData::Binary(blob) => bs58::decode(blob).into_vec().ok(),
UiAccountData::LegacyBinary(blob) => bs58::decode(blob).into_vec().ok(),
UiAccountData::Binary(blob, encoding) => match encoding {
UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(),
UiAccountEncoding::Base64 => base64::decode(blob).ok(),
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
},
}?;
Some(Account {
lamports: self.lamports,
@ -78,3 +104,79 @@ impl UiAccount {
})
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiFeeCalculator {
pub lamports_per_signature: StringAmount,
}
impl From<FeeCalculator> for UiFeeCalculator {
fn from(fee_calculator: FeeCalculator) -> Self {
Self {
lamports_per_signature: fee_calculator.lamports_per_signature.to_string(),
}
}
}
impl Default for UiFeeCalculator {
fn default() -> Self {
Self {
lamports_per_signature: "0".to_string(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiDataSliceConfig {
pub offset: usize,
pub length: usize,
}
fn slice_data(data: &[u8], data_slice_config: Option<UiDataSliceConfig>) -> &[u8] {
if let Some(UiDataSliceConfig { offset, length }) = data_slice_config {
if offset >= data.len() {
&[]
} else if length > data.len() - offset {
&data[offset..]
} else {
&data[offset..offset + length]
}
} else {
data
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_slice_data() {
let data = vec![1, 2, 3, 4, 5];
let slice_config = Some(UiDataSliceConfig {
offset: 0,
length: 5,
});
assert_eq!(slice_data(&data, slice_config), &data[..]);
let slice_config = Some(UiDataSliceConfig {
offset: 0,
length: 10,
});
assert_eq!(slice_data(&data, slice_config), &data[..]);
let slice_config = Some(UiDataSliceConfig {
offset: 1,
length: 2,
});
assert_eq!(slice_data(&data, slice_config), &data[1..3]);
let slice_config = Some(UiDataSliceConfig {
offset: 10,
length: 2,
});
assert_eq!(slice_data(&data, slice_config), &[] as &[u8]);
}
}

View File

@ -1,22 +1,31 @@
use crate::{
parse_config::parse_config,
parse_nonce::parse_nonce,
parse_token::{parse_token, spl_token_id_v1_0},
parse_stake::parse_stake,
parse_sysvar::parse_sysvar,
parse_token::{parse_token, spl_token_id_v2_0},
parse_vote::parse_vote,
};
use inflector::Inflector;
use serde_json::Value;
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program};
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program, sysvar};
use std::collections::HashMap;
use thiserror::Error;
lazy_static! {
static ref CONFIG_PROGRAM_ID: Pubkey = solana_config_program::id();
static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id();
static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v1_0();
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(*CONFIG_PROGRAM_ID, ParsableAccount::Config);
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::SplToken);
m.insert(*STAKE_PROGRAM_ID, ParsableAccount::Stake);
m.insert(*SYSVAR_PROGRAM_ID, ParsableAccount::Sysvar);
m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
m
};
@ -30,6 +39,9 @@ pub enum ParseAccountError {
#[error("Program not parsable")]
ProgramNotParsable,
#[error("Additional data required to parse: {0}")]
AdditionalDataMissing(String),
#[error("Instruction error")]
InstructionError(#[from] InstructionError),
@ -42,31 +54,49 @@ pub enum ParseAccountError {
pub struct ParsedAccount {
pub program: String,
pub parsed: Value,
pub space: u64,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ParsableAccount {
Config,
Nonce,
SplToken,
Stake,
Sysvar,
Vote,
}
#[derive(Default)]
pub struct AccountAdditionalData {
pub spl_token_decimals: Option<u8>,
}
pub fn parse_account_data(
pubkey: &Pubkey,
program_id: &Pubkey,
data: &[u8],
additional_data: Option<AccountAdditionalData>,
) -> Result<ParsedAccount, ParseAccountError> {
let program_name = PARSABLE_PROGRAM_IDS
.get(program_id)
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
let additional_data = additional_data.unwrap_or_default();
let parsed_json = match program_name {
ParsableAccount::Config => serde_json::to_value(parse_config(data, pubkey)?)?,
ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
ParsableAccount::SplToken => serde_json::to_value(parse_token(data)?)?,
ParsableAccount::SplToken => {
serde_json::to_value(parse_token(data, additional_data.spl_token_decimals)?)?
}
ParsableAccount::Stake => serde_json::to_value(parse_stake(data)?)?,
ParsableAccount::Sysvar => serde_json::to_value(parse_sysvar(data, pubkey)?)?,
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
};
Ok(ParsedAccount {
program: format!("{:?}", program_name).to_kebab_case(),
parsed: parsed_json,
space: data.len() as u64,
})
}
@ -81,20 +111,35 @@ mod test {
#[test]
fn test_parse_account_data() {
let other_program = Pubkey::new_rand();
let account_pubkey = solana_sdk::pubkey::new_rand();
let other_program = solana_sdk::pubkey::new_rand();
let data = vec![0; 4];
assert!(parse_account_data(&other_program, &data).is_err());
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
let vote_state = VoteState::default();
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
let versioned = VoteStateVersions::Current(Box::new(vote_state));
VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
let parsed = parse_account_data(&solana_vote_program::id(), &vote_account_data).unwrap();
let parsed = parse_account_data(
&account_pubkey,
&solana_vote_program::id(),
&vote_account_data,
None,
)
.unwrap();
assert_eq!(parsed.program, "vote".to_string());
assert_eq!(parsed.space, VoteState::size_of() as u64);
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
let parsed = parse_account_data(&system_program::id(), &nonce_account_data).unwrap();
let parsed = parse_account_data(
&account_pubkey,
&system_program::id(),
&nonce_account_data,
None,
)
.unwrap();
assert_eq!(parsed.program, "nonce".to_string());
assert_eq!(parsed.space, State::size() as u64);
}
}

View File

@ -0,0 +1,146 @@
use crate::{
parse_account_data::{ParsableAccount, ParseAccountError},
validator_info,
};
use bincode::deserialize;
use serde_json::Value;
use solana_config_program::{get_config_data, ConfigKeys};
use solana_sdk::pubkey::Pubkey;
use solana_stake_program::config::Config as StakeConfig;
pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
let parsed_account = if pubkey == &solana_stake_program::config::id() {
get_config_data(data)
.ok()
.and_then(|data| deserialize::<StakeConfig>(data).ok())
.map(|config| ConfigAccountType::StakeConfig(config.into()))
} else {
deserialize::<ConfigKeys>(data).ok().and_then(|key_list| {
if !key_list.keys.is_empty() && key_list.keys[0].0 == validator_info::id() {
parse_config_data::<String>(data, key_list.keys).and_then(|validator_info| {
Some(ConfigAccountType::ValidatorInfo(UiConfig {
keys: validator_info.keys,
config_data: serde_json::from_str(&validator_info.config_data).ok()?,
}))
})
} else {
None
}
})
};
parsed_account.ok_or(ParseAccountError::AccountNotParsable(
ParsableAccount::Config,
))
}
fn parse_config_data<T>(data: &[u8], keys: Vec<(Pubkey, bool)>) -> Option<UiConfig<T>>
where
T: serde::de::DeserializeOwned,
{
let config_data: T = deserialize(&get_config_data(data).ok()?).ok()?;
let keys = keys
.iter()
.map(|key| UiConfigKey {
pubkey: key.0.to_string(),
signer: key.1,
})
.collect();
Some(UiConfig { keys, config_data })
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum ConfigAccountType {
StakeConfig(UiStakeConfig),
ValidatorInfo(UiConfig<Value>),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiConfigKey {
pub pubkey: String,
pub signer: bool,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiStakeConfig {
pub warmup_cooldown_rate: f64,
pub slash_penalty: u8,
}
impl From<StakeConfig> for UiStakeConfig {
fn from(config: StakeConfig) -> Self {
Self {
warmup_cooldown_rate: config.warmup_cooldown_rate,
slash_penalty: config.slash_penalty,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiConfig<T> {
pub keys: Vec<UiConfigKey>,
pub config_data: T,
}
#[cfg(test)]
mod test {
use super::*;
use crate::validator_info::ValidatorInfo;
use serde_json::json;
use solana_config_program::create_config_account;
#[test]
fn test_parse_config() {
let stake_config = StakeConfig {
warmup_cooldown_rate: 0.25,
slash_penalty: 50,
};
let stake_config_account = create_config_account(vec![], &stake_config, 10);
assert_eq!(
parse_config(
&stake_config_account.data,
&solana_stake_program::config::id()
)
.unwrap(),
ConfigAccountType::StakeConfig(UiStakeConfig {
warmup_cooldown_rate: 0.25,
slash_penalty: 50,
}),
);
let validator_info = ValidatorInfo {
info: serde_json::to_string(&json!({
"name": "Solana",
}))
.unwrap(),
};
let info_pubkey = solana_sdk::pubkey::new_rand();
let validator_info_config_account = create_config_account(
vec![(validator_info::id(), false), (info_pubkey, true)],
&validator_info,
10,
);
assert_eq!(
parse_config(&validator_info_config_account.data, &info_pubkey).unwrap(),
ConfigAccountType::ValidatorInfo(UiConfig {
keys: vec![
UiConfigKey {
pubkey: validator_info::id().to_string(),
signer: false,
},
UiConfigKey {
pubkey: info_pubkey.to_string(),
signer: true,
}
],
config_data: serde_json::from_str(r#"{"name":"Solana"}"#).unwrap(),
}),
);
let bad_data = vec![0; 4];
assert!(parse_config(&bad_data, &info_pubkey).is_err());
}
}

View File

@ -1,6 +1,5 @@
use crate::parse_account_data::ParseAccountError;
use crate::{parse_account_data::ParseAccountError, UiFeeCalculator};
use solana_sdk::{
fee_calculator::FeeCalculator,
instruction::InstructionError,
nonce::{state::Versions, State},
};
@ -14,7 +13,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
authority: data.authority.to_string(),
blockhash: data.blockhash.to_string(),
fee_calculator: data.fee_calculator,
fee_calculator: data.fee_calculator.into(),
})),
}
}
@ -32,7 +31,7 @@ pub enum UiNonceState {
pub struct UiNonceData {
pub authority: String,
pub blockhash: String,
pub fee_calculator: FeeCalculator,
pub fee_calculator: UiFeeCalculator,
}
#[cfg(test)]
@ -56,7 +55,9 @@ mod test {
UiNonceState::Initialized(UiNonceData {
authority: Pubkey::default().to_string(),
blockhash: Hash::default().to_string(),
fee_calculator: FeeCalculator::default(),
fee_calculator: UiFeeCalculator {
lamports_per_signature: 0.to_string(),
},
}),
);

View File

@ -0,0 +1,234 @@
use crate::{
parse_account_data::{ParsableAccount, ParseAccountError},
StringAmount,
};
use bincode::deserialize;
use solana_sdk::clock::{Epoch, UnixTimestamp};
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)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::Stake))?;
let parsed_account = match stake_state {
StakeState::Uninitialized => StakeAccountType::Uninitialized,
StakeState::Initialized(meta) => StakeAccountType::Initialized(UiStakeAccount {
meta: meta.into(),
stake: None,
}),
StakeState::Stake(meta, stake) => StakeAccountType::Delegated(UiStakeAccount {
meta: meta.into(),
stake: Some(stake.into()),
}),
StakeState::RewardsPool => StakeAccountType::RewardsPool,
};
Ok(parsed_account)
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum StakeAccountType {
Uninitialized,
Initialized(UiStakeAccount),
Delegated(UiStakeAccount),
RewardsPool,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiStakeAccount {
pub meta: UiMeta,
pub stake: Option<UiStake>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiMeta {
pub rent_exempt_reserve: StringAmount,
pub authorized: UiAuthorized,
pub lockup: UiLockup,
}
impl From<Meta> for UiMeta {
fn from(meta: Meta) -> Self {
Self {
rent_exempt_reserve: meta.rent_exempt_reserve.to_string(),
authorized: meta.authorized.into(),
lockup: meta.lockup.into(),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiLockup {
pub unix_timestamp: UnixTimestamp,
pub epoch: Epoch,
pub custodian: String,
}
impl From<Lockup> for UiLockup {
fn from(lockup: Lockup) -> Self {
Self {
unix_timestamp: lockup.unix_timestamp,
epoch: lockup.epoch,
custodian: lockup.custodian.to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiAuthorized {
pub staker: String,
pub withdrawer: String,
}
impl From<Authorized> for UiAuthorized {
fn from(authorized: Authorized) -> Self {
Self {
staker: authorized.staker.to_string(),
withdrawer: authorized.withdrawer.to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiStake {
pub delegation: UiDelegation,
pub credits_observed: u64,
}
impl From<Stake> for UiStake {
fn from(stake: Stake) -> Self {
Self {
delegation: stake.delegation.into(),
credits_observed: stake.credits_observed,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiDelegation {
pub voter: String,
pub stake: StringAmount,
pub activation_epoch: StringAmount,
pub deactivation_epoch: StringAmount,
pub warmup_cooldown_rate: f64,
}
impl From<Delegation> for UiDelegation {
fn from(delegation: Delegation) -> Self {
Self {
voter: delegation.voter_pubkey.to_string(),
stake: delegation.stake.to_string(),
activation_epoch: delegation.activation_epoch.to_string(),
deactivation_epoch: delegation.deactivation_epoch.to_string(),
warmup_cooldown_rate: delegation.warmup_cooldown_rate,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use bincode::serialize;
#[test]
fn test_parse_stake() {
let stake_state = StakeState::Uninitialized;
let stake_data = serialize(&stake_state).unwrap();
assert_eq!(
parse_stake(&stake_data).unwrap(),
StakeAccountType::Uninitialized
);
let pubkey = solana_sdk::pubkey::new_rand();
let custodian = solana_sdk::pubkey::new_rand();
let authorized = Authorized::auto(&pubkey);
let lockup = Lockup {
unix_timestamp: 0,
epoch: 1,
custodian,
};
let meta = Meta {
rent_exempt_reserve: 42,
authorized,
lockup,
};
let stake_state = StakeState::Initialized(meta);
let stake_data = serialize(&stake_state).unwrap();
assert_eq!(
parse_stake(&stake_data).unwrap(),
StakeAccountType::Initialized(UiStakeAccount {
meta: UiMeta {
rent_exempt_reserve: 42.to_string(),
authorized: UiAuthorized {
staker: pubkey.to_string(),
withdrawer: pubkey.to_string(),
},
lockup: UiLockup {
unix_timestamp: 0,
epoch: 1,
custodian: custodian.to_string(),
}
},
stake: None,
})
);
let voter_pubkey = solana_sdk::pubkey::new_rand();
let stake = Stake {
delegation: Delegation {
voter_pubkey,
stake: 20,
activation_epoch: 2,
deactivation_epoch: std::u64::MAX,
warmup_cooldown_rate: 0.25,
},
credits_observed: 10,
};
let stake_state = StakeState::Stake(meta, stake);
let stake_data = serialize(&stake_state).unwrap();
assert_eq!(
parse_stake(&stake_data).unwrap(),
StakeAccountType::Delegated(UiStakeAccount {
meta: UiMeta {
rent_exempt_reserve: 42.to_string(),
authorized: UiAuthorized {
staker: pubkey.to_string(),
withdrawer: pubkey.to_string(),
},
lockup: UiLockup {
unix_timestamp: 0,
epoch: 1,
custodian: custodian.to_string(),
}
},
stake: Some(UiStake {
delegation: UiDelegation {
voter: voter_pubkey.to_string(),
stake: 20.to_string(),
activation_epoch: 2.to_string(),
deactivation_epoch: std::u64::MAX.to_string(),
warmup_cooldown_rate: 0.25,
},
credits_observed: 10,
})
})
);
let stake_state = StakeState::RewardsPool;
let stake_data = serialize(&stake_state).unwrap();
assert_eq!(
parse_stake(&stake_data).unwrap(),
StakeAccountType::RewardsPool
);
let bad_data = vec![1, 2, 3, 4];
assert!(parse_stake(&bad_data).is_err());
}
}

View File

@ -0,0 +1,328 @@
use crate::{
parse_account_data::{ParsableAccount, ParseAccountError},
StringAmount, UiFeeCalculator,
};
use bincode::deserialize;
use bv::BitVec;
use solana_sdk::{
clock::{Clock, Epoch, Slot, UnixTimestamp},
epoch_schedule::EpochSchedule,
pubkey::Pubkey,
rent::Rent,
slot_hashes::SlotHashes,
slot_history::{self, SlotHistory},
stake_history::{StakeHistory, StakeHistoryEntry},
sysvar::{self, fees::Fees, recent_blockhashes::RecentBlockhashes, rewards::Rewards},
};
pub fn parse_sysvar(data: &[u8], pubkey: &Pubkey) -> Result<SysvarAccountType, ParseAccountError> {
let parsed_account = {
if pubkey == &sysvar::clock::id() {
deserialize::<Clock>(data)
.ok()
.map(|clock| SysvarAccountType::Clock(clock.into()))
} else if pubkey == &sysvar::epoch_schedule::id() {
deserialize(data).ok().map(SysvarAccountType::EpochSchedule)
} else if pubkey == &sysvar::fees::id() {
deserialize::<Fees>(data)
.ok()
.map(|fees| SysvarAccountType::Fees(fees.into()))
} else if pubkey == &sysvar::recent_blockhashes::id() {
deserialize::<RecentBlockhashes>(data)
.ok()
.map(|recent_blockhashes| {
let recent_blockhashes = recent_blockhashes
.iter()
.map(|entry| UiRecentBlockhashesEntry {
blockhash: entry.blockhash.to_string(),
fee_calculator: entry.fee_calculator.clone().into(),
})
.collect();
SysvarAccountType::RecentBlockhashes(recent_blockhashes)
})
} else if pubkey == &sysvar::rent::id() {
deserialize::<Rent>(data)
.ok()
.map(|rent| SysvarAccountType::Rent(rent.into()))
} else if pubkey == &sysvar::rewards::id() {
deserialize::<Rewards>(data)
.ok()
.map(|rewards| SysvarAccountType::Rewards(rewards.into()))
} else if pubkey == &sysvar::slot_hashes::id() {
deserialize::<SlotHashes>(data).ok().map(|slot_hashes| {
let slot_hashes = slot_hashes
.iter()
.map(|slot_hash| UiSlotHashEntry {
slot: slot_hash.0,
hash: slot_hash.1.to_string(),
})
.collect();
SysvarAccountType::SlotHashes(slot_hashes)
})
} else if pubkey == &sysvar::slot_history::id() {
deserialize::<SlotHistory>(data).ok().map(|slot_history| {
SysvarAccountType::SlotHistory(UiSlotHistory {
next_slot: slot_history.next_slot,
bits: format!("{:?}", SlotHistoryBits(slot_history.bits)),
})
})
} else if pubkey == &sysvar::stake_history::id() {
deserialize::<StakeHistory>(data).ok().map(|stake_history| {
let stake_history = stake_history
.iter()
.map(|entry| UiStakeHistoryEntry {
epoch: entry.0,
stake_history: entry.1.clone(),
})
.collect();
SysvarAccountType::StakeHistory(stake_history)
})
} else {
None
}
};
parsed_account.ok_or(ParseAccountError::AccountNotParsable(
ParsableAccount::Sysvar,
))
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum SysvarAccountType {
Clock(UiClock),
EpochSchedule(EpochSchedule),
Fees(UiFees),
RecentBlockhashes(Vec<UiRecentBlockhashesEntry>),
Rent(UiRent),
Rewards(UiRewards),
SlotHashes(Vec<UiSlotHashEntry>),
SlotHistory(UiSlotHistory),
StakeHistory(Vec<UiStakeHistoryEntry>),
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct UiClock {
pub slot: Slot,
pub epoch: Epoch,
pub leader_schedule_epoch: Epoch,
pub unix_timestamp: UnixTimestamp,
}
impl From<Clock> for UiClock {
fn from(clock: Clock) -> Self {
Self {
slot: clock.slot,
epoch: clock.epoch,
leader_schedule_epoch: clock.leader_schedule_epoch,
unix_timestamp: clock.unix_timestamp,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct UiFees {
pub fee_calculator: UiFeeCalculator,
}
impl From<Fees> for UiFees {
fn from(fees: Fees) -> Self {
Self {
fee_calculator: fees.fee_calculator.into(),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct UiRent {
pub lamports_per_byte_year: StringAmount,
pub exemption_threshold: f64,
pub burn_percent: u8,
}
impl From<Rent> for UiRent {
fn from(rent: Rent) -> Self {
Self {
lamports_per_byte_year: rent.lamports_per_byte_year.to_string(),
exemption_threshold: rent.exemption_threshold,
burn_percent: rent.burn_percent,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct UiRewards {
pub validator_point_value: f64,
}
impl From<Rewards> for UiRewards {
fn from(rewards: Rewards) -> Self {
Self {
validator_point_value: rewards.validator_point_value,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiRecentBlockhashesEntry {
pub blockhash: String,
pub fee_calculator: UiFeeCalculator,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiSlotHashEntry {
pub slot: Slot,
pub hash: String,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiSlotHistory {
pub next_slot: Slot,
pub bits: String,
}
struct SlotHistoryBits(BitVec<u64>);
impl std::fmt::Debug for SlotHistoryBits {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for i in 0..slot_history::MAX_ENTRIES {
if self.0.get(i) {
write!(f, "1")?;
} else {
write!(f, "0")?;
}
}
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiStakeHistoryEntry {
pub epoch: Epoch,
pub stake_history: StakeHistoryEntry,
}
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::{
fee_calculator::FeeCalculator,
hash::Hash,
sysvar::{recent_blockhashes::IterItem, Sysvar},
};
use std::iter::FromIterator;
#[test]
fn test_parse_sysvars() {
let clock_sysvar = Clock::default().create_account(1);
assert_eq!(
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
SysvarAccountType::Clock(UiClock::default()),
);
let epoch_schedule = EpochSchedule {
slots_per_epoch: 12,
leader_schedule_slot_offset: 0,
warmup: false,
first_normal_epoch: 1,
first_normal_slot: 12,
};
let epoch_schedule_sysvar = epoch_schedule.create_account(1);
assert_eq!(
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
SysvarAccountType::EpochSchedule(epoch_schedule),
);
let fees_sysvar = Fees::default().create_account(1);
assert_eq!(
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
SysvarAccountType::Fees(UiFees::default()),
);
let hash = Hash::new(&[1; 32]);
let fee_calculator = FeeCalculator {
lamports_per_signature: 10,
};
let recent_blockhashes =
RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter());
let recent_blockhashes_sysvar = recent_blockhashes.create_account(1);
assert_eq!(
parse_sysvar(
&recent_blockhashes_sysvar.data,
&sysvar::recent_blockhashes::id()
)
.unwrap(),
SysvarAccountType::RecentBlockhashes(vec![UiRecentBlockhashesEntry {
blockhash: hash.to_string(),
fee_calculator: fee_calculator.into(),
}]),
);
let rent = Rent {
lamports_per_byte_year: 10,
exemption_threshold: 2.0,
burn_percent: 5,
};
let rent_sysvar = rent.create_account(1);
assert_eq!(
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
SysvarAccountType::Rent(rent.into()),
);
let rewards_sysvar = Rewards::default().create_account(1);
assert_eq!(
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
SysvarAccountType::Rewards(UiRewards::default()),
);
let mut slot_hashes = SlotHashes::default();
slot_hashes.add(1, hash);
let slot_hashes_sysvar = slot_hashes.create_account(1);
assert_eq!(
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
slot: 1,
hash: hash.to_string(),
}]),
);
let mut slot_history = SlotHistory::default();
slot_history.add(42);
let slot_history_sysvar = slot_history.create_account(1);
assert_eq!(
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
SysvarAccountType::SlotHistory(UiSlotHistory {
next_slot: slot_history.next_slot,
bits: format!("{:?}", SlotHistoryBits(slot_history.bits)),
}),
);
let mut stake_history = StakeHistory::default();
let stake_history_entry = StakeHistoryEntry {
effective: 10,
activating: 2,
deactivating: 3,
};
stake_history.add(1, stake_history_entry.clone());
let stake_history_sysvar = stake_history.create_account(1);
assert_eq!(
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
epoch: 1,
stake_history: stake_history_entry,
}]),
);
let bad_pubkey = solana_sdk::pubkey::new_rand();
assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
let bad_data = vec![0; 4];
assert!(parse_sysvar(&bad_data, &sysvar::stake_history::id()).is_err());
}
}

View File

@ -1,54 +1,83 @@
use crate::parse_account_data::{ParsableAccount, ParseAccountError};
use solana_sdk::pubkey::Pubkey;
use spl_token_v1_0::{
option::COption,
solana_sdk::pubkey::Pubkey as SplTokenPubkey,
state::{unpack, Account, Mint, Multisig},
use crate::{
parse_account_data::{ParsableAccount, ParseAccountError},
StringAmount,
};
use std::{mem::size_of, str::FromStr};
use solana_sdk::pubkey::Pubkey;
use spl_token_v2_0::{
solana_sdk::{program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey},
state::{Account, AccountState, Mint, Multisig},
};
use std::str::FromStr;
// A helper function to convert spl_token_v1_0::id() as spl_sdk::pubkey::Pubkey to
// 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_v1_0() -> Pubkey {
Pubkey::from_str(&spl_token_v1_0::id().to_string()).unwrap()
pub fn spl_token_id_v2_0() -> Pubkey {
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
}
// A helper function to convert spl_token_v1_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
// 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_v1_0_native_mint() -> Pubkey {
Pubkey::from_str(&spl_token_v1_0::native_mint::id().to_string()).unwrap()
pub fn spl_token_v2_0_native_mint() -> Pubkey {
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
}
pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
let mut data = data.to_vec();
if data.len() == size_of::<Account>() {
let account: Account = *unpack(&mut data)
pub fn parse_token(
data: &[u8],
mint_decimals: Option<u8>,
) -> Result<TokenAccountType, ParseAccountError> {
if data.len() == Account::get_packed_len() {
let account = Account::unpack(data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
let decimals = mint_decimals.ok_or_else(|| {
ParseAccountError::AdditionalDataMissing(
"no mint_decimals provided to parse spl-token account".to_string(),
)
})?;
Ok(TokenAccountType::Account(UiTokenAccount {
mint: account.mint.to_string(),
owner: account.owner.to_string(),
amount: account.amount,
token_amount: token_amount_to_ui_amount(account.amount, decimals),
delegate: match account.delegate {
COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None,
},
is_initialized: account.is_initialized,
is_native: account.is_native,
delegated_amount: account.delegated_amount,
}))
} else if data.len() == size_of::<Mint>() {
let mint: Mint = *unpack(&mut data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
Ok(TokenAccountType::Mint(UiMint {
owner: match mint.owner {
state: account.state.into(),
is_native: account.is_native(),
rent_exempt_reserve: match account.is_native {
COption::Some(reserve) => Some(token_amount_to_ui_amount(reserve, decimals)),
COption::None => None,
},
delegated_amount: if account.delegate.is_none() {
None
} else {
Some(token_amount_to_ui_amount(
account.delegated_amount,
decimals,
))
},
close_authority: match account.close_authority {
COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None,
},
}))
} else if data.len() == Mint::get_packed_len() {
let mint = Mint::unpack(data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
Ok(TokenAccountType::Mint(UiMint {
mint_authority: match mint.mint_authority {
COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None,
},
supply: mint.supply.to_string(),
decimals: mint.decimals,
is_initialized: mint.is_initialized,
freeze_authority: match mint.freeze_authority {
COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None,
},
}))
} else if data.len() == size_of::<Multisig>() {
let multisig: Multisig = *unpack(&mut data)
} else if data.len() == Multisig::get_packed_len() {
let multisig = Multisig::unpack(data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
Ok(TokenAccountType::Multisig(UiMultisig {
num_required_signers: multisig.m,
@ -86,19 +115,88 @@ pub enum TokenAccountType {
pub struct UiTokenAccount {
pub mint: String,
pub owner: String,
pub amount: u64,
pub token_amount: UiTokenAmount,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegate: Option<String>,
pub is_initialized: bool,
pub state: UiAccountState,
pub is_native: bool,
pub delegated_amount: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub rent_exempt_reserve: Option<UiTokenAmount>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegated_amount: Option<UiTokenAmount>,
#[serde(skip_serializing_if = "Option::is_none")]
pub close_authority: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum UiAccountState {
Uninitialized,
Initialized,
Frozen,
}
impl From<AccountState> for UiAccountState {
fn from(state: AccountState) -> Self {
match state {
AccountState::Uninitialized => UiAccountState::Uninitialized,
AccountState::Initialized => UiAccountState::Initialized,
AccountState::Frozen => UiAccountState::Frozen,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiTokenAmount {
pub ui_amount: f64,
pub decimals: u8,
pub amount: StringAmount,
}
impl UiTokenAmount {
pub fn real_number_string(&self) -> String {
let decimals = self.decimals as usize;
if decimals > 0 {
let amount = u64::from_str(&self.amount).unwrap_or(0);
// Left-pad zeros to decimals + 1, so we at least have an integer zero
let mut s = format!("{:01$}", amount, decimals + 1);
// Add the decimal point (Sorry, "," locales!)
s.insert(s.len() - decimals, '.');
s
} else {
self.amount.clone()
}
}
pub fn real_number_string_trimmed(&self) -> String {
let s = self.real_number_string();
let zeros_trimmed = s.trim_end_matches('0');
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
decimal_trimmed.to_string()
}
}
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
// 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(),
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiMint {
pub owner: Option<String>,
pub mint_authority: Option<String>,
pub supply: StringAmount,
pub decimals: u8,
pub is_initialized: bool,
pub freeze_authority: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
@ -110,63 +208,89 @@ pub struct UiMultisig {
pub signers: Vec<String>,
}
pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> {
if data.len() == Account::get_packed_len() {
Some(Pubkey::new(&data[0..32]))
} else {
None
}
}
#[cfg(test)]
mod test {
use super::*;
use spl_token_v1_0::state::unpack_unchecked;
#[test]
fn test_parse_token() {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
let mut account_data = [0; size_of::<Account>()];
let mut account: &mut Account = unpack_unchecked(&mut account_data).unwrap();
let mut account_data = vec![0; Account::get_packed_len()];
let mut account = Account::unpack_unchecked(&account_data).unwrap();
account.mint = mint_pubkey;
account.owner = owner_pubkey;
account.amount = 42;
account.is_initialized = true;
account.state = AccountState::Initialized;
account.is_native = COption::None;
account.close_authority = COption::Some(owner_pubkey);
Account::pack(account, &mut account_data).unwrap();
assert!(parse_token(&account_data, None).is_err());
assert_eq!(
parse_token(&account_data).unwrap(),
parse_token(&account_data, Some(2)).unwrap(),
TokenAccountType::Account(UiTokenAccount {
mint: mint_pubkey.to_string(),
owner: owner_pubkey.to_string(),
amount: 42,
token_amount: UiTokenAmount {
ui_amount: 0.42,
decimals: 2,
amount: "42".to_string()
},
delegate: None,
is_initialized: true,
state: UiAccountState::Initialized,
is_native: false,
delegated_amount: 0,
rent_exempt_reserve: None,
delegated_amount: None,
close_authority: Some(owner_pubkey.to_string()),
}),
);
let mut mint_data = [0; size_of::<Mint>()];
let mut mint: &mut Mint = unpack_unchecked(&mut mint_data).unwrap();
mint.owner = COption::Some(owner_pubkey);
let mut mint_data = vec![0; Mint::get_packed_len()];
let mut mint = Mint::unpack_unchecked(&mint_data).unwrap();
mint.mint_authority = COption::Some(owner_pubkey);
mint.supply = 42;
mint.decimals = 3;
mint.is_initialized = true;
mint.freeze_authority = COption::Some(owner_pubkey);
Mint::pack(mint, &mut mint_data).unwrap();
assert_eq!(
parse_token(&mint_data).unwrap(),
parse_token(&mint_data, None).unwrap(),
TokenAccountType::Mint(UiMint {
owner: Some(owner_pubkey.to_string()),
mint_authority: Some(owner_pubkey.to_string()),
supply: 42.to_string(),
decimals: 3,
is_initialized: true,
freeze_authority: Some(owner_pubkey.to_string()),
}),
);
let signer1 = SplTokenPubkey::new(&[1; 32]);
let signer2 = SplTokenPubkey::new(&[2; 32]);
let signer3 = SplTokenPubkey::new(&[3; 32]);
let mut multisig_data = [0; size_of::<Multisig>()];
let mut multisig: &mut Multisig = unpack_unchecked(&mut multisig_data).unwrap();
let mut multisig_data = vec![0; Multisig::get_packed_len()];
let mut signers = [SplTokenPubkey::default(); 11];
signers[0] = signer1;
signers[1] = signer2;
signers[2] = signer3;
let mut multisig = Multisig::unpack_unchecked(&multisig_data).unwrap();
multisig.m = 2;
multisig.n = 3;
multisig.is_initialized = true;
multisig.signers = signers;
Multisig::pack(multisig, &mut multisig_data).unwrap();
assert_eq!(
parse_token(&multisig_data).unwrap(),
parse_token(&multisig_data, None).unwrap(),
TokenAccountType::Multisig(UiMultisig {
num_required_signers: 2,
num_valid_signers: 3,
@ -180,6 +304,37 @@ mod test {
);
let bad_data = vec![0; 4];
assert!(parse_token(&bad_data).is_err());
assert!(parse_token(&bad_data, None).is_err());
}
#[test]
fn test_get_token_account_mint() {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let mut account_data = vec![0; Account::get_packed_len()];
let mut account = Account::unpack_unchecked(&account_data).unwrap();
account.mint = mint_pubkey;
Account::pack(account, &mut account_data).unwrap();
let expected_mint_pubkey = Pubkey::new(&[2; 32]);
assert_eq!(
get_token_account_mint(&account_data),
Some(expected_mint_pubkey)
);
}
#[test]
fn test_ui_token_amount_real_string() {
let token_amount = token_amount_to_ui_amount(1, 0);
assert_eq!(&token_amount.real_number_string(), "1");
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
let token_amount = token_amount_to_ui_amount(1, 9);
assert_eq!(&token_amount.real_number_string(), "0.000000001");
assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001");
let token_amount = token_amount_to_ui_amount(1_000_000_000, 9);
assert_eq!(&token_amount.real_number_string(), "1.000000000");
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
let token_amount = token_amount_to_ui_amount(1_234_567_890, 3);
assert_eq!(&token_amount.real_number_string(), "1234567.890");
assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89");
}
}

View File

@ -1,4 +1,4 @@
use crate::parse_account_data::ParseAccountError;
use crate::{parse_account_data::ParseAccountError, StringAmount};
use solana_sdk::{
clock::{Epoch, Slot},
pubkey::Pubkey,
@ -12,8 +12,8 @@ pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
.iter()
.map(|(epoch, credits, previous_credits)| UiEpochCredits {
epoch: *epoch,
credits: *credits,
previous_credits: *previous_credits,
credits: credits.to_string(),
previous_credits: previous_credits.to_string(),
})
.collect();
let votes = vote_state
@ -115,8 +115,8 @@ struct UiPriorVoters {
#[serde(rename_all = "camelCase")]
struct UiEpochCredits {
epoch: Epoch,
credits: u64,
previous_credits: u64,
credits: StringAmount,
previous_credits: StringAmount,
}
#[cfg(test)]

View File

@ -0,0 +1,18 @@
use solana_config_program::ConfigState;
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
pub const MAX_VALIDATOR_INFO: u64 = 576;
solana_sdk::declare_id!("Va1idator1nfo111111111111111111111111111111");
#[derive(Debug, Deserialize, PartialEq, Serialize, Default)]
pub struct ValidatorInfo {
pub info: String,
}
impl ConfigState for ValidatorInfo {
fn max_space() -> u64 {
MAX_VALIDATOR_INFO
}
}

View File

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

View File

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

View File

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

View File

@ -20,7 +20,6 @@ use solana_perf::packet::to_packets_chunked;
use solana_runtime::{bank::Bank, bank_forks::BankForks};
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::Keypair,
signature::Signature,
system_transaction,
@ -69,7 +68,7 @@ fn make_accounts_txs(
hash: Hash,
same_payer: bool,
) -> Vec<Transaction> {
let to_pubkey = Pubkey::new_rand();
let to_pubkey = solana_sdk::pubkey::new_rand();
let payer_key = Keypair::new();
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
(0..total_num_transactions)
@ -78,9 +77,9 @@ fn make_accounts_txs(
let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
if !same_payer {
new.message.account_keys[0] = Pubkey::new_rand();
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
}
new.message.account_keys[1] = Pubkey::new_rand();
new.message.account_keys[1] = solana_sdk::pubkey::new_rand();
new.signatures = vec![Signature::new(&sig[0..64])];
new
})
@ -167,6 +166,7 @@ fn main() {
let (verified_sender, verified_receiver) = unbounded();
let (vote_sender, vote_receiver) = unbounded();
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
let bank0 = Bank::new(&genesis_config);
let mut bank_forks = BankForks::new(bank0);
let mut bank = bank_forks.working_bank();
@ -224,6 +224,7 @@ fn main() {
verified_receiver,
vote_receiver,
None,
replay_vote_sender,
);
poh_recorder.lock().unwrap().set_bank(&bank);
@ -239,7 +240,7 @@ fn main() {
let base_tx_count = bank.transaction_count();
let mut txs_processed = 0;
let mut root = 1;
let collector = Pubkey::new_rand();
let collector = solana_sdk::pubkey::new_rand();
let config = Config {
packets_per_batch: packets_per_chunk,
chunk_len,

31
banks-client/Cargo.toml Normal file
View File

@ -0,0 +1,31 @@
[package]
name = "solana-banks-client"
version = "1.3.24"
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/"
edition = "2018"
[dependencies]
async-trait = "0.1.36"
bincode = "1.3.1"
futures = "0.3"
mio = "0.7.6"
solana-banks-interface = { path = "../banks-interface", version = "1.3.24" }
solana-sdk = { path = "../sdk", version = "1.3.24" }
tarpc = { version = "0.21.0", features = ["full"] }
tokio = "0.2"
tokio-serde = { version = "0.6", features = ["bincode"] }
[dev-dependencies]
solana-runtime = { path = "../runtime", version = "1.3.24" }
solana-banks-server = { path = "../banks-server", version = "1.3.24" }
[lib]
crate-type = ["lib"]
name = "solana_banks_client"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

296
banks-client/src/lib.rs Normal file
View File

@ -0,0 +1,296 @@
//! A client for the ledger state, from the perspective of an arbitrary validator.
//!
//! Use start_tcp_client() to create a client and then import BanksClientExt to
//! access its methods. Additional "*_with_context" methods are also available,
//! but they are undocumented, may change over time, and are generally more
//! cumbersome to use.
use async_trait::async_trait;
use futures::future::join_all;
pub use solana_banks_interface::{BanksClient, TransactionStatus};
use solana_banks_interface::{BanksRequest, BanksResponse};
use solana_sdk::{
account::Account, clock::Slot, commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, signature::Signature,
transaction::Transaction, transport,
};
use std::io::{self, Error, ErrorKind};
use tarpc::{
client, context,
rpc::{transport::channel::UnboundedChannel, ClientMessage, Response},
serde_transport::tcp,
};
use tokio::{net::ToSocketAddrs, time::Duration};
use tokio_serde::formats::Bincode;
#[async_trait]
pub trait BanksClientExt {
/// Send a transaction and return immediately. The server will resend the
/// transaction until either it is accepted by the cluster or the transaction's
/// blockhash expires.
async fn send_transaction(&mut self, transaction: Transaction) -> io::Result<()>;
/// Return a recent, rooted blockhash from the server. The cluster will only accept
/// transactions with a blockhash that has not yet expired. Use the `get_fees`
/// method to get both a blockhash and the blockhash's last valid slot.
async fn get_recent_blockhash(&mut self) -> io::Result<Hash>;
/// Return the fee parameters associated with a recent, rooted blockhash. The cluster
/// will use the transaction's blockhash to look up these same fee parameters and
/// use them to calculate the transaction fee.
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)>;
/// Send a transaction and return after the transaction has been rejected or
/// reached the given level of commitment.
async fn process_transaction_with_commitment(
&mut self,
transaction: Transaction,
commitment: CommitmentLevel,
) -> transport::Result<()>;
/// Send a transaction and return after the transaction has been finalized or rejected.
async fn process_transaction(&mut self, transaction: Transaction) -> transport::Result<()>;
/// Return the status of a transaction with a signature matching the transaction's first
/// signature. Return None if the transaction is not found, which may be because the
/// blockhash was expired or the fee-paying account had insufficient funds to pay the
/// transaction fee. Note that servers rarely store the full transaction history. This
/// method may return None if the transaction status has been discarded.
async fn get_transaction_status(
&mut self,
signature: Signature,
) -> io::Result<Option<TransactionStatus>>;
/// Same as get_transaction_status, but for multiple transactions.
async fn get_transaction_statuses(
&mut self,
signatures: Vec<Signature>,
) -> io::Result<Vec<Option<TransactionStatus>>>;
/// Return the most recent rooted slot height. All transactions at or below this height
/// are said to be finalized. The cluster will not fork to a higher slot height.
async fn get_root_slot(&mut self) -> io::Result<Slot>;
/// Return the account at the given address at the slot corresponding to the given
/// commitment level. If the account is not found, None is returned.
async fn get_account_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<Option<Account>>;
/// Return the account at the given address at the time of the most recent root slot.
/// If the account is not found, None is returned.
async fn get_account(&mut self, address: Pubkey) -> io::Result<Option<Account>>;
/// Return the balance in lamports of an account at the given address at the slot
/// corresponding to the given commitment level.
async fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<u64>;
/// Return the balance in lamports of an account at the given address at the time
/// of the most recent root slot.
async fn get_balance(&mut self, address: Pubkey) -> io::Result<u64>;
}
#[async_trait]
impl BanksClientExt for BanksClient {
async fn send_transaction(&mut self, transaction: Transaction) -> io::Result<()> {
self.send_transaction_with_context(context::current(), transaction)
.await
}
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)> {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::Root)
.await
}
async fn get_recent_blockhash(&mut self) -> io::Result<Hash> {
Ok(self.get_fees().await?.1)
}
async fn process_transaction_with_commitment(
&mut self,
transaction: Transaction,
commitment: CommitmentLevel,
) -> transport::Result<()> {
let mut ctx = context::current();
ctx.deadline += Duration::from_secs(50);
let result = self
.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
.await?;
match result {
None => Err(Error::new(ErrorKind::TimedOut, "invalid blockhash or fee-payer").into()),
Some(transaction_result) => Ok(transaction_result?),
}
}
async fn process_transaction(&mut self, transaction: Transaction) -> transport::Result<()> {
self.process_transaction_with_commitment(transaction, CommitmentLevel::default())
.await
}
async fn get_root_slot(&mut self) -> io::Result<Slot> {
self.get_slot_with_context(context::current(), CommitmentLevel::Root)
.await
}
async fn get_account_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<Option<Account>> {
self.get_account_with_commitment_and_context(context::current(), address, commitment)
.await
}
async fn get_account(&mut self, address: Pubkey) -> io::Result<Option<Account>> {
self.get_account_with_commitment(address, CommitmentLevel::default())
.await
}
async fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<u64> {
let account = self
.get_account_with_commitment_and_context(context::current(), address, commitment)
.await?;
Ok(account.map(|x| x.lamports).unwrap_or(0))
}
async fn get_balance(&mut self, address: Pubkey) -> io::Result<u64> {
self.get_balance_with_commitment(address, CommitmentLevel::default())
.await
}
async fn get_transaction_status(
&mut self,
signature: Signature,
) -> io::Result<Option<TransactionStatus>> {
self.get_transaction_status_with_context(context::current(), signature)
.await
}
async fn get_transaction_statuses(
&mut self,
signatures: Vec<Signature>,
) -> io::Result<Vec<Option<TransactionStatus>>> {
// tarpc futures oddly hold a mutable reference back to the client so clone the client upfront
let mut clients_and_signatures: Vec<_> = signatures
.into_iter()
.map(|signature| (self.clone(), signature))
.collect();
let futs = clients_and_signatures
.iter_mut()
.map(|(client, signature)| client.get_transaction_status(*signature));
let statuses = join_all(futs).await;
// Convert Vec<Result<_, _>> to Result<Vec<_>>
statuses.into_iter().collect()
}
}
pub async fn start_client(
transport: UnboundedChannel<Response<BanksResponse>, ClientMessage<BanksRequest>>,
) -> io::Result<BanksClient> {
BanksClient::new(client::Config::default(), transport).spawn()
}
pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> io::Result<BanksClient> {
let transport = tcp::connect(addr, Bincode::default()).await?;
BanksClient::new(client::Config::default(), transport).spawn()
}
#[cfg(test)]
mod tests {
use super::*;
use solana_banks_server::banks_server::start_local_server;
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;
use tokio::{runtime::Runtime, time::delay_for};
#[test]
fn test_banks_client_new() {
let (client_transport, _server_transport) = transport::channel::unbounded();
BanksClient::new(client::Config::default(), client_transport);
}
#[test]
fn test_banks_server_transfer_via_server() -> io::Result<()> {
// This test shows the preferred way to interact with BanksServer.
// It creates a runtime explicitly (no globals via tokio macros) and calls
// `runtime.block_on()` just once, to run all the async code.
let genesis = create_genesis_config(10);
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();
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(&bank_forks).await;
let mut banks_client =
BanksClient::new(client::Config::default(), client_transport).spawn()?;
let recent_blockhash = banks_client.get_recent_blockhash().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
banks_client.process_transaction(transaction).await.unwrap();
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
Ok(())
})
}
#[test]
fn test_banks_server_transfer_via_client() -> io::Result<()> {
// The caller may not want to hold the connection open until the transaction
// is processed (or blockhash expires). In this test, we verify the
// server-side functionality is available to the client.
let genesis = create_genesis_config(10);
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();
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(&bank_forks).await;
let mut banks_client =
BanksClient::new(client::Config::default(), client_transport).spawn()?;
let (_, recent_blockhash, last_valid_slot) = banks_client.get_fees().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
let signature = transaction.signatures[0];
banks_client.send_transaction(transaction).await?;
let mut status = banks_client.get_transaction_status(signature).await?;
while status.is_none() {
let root_slot = banks_client.get_root_slot().await?;
if root_slot > last_valid_slot {
break;
}
delay_for(Duration::from_millis(100)).await;
status = banks_client.get_transaction_status(signature).await?;
}
assert!(status.unwrap().err.is_none());
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
Ok(())
})
}
}

View File

@ -0,0 +1,22 @@
[package]
name = "solana-banks-interface"
version = "1.3.24"
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/"
edition = "2018"
[dependencies]
mio = "0.7.6"
serde = { version = "1.0.112", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "1.3.24" }
tarpc = { version = "0.21.0", features = ["full"] }
[lib]
crate-type = ["lib"]
name = "solana_banks_interface"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};
use solana_sdk::{
account::Account,
clock::Slot,
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
signature::Signature,
transaction::{self, Transaction, TransactionError},
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TransactionStatus {
pub slot: Slot,
pub confirmations: Option<usize>, // None = rooted
pub err: Option<TransactionError>,
}
#[tarpc::service]
pub trait Banks {
async fn send_transaction_with_context(transaction: Transaction);
async fn get_fees_with_commitment_and_context(
commitment: CommitmentLevel,
) -> (FeeCalculator, Hash, Slot);
async fn get_transaction_status_with_context(signature: Signature)
-> Option<TransactionStatus>;
async fn get_slot_with_context(commitment: CommitmentLevel) -> Slot;
async fn process_transaction_with_commitment_and_context(
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>>;
async fn get_account_with_commitment_and_context(
address: Pubkey,
commitment: CommitmentLevel,
) -> Option<Account>;
}
#[cfg(test)]
mod tests {
use super::*;
use tarpc::{client, transport};
#[test]
fn test_banks_client_new() {
let (client_transport, _server_transport) = transport::channel::unbounded();
BanksClient::new(client::Config::default(), client_transport);
}
}

29
banks-server/Cargo.toml Normal file
View File

@ -0,0 +1,29 @@
[package]
name = "solana-banks-server"
version = "1.3.24"
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/"
edition = "2018"
[dependencies]
bincode = "1.3.1"
futures = "0.3"
log = "0.4.8"
mio = "0.7.6"
solana-banks-interface = { path = "../banks-interface", version = "1.3.24" }
solana-runtime = { path = "../runtime", version = "1.3.24" }
solana-sdk = { path = "../sdk", version = "1.3.24" }
solana-metrics = { path = "../metrics", version = "1.3.24" }
tarpc = { version = "0.21.0", features = ["full"] }
tokio = "0.2"
tokio-serde = { version = "0.6", features = ["bincode"] }
[lib]
crate-type = ["lib"]
name = "solana_banks_server"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,270 @@
use crate::send_transaction_service::{SendTransactionService, TransactionInfo};
use bincode::{deserialize, serialize};
use futures::{
future,
prelude::stream::{self, StreamExt},
};
use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus};
use solana_runtime::{
bank::Bank,
bank_forks::BankForks,
commitment::{BlockCommitmentCache, CommitmentSlots},
};
use solana_sdk::{
account::Account,
clock::Slot,
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
signature::Signature,
transaction::{self, Transaction},
};
use std::{
collections::HashMap,
io,
net::{Ipv4Addr, SocketAddr},
sync::{
mpsc::{channel, Receiver, Sender},
Arc, RwLock,
},
thread::Builder,
time::Duration,
};
use tarpc::{
context::Context,
rpc::{transport::channel::UnboundedChannel, ClientMessage, Response},
serde_transport::tcp,
server::{self, Channel, Handler},
transport,
};
use tokio::time::delay_for;
use tokio_serde::formats::Bincode;
#[derive(Clone)]
struct BanksServer {
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
transaction_sender: Sender<TransactionInfo>,
}
impl BanksServer {
/// Return a BanksServer that forwards transactions to the
/// given sender. If unit-testing, those transactions can go to
/// a bank in the given BankForks. Otherwise, the receiver should
/// forward them to a validator in the leader schedule.
fn new(
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
transaction_sender: Sender<TransactionInfo>,
) -> Self {
Self {
bank_forks,
block_commitment_cache,
transaction_sender,
}
}
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() {
transaction_infos.push(info);
}
let transactions: Vec<_> = transaction_infos
.into_iter()
.map(|info| deserialize(&info.wire_transaction).unwrap())
.collect();
let _ = bank.process_transactions(&transactions);
}
}
/// Useful for unit-testing
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();
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
HashMap::default(),
0,
CommitmentSlots {
slot,
root: 0,
highest_confirmed_slot: 0,
highest_confirmed_root: 0,
},
)));
Builder::new()
.name("solana-bank-forks-client".to_string())
.spawn(move || Self::run(&bank, transaction_receiver))
.unwrap();
Self::new(bank_forks, block_commitment_cache, transaction_sender)
}
fn slot(&self, commitment: CommitmentLevel) -> Slot {
self.block_commitment_cache
.read()
.unwrap()
.slot_with_commitment(commitment)
}
fn bank(&self, commitment: CommitmentLevel) -> Arc<Bank> {
self.bank_forks.read().unwrap()[self.slot(commitment)].clone()
}
async fn poll_signature_status(
self,
signature: Signature,
last_valid_slot: Slot,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
let mut status = self.bank(commitment).get_signature_status(&signature);
while status.is_none() {
delay_for(Duration::from_millis(200)).await;
let bank = self.bank(commitment);
if bank.slot() > last_valid_slot {
break;
}
status = bank.get_signature_status(&signature);
}
status
}
}
#[tarpc::server]
impl Banks for BanksServer {
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
let blockhash = &transaction.message.recent_blockhash;
let last_valid_slot = self
.bank_forks
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_slot(&blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap();
}
async fn get_fees_with_commitment_and_context(
self,
_: Context,
commitment: CommitmentLevel,
) -> (FeeCalculator, Hash, Slot) {
let bank = self.bank(commitment);
let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator();
let last_valid_slot = bank.get_blockhash_last_valid_slot(&blockhash).unwrap();
(fee_calculator, blockhash, last_valid_slot)
}
async fn get_transaction_status_with_context(
self,
_: Context,
signature: Signature,
) -> Option<TransactionStatus> {
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 confirmations = if r_block_commitment_cache.root() >= slot {
None
} else {
r_block_commitment_cache
.get_confirmation_count(slot)
.or(Some(0))
};
Some(TransactionStatus {
slot,
confirmations,
err: status.err(),
})
}
async fn get_slot_with_context(self, _: Context, commitment: CommitmentLevel) -> Slot {
self.slot(commitment)
}
async fn process_transaction_with_commitment_and_context(
self,
_: Context,
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
let blockhash = &transaction.message.recent_blockhash;
let last_valid_slot = self
.bank_forks
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_slot(&blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap();
self.poll_signature_status(signature, last_valid_slot, commitment)
.await
}
async fn get_account_with_commitment_and_context(
self,
_: Context,
address: Pubkey,
commitment: CommitmentLevel,
) -> Option<Account> {
let bank = self.bank(commitment);
bank.get_account(&address)
}
}
pub async fn start_local_server(
bank_forks: &Arc<RwLock<BankForks>>,
) -> UnboundedChannel<Response<BanksResponse>, ClientMessage<BanksRequest>> {
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)))
.respond_with(banks_server.serve());
tokio::spawn(server);
client_transport
}
pub async fn start_tcp_server(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) -> io::Result<()> {
// Note: These settings are copied straight from the tarpc example.
let server = tcp::listen(listen_addr, Bincode::default)
.await?
// Ignore accept errors.
.filter_map(|r| future::ready(r.ok()))
.map(server::BaseChannel::with_defaults)
// Limit channels to 1 per IP.
.max_channels_per_key(1, |t| {
t.as_ref()
.peer_addr()
.map(|x| x.ip())
.unwrap_or_else(|_| Ipv4Addr::new(0, 0, 0, 0).into())
})
// serve is generated by the service attribute. It takes as input any type implementing
// the generated Banks trait.
.map(move |chan| {
let (sender, receiver) = channel();
SendTransactionService::new(tpu_addr, &bank_forks, receiver);
let server =
BanksServer::new(bank_forks.clone(), block_commitment_cache.clone(), sender);
chan.respond_with(server.serve()).execute()
})
// Max 10 channels.
.buffer_unordered(10)
.for_each(|_| async {});
server.await;
Ok(())
}

6
banks-server/src/lib.rs Normal file
View File

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

View File

@ -0,0 +1,116 @@
//! The `rpc_banks_service` module implements the Solana Banks RPC API.
use crate::banks_server::start_tcp_server;
use futures::{future::FutureExt, pin_mut, prelude::stream::StreamExt, select};
use solana_runtime::{bank_forks::BankForks, commitment::BlockCommitmentCache};
use std::{
net::SocketAddr,
sync::{
atomic::{AtomicBool, Ordering},
Arc, RwLock,
},
thread::{self, Builder, JoinHandle},
};
use tokio::{
runtime::Runtime,
time::{self, Duration},
};
pub struct RpcBanksService {
thread_hdl: JoinHandle<()>,
}
/// Run the TCP service until `exit` is set to true
async fn start_abortable_tcp_server(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
exit: Arc<AtomicBool>,
) {
let server = start_tcp_server(
listen_addr,
tpu_addr,
bank_forks.clone(),
block_commitment_cache.clone(),
)
.fuse();
let interval = time::interval(Duration::from_millis(100)).fuse();
pin_mut!(server, interval);
loop {
select! {
_ = server => {},
_ = interval.select_next_some() => {
if exit.load(Ordering::Relaxed) {
break;
}
}
}
}
}
impl RpcBanksService {
fn run(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
exit: Arc<AtomicBool>,
) {
let server = start_abortable_tcp_server(
listen_addr,
tpu_addr,
bank_forks,
block_commitment_cache,
exit,
);
Runtime::new().unwrap().block_on(server);
}
pub fn new(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
exit: &Arc<AtomicBool>,
) -> Self {
let bank_forks = bank_forks.clone();
let block_commitment_cache = block_commitment_cache.clone();
let exit = exit.clone();
let thread_hdl = Builder::new()
.name("solana-rpc-banks".to_string())
.spawn(move || {
Self::run(
listen_addr,
tpu_addr,
bank_forks,
block_commitment_cache,
exit,
)
})
.unwrap();
Self { thread_hdl }
}
pub fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_runtime::bank::Bank;
#[test]
fn test_rpc_banks_server_exit() {
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::default())));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
let exit = Arc::new(AtomicBool::new(false));
let addr = "127.0.0.1:0".parse().unwrap();
let service = RpcBanksService::new(addr, addr, &bank_forks, &block_commitment_cache, &exit);
exit.store(true, Ordering::Relaxed);
service.join().unwrap();
}
}

View File

@ -1,13 +1,13 @@
use crate::{bank::Bank, bank_forks::BankForks};
// TODO: Merge this implementation with the one at `core/src/send_transaction_service.rs`
use log::*;
use solana_metrics::{datapoint_warn, inc_new_counter_info};
use solana_runtime::{bank::Bank, bank_forks::BankForks};
use solana_sdk::{clock::Slot, signature::Signature};
use std::{
collections::HashMap,
net::{SocketAddr, UdpSocket},
sync::{
atomic::{AtomicBool, Ordering},
mpsc::Receiver,
mpsc::{Receiver, RecvTimeoutError},
Arc, RwLock,
},
thread::{self, Builder, JoinHandle},
@ -22,9 +22,9 @@ pub struct SendTransactionService {
}
pub struct TransactionInfo {
signature: Signature,
wire_transaction: Vec<u8>,
last_valid_slot: Slot,
pub signature: Signature,
pub wire_transaction: Vec<u8>,
pub last_valid_slot: Slot,
}
impl TransactionInfo {
@ -50,10 +50,9 @@ impl SendTransactionService {
pub fn new(
tpu_address: SocketAddr,
bank_forks: &Arc<RwLock<BankForks>>,
exit: &Arc<AtomicBool>,
receiver: Receiver<TransactionInfo>,
) -> Self {
let thread = Self::retry_thread(receiver, bank_forks.clone(), tpu_address, exit.clone());
let thread = Self::retry_thread(receiver, bank_forks.clone(), tpu_address);
Self { thread }
}
@ -61,7 +60,6 @@ impl SendTransactionService {
receiver: Receiver<TransactionInfo>,
bank_forks: Arc<RwLock<BankForks>>,
tpu_address: SocketAddr,
exit: Arc<AtomicBool>,
) -> JoinHandle<()> {
let mut last_status_check = Instant::now();
let mut transactions = HashMap::new();
@ -70,11 +68,10 @@ impl SendTransactionService {
Builder::new()
.name("send-tx-svc".to_string())
.spawn(move || loop {
if exit.load(Ordering::Relaxed) {
break;
}
if let Ok(transaction_info) = receiver.recv_timeout(Duration::from_secs(1)) {
match receiver.recv_timeout(Duration::from_secs(1)) {
Err(RecvTimeoutError::Disconnected) => break,
Err(RecvTimeoutError::Timeout) => {}
Ok(transaction_info) => {
Self::send_transaction(
&send_socket,
&tpu_address,
@ -86,6 +83,7 @@ impl SendTransactionService {
datapoint_warn!("send_transaction_service-queue-overflow");
}
}
}
if Instant::now().duration_since(last_status_check).as_secs() >= 5 {
if !transactions.is_empty() {
@ -193,20 +191,17 @@ mod test {
let tpu_address = "127.0.0.1:0".parse().unwrap();
let bank = Bank::default();
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let exit = Arc::new(AtomicBool::new(false));
let (_sender, receiver) = channel();
let (sender, receiver) = channel();
let send_tranaction_service =
SendTransactionService::new(tpu_address, &bank_forks, &exit, receiver);
SendTransactionService::new(tpu_address, &bank_forks, receiver);
exit.store(true, Ordering::Relaxed);
drop(sender);
send_tranaction_service.join().unwrap();
}
#[test]
fn process_transactions() {
solana_logger::setup();
let (genesis_config, mint_keypair) = create_genesis_config(4);
let bank = Bank::new(&genesis_config);
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));

View File

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

View File

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

View File

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

View File

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

31
cargo Executable file
View File

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

View File

@ -89,11 +89,17 @@ BETA_CHANNEL_LATEST_TAG=${beta_tag:+v$beta_tag}
STABLE_CHANNEL_LATEST_TAG=${stable_tag:+v$stable_tag}
if [[ $CI_BRANCH = "$STABLE_CHANNEL" ]]; then
if [[ -n $CI_BASE_BRANCH ]]; then
BRANCH="$CI_BASE_BRANCH"
elif [[ -n $CI_BRANCH ]]; then
BRANCH="$CI_BRANCH"
fi
if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
CHANNEL=stable
elif [[ $CI_BRANCH = "$EDGE_CHANNEL" ]]; then
elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then
CHANNEL=edge
elif [[ $CI_BRANCH = "$BETA_CHANNEL" ]]; then
elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then
CHANNEL=beta
fi

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ source multinode-demo/common.sh
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
timeout 15 ./run.sh &
timeout 120 ./run.sh &
pid=$!
attempts=20

View File

@ -76,7 +76,7 @@ RestartForceExitStatus=SIGPIPE
TimeoutStartSec=10
TimeoutStopSec=0
KillMode=process
LimitNOFILE=65536
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
sed -i 's/^\(# End of file\)/* soft nofile 65535\n\n\1/' /etc/security/limits.conf
echo "* - nofile 500000" > /etc/security/limits.d/90-solana-nofiles.conf

View File

@ -9,6 +9,8 @@ source ci/rust-version.sh stable
source ci/rust-version.sh nightly
eval "$(ci/channel-info.sh)"
scripts/increment-cargo-version.sh check
echo --- build environment
(
set -x
@ -41,13 +43,14 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
exit "$check_status"
fi
# Ensure nightly and --benches
_ 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
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
_ ci/order-crates-for-publishing.py
_ cargo +"$rust_stable" fmt --all -- --check
@ -55,7 +58,34 @@ _ cargo +"$rust_stable" fmt --all -- --check
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
_ cargo +"$rust_nightly" clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
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
# memmap crate is unmaintained
#
# Blocked on us releasing new solana crates
--ignore RUSTSEC-2020-0077
)
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
{
cd programs/bpf

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ 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},
@ -178,6 +179,10 @@ pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
value_of(matches, name).map(sol_to_lamports)
}
pub fn cluster_type_of(matches: &ArgMatches<'_>, name: &str) -> Option<ClusterType> {
value_of(matches, name)
}
pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
matches.value_of(name).map(|value| match value {
"max" => CommitmentConfig::max(),
@ -223,8 +228,8 @@ mod tests {
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
assert_eq!(values_of::<u64>(&matches, "single"), None);
let pubkey0 = Pubkey::new_rand();
let pubkey1 = Pubkey::new_rand();
let pubkey0 = solana_sdk::pubkey::new_rand();
let pubkey1 = solana_sdk::pubkey::new_rand();
let matches = app().clone().get_matches_from(vec![
"test",
"--multiple",
@ -246,7 +251,7 @@ mod tests {
assert_eq!(value_of(&matches, "single"), Some(50));
assert_eq!(value_of::<u64>(&matches, "multiple"), None);
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
@ -326,8 +331,8 @@ mod tests {
#[test]
fn test_pubkeys_sigs_of() {
let key1 = Pubkey::new_rand();
let key2 = Pubkey::new_rand();
let key1 = solana_sdk::pubkey::new_rand();
let key2 = solana_sdk::pubkey::new_rand();
let sig1 = Keypair::new().sign_message(&[0u8]);
let sig2 = Keypair::new().sign_message(&[1u8]);
let signer1 = format!("{}={}", key1, sig1);

View File

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

View File

@ -24,7 +24,9 @@ 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 nonce;
pub mod offline;

50
clap-utils/src/nonce.rs Normal file
View File

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

View File

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

View File

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

View File

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

29
cli-output/Cargo.toml Normal file
View File

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

View File

@ -1,20 +1,26 @@
use crate::{
cli::build_balance_message,
display::{format_labeled_address, writeln_name_value},
display::{build_balance_message, format_labeled_address, writeln_name_value},
QuietDisplay, VerboseDisplay,
};
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use console::{style, Emoji};
use inflector::cases::titlecase::to_title_case;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use solana_account_decoder::parse_token::UiTokenAccount;
use solana_clap_utils::keypair::SignOnly;
use solana_client::rpc_response::{
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
};
use solana_sdk::{
clock::{self, Epoch, Slot, UnixTimestamp},
epoch_info::EpochInfo,
hash::Hash,
native_token::lamports_to_sol,
pubkey::Pubkey,
signature::Signature,
stake_history::StakeHistoryEntry,
transaction::Transaction,
};
use solana_stake_program::stake_state::{Authorized, Lockup};
use solana_vote_program::{
@ -24,6 +30,7 @@ use solana_vote_program::{
use std::{
collections::{BTreeMap, HashMap},
fmt,
str::FromStr,
time::Duration,
};
@ -34,15 +41,27 @@ pub enum OutputFormat {
Display,
Json,
JsonCompact,
DisplayQuiet,
DisplayVerbose,
}
impl OutputFormat {
pub fn formatted_string<T>(&self, item: &T) -> String
where
T: Serialize + fmt::Display,
T: Serialize + fmt::Display + QuietDisplay + VerboseDisplay,
{
match self {
OutputFormat::Display => format!("{}", item),
OutputFormat::DisplayQuiet => {
let mut s = String::new();
QuietDisplay::write_str(item, &mut s).unwrap();
s
}
OutputFormat::DisplayVerbose => {
let mut s = String::new();
VerboseDisplay::write_str(item, &mut s).unwrap();
s
}
OutputFormat::Json => serde_json::to_string_pretty(item).unwrap(),
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
}
@ -57,6 +76,9 @@ pub struct CliAccount {
pub use_lamports_unit: bool,
}
impl QuietDisplay for CliAccount {}
impl VerboseDisplay for CliAccount {}
impl fmt::Display for CliAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
@ -99,6 +121,9 @@ pub struct CliBlockProduction {
pub verbose: bool,
}
impl QuietDisplay for CliBlockProduction {}
impl VerboseDisplay for CliBlockProduction {}
impl fmt::Display for CliBlockProduction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
@ -203,6 +228,9 @@ impl From<EpochInfo> for CliEpochInfo {
}
}
impl QuietDisplay for CliEpochInfo {}
impl VerboseDisplay for CliEpochInfo {}
impl fmt::Display for CliEpochInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
@ -273,7 +301,7 @@ pub struct CliValidatorsStakeByVersion {
pub struct CliValidators {
pub total_active_stake: u64,
pub total_current_stake: u64,
pub total_deliquent_stake: u64,
pub total_delinquent_stake: u64,
pub current_validators: Vec<CliValidator>,
pub delinquent_validators: Vec<CliValidator>,
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
@ -281,6 +309,9 @@ pub struct CliValidators {
pub use_lamports_unit: bool,
}
impl QuietDisplay for CliValidators {}
impl VerboseDisplay for CliValidators {}
impl fmt::Display for CliValidators {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn write_vote_account(
@ -300,7 +331,7 @@ impl fmt::Display for CliValidators {
writeln!(
f,
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>17} {}",
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>8} {}",
if delinquent {
WARNING.to_string()
} else {
@ -329,7 +360,7 @@ impl fmt::Display for CliValidators {
"Active Stake:",
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
)?;
if self.total_deliquent_stake > 0 {
if self.total_delinquent_stake > 0 {
writeln_name_value(
f,
"Current Stake:",
@ -345,11 +376,11 @@ impl fmt::Display for CliValidators {
&format!(
"{} ({:0.2}%)",
&build_balance_message(
self.total_deliquent_stake,
self.total_delinquent_stake,
self.use_lamports_unit,
true
),
100. * self.total_deliquent_stake as f64 / self.total_active_stake as f64
100. * self.total_delinquent_stake as f64 / self.total_active_stake as f64
),
)?;
}
@ -359,7 +390,7 @@ impl fmt::Display for CliValidators {
for (version, info) in self.stake_by_version.iter() {
writeln!(
f,
"{:<16} - {:3} current validators ({:>5.2}%){}",
"{:<8} - {:3} current validators ({:>5.2}%){}",
version,
info.current_validators,
100. * info.current_active_stake as f64 / self.total_active_stake as f64,
@ -380,7 +411,7 @@ impl fmt::Display for CliValidators {
f,
"{}",
style(format!(
" {:<44} {:<38} {} {} {} {:>10} {:^17} {}",
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}",
"Identity Pubkey",
"Vote Account Pubkey",
"Commission",
@ -469,6 +500,9 @@ pub struct CliNonceAccount {
pub use_lamports_unit: bool,
}
impl QuietDisplay for CliNonceAccount {}
impl VerboseDisplay for CliNonceAccount {}
impl fmt::Display for CliNonceAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
@ -506,6 +540,9 @@ impl CliStakeVec {
}
}
impl QuietDisplay for CliStakeVec {}
impl VerboseDisplay for CliStakeVec {}
impl fmt::Display for CliStakeVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for state in &self.0 {
@ -524,6 +561,9 @@ pub struct CliKeyedStakeState {
pub stake_state: CliStakeState,
}
impl QuietDisplay for CliKeyedStakeState {}
impl VerboseDisplay for CliKeyedStakeState {}
impl fmt::Display for CliKeyedStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?;
@ -531,6 +571,48 @@ impl fmt::Display for CliKeyedStakeState {
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliEpochReward {
pub epoch: Epoch,
pub effective_slot: Slot,
pub amount: u64, // lamports
pub post_balance: u64, // lamports
pub percent_change: f64,
pub apr: f64,
}
fn show_epoch_rewards(
f: &mut fmt::Formatter,
epoch_rewards: &Option<Vec<CliEpochReward>>,
) -> fmt::Result {
if let Some(epoch_rewards) = epoch_rewards {
if epoch_rewards.is_empty() {
return Ok(());
}
writeln!(f, "Epoch Rewards:")?;
writeln!(
f,
" {:<8} {:<11} {:<15} {:<15} {:>14} {:>14}",
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR"
)?;
for reward in epoch_rewards {
writeln!(
f,
" {:<8} {:<11} ◎{:<14.9} ◎{:<14.9} {:>13.9}% {:>13.9}%",
reward.epoch,
reward.effective_slot,
lamports_to_sol(reward.amount),
lamports_to_sol(reward.post_balance),
reward.percent_change,
reward.apr,
)?;
}
}
Ok(())
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliStakeState {
@ -560,8 +642,13 @@ pub struct CliStakeState {
pub activating_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deactivating_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub epoch_rewards: Option<Vec<CliEpochReward>>,
}
impl QuietDisplay for CliStakeState {}
impl VerboseDisplay for CliStakeState {}
impl fmt::Display for CliStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn show_authorized(f: &mut fmt::Formatter, authorized: &CliAuthorized) -> fmt::Result {
@ -574,13 +661,8 @@ impl fmt::Display for CliStakeState {
if lockup.unix_timestamp != UnixTimestamp::default() {
writeln!(
f,
"Lockup Timestamp: {} (UnixTimestamp: {})",
DateTime::<Utc>::from_utc(
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
Utc
)
.to_rfc3339_opts(SecondsFormat::Secs, true),
lockup.unix_timestamp
"Lockup Timestamp: {}",
unix_timestamp_to_string(lockup.unix_timestamp)
)?;
}
if lockup.epoch != Epoch::default() {
@ -710,13 +792,14 @@ impl fmt::Display for CliStakeState {
}
show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref())?;
show_epoch_rewards(f, &self.epoch_rewards)?
}
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, PartialEq)]
pub enum CliStakeType {
Stake,
RewardsPool,
@ -738,6 +821,9 @@ pub struct CliStakeHistory {
pub use_lamports_unit: bool,
}
impl QuietDisplay for CliStakeHistory {}
impl VerboseDisplay for CliStakeHistory {}
impl fmt::Display for CliStakeHistory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
@ -832,6 +918,9 @@ impl CliValidatorInfoVec {
}
}
impl QuietDisplay for CliValidatorInfoVec {}
impl VerboseDisplay for CliValidatorInfoVec {}
impl fmt::Display for CliValidatorInfoVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.0.is_empty() {
@ -853,6 +942,9 @@ pub struct CliValidatorInfo {
pub info: Map<String, Value>,
}
impl QuietDisplay for CliValidatorInfo {}
impl VerboseDisplay for CliValidatorInfo {}
impl fmt::Display for CliValidatorInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Validator Identity Pubkey:", &self.identity_pubkey)?;
@ -884,8 +976,13 @@ pub struct CliVoteAccount {
pub epoch_voting_history: Vec<CliEpochVotingHistory>,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub epoch_rewards: Option<Vec<CliEpochReward>>,
}
impl QuietDisplay for CliVoteAccount {}
impl VerboseDisplay for CliVoteAccount {}
impl fmt::Display for CliVoteAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
@ -906,7 +1003,12 @@ impl fmt::Display for CliVoteAccount {
None => "~".to_string(),
}
)?;
writeln!(f, "Recent Timestamp: {:?}", self.recent_timestamp)?;
writeln!(
f,
"Recent Timestamp: {} from slot {}",
unix_timestamp_to_string(self.recent_timestamp.timestamp),
self.recent_timestamp.slot
)?;
if !self.votes.is_empty() {
writeln!(f, "Recent Votes:")?;
for vote in &self.votes {
@ -925,6 +1027,7 @@ impl fmt::Display for CliVoteAccount {
)?;
}
}
show_epoch_rewards(f, &self.epoch_rewards)?;
Ok(())
}
}
@ -935,6 +1038,9 @@ pub struct CliAuthorizedVoters {
authorized_voters: BTreeMap<Epoch, String>,
}
impl QuietDisplay for CliAuthorizedVoters {}
impl VerboseDisplay for CliAuthorizedVoters {}
impl fmt::Display for CliAuthorizedVoters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.authorized_voters)
@ -984,19 +1090,25 @@ pub struct CliBlockTime {
pub timestamp: UnixTimestamp,
}
impl QuietDisplay for CliBlockTime {}
impl VerboseDisplay for CliBlockTime {}
fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
format!(
"{} (UnixTimestamp: {})",
match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
Some(ndt) =>
DateTime::<Utc>::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true),
None => "unknown".to_string(),
},
unix_timestamp,
)
}
impl fmt::Display for CliBlockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Block:", &self.slot.to_string())?;
writeln_name_value(
f,
"Date:",
&format!(
"{} (UnixTimestamp: {})",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.timestamp, 0), Utc)
.to_rfc3339_opts(SecondsFormat::Secs, true),
self.timestamp
),
)
writeln_name_value(f, "Date:", &unix_timestamp_to_string(self.timestamp))
}
}
@ -1012,6 +1124,9 @@ pub struct CliSignOnlyData {
pub bad_sig: Vec<String>,
}
impl QuietDisplay for CliSignOnlyData {}
impl VerboseDisplay for CliSignOnlyData {}
impl fmt::Display for CliSignOnlyData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
@ -1044,6 +1159,9 @@ pub struct CliSignature {
pub signature: String,
}
impl QuietDisplay for CliSignature {}
impl VerboseDisplay for CliSignature {}
impl fmt::Display for CliSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
@ -1058,6 +1176,9 @@ pub struct CliAccountBalances {
pub accounts: Vec<RpcAccountBalance>,
}
impl QuietDisplay for CliAccountBalances {}
impl VerboseDisplay for CliAccountBalances {}
impl fmt::Display for CliAccountBalances {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
@ -1100,6 +1221,9 @@ impl From<RpcSupply> for CliSupply {
}
}
impl QuietDisplay for CliSupply {}
impl VerboseDisplay for CliSupply {}
impl fmt::Display for CliSupply {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?;
@ -1133,6 +1257,9 @@ pub struct CliFees {
pub last_valid_slot: Slot,
}
impl QuietDisplay for CliFees {}
impl VerboseDisplay for CliFees {}
impl fmt::Display for CliFees {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Blockhash:", &self.blockhash)?;
@ -1145,3 +1272,240 @@ impl fmt::Display for CliFees {
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliTokenAccount {
pub address: String,
#[serde(flatten)]
pub token_account: UiTokenAccount,
}
impl QuietDisplay for CliTokenAccount {}
impl VerboseDisplay for CliTokenAccount {}
impl fmt::Display for CliTokenAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln_name_value(f, "Address:", &self.address)?;
let account = &self.token_account;
writeln_name_value(
f,
"Balance:",
&account.token_amount.real_number_string_trimmed(),
)?;
let mint = format!(
"{}{}",
account.mint,
if account.is_native { " (native)" } else { "" }
);
writeln_name_value(f, "Mint:", &mint)?;
writeln_name_value(f, "Owner:", &account.owner)?;
writeln_name_value(f, "State:", &format!("{:?}", account.state))?;
if let Some(delegate) = &account.delegate {
writeln!(f, "Delegation:")?;
writeln_name_value(f, " Delegate:", delegate)?;
let allowance = account.delegated_amount.as_ref().unwrap();
writeln_name_value(f, " Allowance:", &allowance.real_number_string_trimmed())?;
}
writeln_name_value(
f,
"Close authority:",
&account.close_authority.as_ref().unwrap_or(&String::new()),
)?;
Ok(())
}
}
pub fn return_signers(
tx: &Transaction,
output_format: &OutputFormat,
) -> Result<String, Box<dyn std::error::Error>> {
let verify_results = tx.verify_with_results();
let mut signers = Vec::new();
let mut absent = Vec::new();
let mut bad_sig = Vec::new();
tx.signatures
.iter()
.zip(tx.message.account_keys.iter())
.zip(verify_results.into_iter())
.for_each(|((sig, key), res)| {
if res {
signers.push(format!("{}={}", key, sig))
} else if *sig == Signature::default() {
absent.push(key.to_string());
} else {
bad_sig.push(key.to_string());
}
});
let cli_command = CliSignOnlyData {
blockhash: tx.message.recent_blockhash.to_string(),
signers,
absent,
bad_sig,
};
Ok(output_format.formatted_string(&cli_command))
}
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
let object: Value = serde_json::from_str(&reply).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let blockhash = blockhash_str.parse::<Hash>().unwrap();
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
let signer_strings = object.get("signers");
if let Some(sig_strings) = signer_strings {
present_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
}
let mut absent_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("absent");
if let Some(sig_strings) = signer_strings {
absent_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
let mut bad_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("badSig");
if let Some(sig_strings) = signer_strings {
bad_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
SignOnly {
blockhash,
present_signers,
absent_signers,
bad_signers,
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::{
message::Message,
pubkey::Pubkey,
signature::{keypair_from_seed, NullSigner, Signature, Signer, SignerError},
system_instruction,
transaction::Transaction,
};
#[test]
fn test_return_signers() {
struct BadSigner {
pubkey: Pubkey,
}
impl BadSigner {
pub fn new(pubkey: Pubkey) -> Self {
Self { pubkey }
}
}
impl Signer for BadSigner {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(self.pubkey)
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::new(&[1u8; 64]))
}
}
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
let bad: Box<dyn Signer> = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32])));
let to = Pubkey::new(&[5u8; 32]);
let nonce = Pubkey::new(&[6u8; 32]);
let from = present.pubkey();
let fee_payer = absent.pubkey();
let nonce_auth = bad.pubkey();
let mut tx = Transaction::new_unsigned(Message::new_with_nonce(
vec![system_instruction::transfer(&from, &to, 42)],
Some(&fee_payer),
&nonce,
&nonce_auth,
));
let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()];
let blockhash = Hash::new(&[7u8; 32]);
tx.try_partial_sign(&signers, blockhash).unwrap();
let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap();
let sign_only = parse_sign_only_reply_string(&res);
assert_eq!(sign_only.blockhash, blockhash);
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
}
#[test]
fn test_verbose_quiet_output_formats() {
#[derive(Deserialize, Serialize)]
struct FallbackToDisplay {}
impl std::fmt::Display for FallbackToDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "display")
}
}
impl QuietDisplay for FallbackToDisplay {}
impl VerboseDisplay for FallbackToDisplay {}
let f = FallbackToDisplay {};
assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "display");
assert_eq!(
&OutputFormat::DisplayVerbose.formatted_string(&f),
"display"
);
#[derive(Deserialize, Serialize)]
struct DiscreteVerbosityDisplay {}
impl std::fmt::Display for DiscreteVerbosityDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "display")
}
}
impl QuietDisplay for DiscreteVerbosityDisplay {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "quiet")
}
}
impl VerboseDisplay for DiscreteVerbosityDisplay {
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
write!(w, "verbose")
}
}
let f = DiscreteVerbosityDisplay {};
assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "quiet");
assert_eq!(
&OutputFormat::DisplayVerbose.formatted_string(&f),
"verbose"
);
}
}

View File

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

15
cli-output/src/lib.rs Normal file
View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,30 @@
use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::*,
display::{format_labeled_address, new_spinner_progress_bar, println_name_value},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
stake::is_stake_program_v2_enabled,
};
use chrono::{Local, TimeZone};
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji};
use solana_clap_utils::{
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path,
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::DefaultSigner,
};
use solana_cli_output::{
display::{
format_labeled_address, new_spinner_progress_bar, println_name_value, println_transaction,
},
*,
};
use solana_client::{
pubsub_client::{PubsubClient, SlotInfoMessage},
rpc_client::RpcClient,
rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
rpc_request::MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE,
client_error::ClientErrorKind,
pubsub_client::PubsubClient,
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
rpc_config::{
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
RpcProgramAccountsConfig,
},
rpc_filter,
rpc_response::SlotInfo,
};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
@ -24,6 +35,7 @@ use solana_sdk::{
message::Message,
native_token::lamports_to_sol,
pubkey::{self, Pubkey},
signature::Signature,
system_instruction, system_program,
sysvar::{
self,
@ -32,6 +44,7 @@ use solana_sdk::{
},
transaction::Transaction,
};
use solana_transaction_status::UiTransactionEncoding;
use std::{
collections::{BTreeMap, HashMap, VecDeque},
net::SocketAddr,
@ -53,6 +66,18 @@ pub trait ClusterQuerySubCommands {
impl ClusterQuerySubCommands for App<'_, '_> {
fn cluster_query_subcommands(self) -> Self {
self.subcommand(
SubCommand::with_name("block")
.about("Get a confirmed block")
.arg(
Arg::with_name("slot")
.long("slot")
.validator(is_slot)
.value_name("SLOT")
.takes_value(true)
.index(1),
),
)
.subcommand(
SubCommand::with_name("catchup")
.about("Wait for a validator to catch up to the cluster")
.arg(
@ -87,6 +112,10 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.about("Get the version of the cluster entrypoint"),
)
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees"))
.subcommand(
SubCommand::with_name("first-available-block")
.about("Get the first available block in the storage"),
)
.subcommand(SubCommand::with_name("block-time")
.about("Get estimated production time of a block")
.alias("get-block-time")
@ -257,9 +286,8 @@ impl ClusterQuerySubCommands for App<'_, '_> {
)
.subcommand(
SubCommand::with_name("transaction-history")
.about("Show historical transactions affecting the given address, \
ordered based on the slot in which they were confirmed in \
from lowest to highest slot")
.about("Show historical transactions affecting the given address \
from newest to oldest")
.arg(
pubkey!(Arg::with_name("address")
.index(1)
@ -267,26 +295,28 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.required(true),
"Account address"),
)
.arg(
Arg::with_name("end_slot")
.takes_value(false)
.value_name("SLOT")
.index(2)
.validator(is_slot)
.help(
"Slot to start from [default: latest slot at maximum commitment]"
),
)
.arg(
Arg::with_name("limit")
.long("limit")
.takes_value(true)
.value_name("NUMBER OF SLOTS")
.value_name("LIMIT")
.validator(is_slot)
.help(
"Limit the search to this many slots"
),
),
.default_value("1000")
.help("Maximum number of transaction signatures to return"),
)
.arg(
Arg::with_name("before")
.long("before")
.value_name("TRANSACTION_SIGNATURE")
.takes_value(true)
.help("Start with the first signature older than this one"),
)
.arg(
Arg::with_name("show_transactions")
.long("show-transactions")
.takes_value(false)
.help("Display the full transactions"),
)
)
}
}
@ -310,7 +340,7 @@ pub fn parse_catchup(
pub fn parse_cluster_ping(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let lamports = value_t_or_exit!(matches, "lamports", u64);
@ -328,12 +358,15 @@ pub fn parse_cluster_ping(
count,
timeout,
},
signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
})
}
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let slot = value_of(matches, "slot");
Ok(CliCommandInfo {
command: CliCommand::GetBlock { slot },
signers: vec![],
})
}
@ -440,14 +473,33 @@ pub fn parse_transaction_history(
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let address = pubkey_of_signer(matches, "address", wallet_manager)?.unwrap();
let end_slot = value_t!(matches, "end_slot", Slot).ok();
let slot_limit = value_t!(matches, "limit", u64).ok();
let before = match matches.value_of("before") {
Some(signature) => Some(
signature
.parse()
.map_err(|err| CliError::BadParameter(format!("Invalid signature: {}", err)))?,
),
None => None,
};
let until = match matches.value_of("until") {
Some(signature) => Some(
signature
.parse()
.map_err(|err| CliError::BadParameter(format!("Invalid signature: {}", err)))?,
),
None => None,
};
let limit = value_t_or_exit!(matches, "limit", usize);
let show_transactions = matches.is_present("show_transactions");
Ok(CliCommandInfo {
command: CliCommand::TransactionHistory {
address,
end_slot,
slot_limit,
before,
until,
limit,
show_transactions,
},
signers: vec![],
})
@ -490,7 +542,20 @@ pub fn process_catchup(
RpcClient::new_socket(rpc_addr)
};
let reported_node_pubkey = node_client.get_identity()?;
let reported_node_pubkey = loop {
match node_client.get_identity() {
Ok(reported_node_pubkey) => break reported_node_pubkey,
Err(err) => {
if let ClientErrorKind::Reqwest(err) = err.kind() {
progress_bar.set_message(&format!("Connection failed: {}", err));
sleep(Duration::from_secs(sleep_interval as u64));
continue;
}
return Err(Box::new(err));
}
}
};
if reported_node_pubkey != *node_pubkey {
return Err(format!(
"The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}",
@ -573,9 +638,14 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
}
}
pub fn process_cluster_version(rpc_client: &RpcClient) -> ProcessResult {
pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
let remote_version = rpc_client.get_version()?;
Ok(remote_version.solana_core)
if config.verbose {
Ok(format!("{:?}", remote_version))
} else {
Ok(remote_version.to_string())
}
}
pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
@ -590,6 +660,11 @@ pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult
Ok(config.output_format.formatted_string(&fees))
}
pub fn process_first_available_block(rpc_client: &RpcClient) -> ProcessResult {
let first_available_block = rpc_client.get_first_available_block()?;
Ok(format!("{}", first_available_block))
}
pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
let epoch_info = rpc_client.get_epoch_info()?;
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
@ -625,6 +700,83 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
Ok("".to_string())
}
pub fn process_get_block(
rpc_client: &RpcClient,
_config: &CliConfig,
slot: Option<Slot>,
) -> ProcessResult {
let slot = if let Some(slot) = slot {
slot
} else {
rpc_client.get_slot()?
};
let mut block =
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
println!("Slot: {}", slot);
println!("Parent Slot: {}", block.parent_slot);
println!("Blockhash: {}", block.blockhash);
println!("Previous Blockhash: {}", block.previous_blockhash);
if let Some(block_time) = block.block_time {
println!("Block Time: {:?}", Local.timestamp(block_time, 0));
}
if !block.rewards.is_empty() {
block.rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
let mut total_rewards = 0;
println!("Rewards:",);
println!(
" {:<44} {:^15} {:<15} {:<20} {:>14}",
"Address", "Type", "Amount", "New Balance", "Percent Change"
);
for reward in block.rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
total_rewards += reward.lamports;
println!(
" {:<44} {:^15} {:>15} {}",
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!(
"{}{:<14.9}",
sign,
lamports_to_sol(reward.lamports.abs() as u64)
),
if reward.post_balance == 0 {
" - -".to_string()
} else {
format!(
"{:<19.9} {:>13.9}%",
lamports_to_sol(reward.post_balance),
reward.lamports.abs() as f64
/ (reward.post_balance as f64 - reward.lamports as f64)
)
}
);
}
let sign = if total_rewards < 0 { "-" } else { "" };
println!(
"Total Rewards: {}{:<12.9}",
sign,
lamports_to_sol(total_rewards.abs() as u64)
);
}
for (index, transaction_with_meta) in block.transactions.iter().enumerate() {
println!("Transaction {}:", index);
println_transaction(
&transaction_with_meta.transaction.decode().unwrap(),
&transaction_with_meta.meta,
" ",
);
}
Ok("".to_string())
}
pub fn process_get_block_time(
rpc_client: &RpcClient,
config: &CliConfig,
@ -1021,7 +1173,7 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
})?;
*/
let mut current: Option<SlotInfoMessage> = None;
let mut current: Option<SlotInfo> = None;
let mut message = "".to_string();
let slot_progress = new_spinner_progress_bar();
@ -1116,14 +1268,16 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
.into_iter()
.map(|node| {
format!(
"{:15} | {:44} | {:6} | {:5} | {:5} | {}",
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
node.gossip
.map(|addr| addr.ip().to_string())
.unwrap_or_else(|| "none".to_string()),
format_labeled_address(&node.pubkey, &config.address_labels),
format_port(node.gossip),
format_port(node.tpu),
format_port(node.rpc),
node.rpc
.map(|addr| addr.to_string())
.unwrap_or_else(|| "none".to_string()),
node.version.unwrap_or_else(|| "unknown".to_string()),
)
})
@ -1131,9 +1285,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
Ok(format!(
"IP Address | Node identifier \
| Gossip | TPU | RPC | Version\n\
| Gossip | TPU | RPC Address | Version\n\
----------------+----------------------------------------------+\
--------+-------+-------+----------------\n\
--------+-------+-----------------------+----------------\n\
{}\n\
Nodes: {}",
s.join("\n"),
@ -1152,17 +1306,52 @@ pub fn process_show_stakes(
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching stake accounts...");
let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
let mut program_accounts_config = RpcProgramAccountsConfig {
filters: None,
account_config: RpcAccountInfoConfig {
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
..RpcAccountInfoConfig::default()
},
};
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
// Use server-side filtering if only one vote account is provided
if vote_account_pubkeys.len() == 1 {
program_accounts_config.filters = Some(vec![
// Filter by `StakeState::Stake(_, _)`
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
offset: 0,
bytes: rpc_filter::MemcmpEncodedBytes::Binary(
bs58::encode([2, 0, 0, 0]).into_string(),
),
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
}),
// Filter by `Delegation::voter_pubkey`, which begins at byte offset 124
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
offset: 124,
bytes: rpc_filter::MemcmpEncodedBytes::Binary(
vote_account_pubkeys[0].to_string(),
),
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
}),
]);
}
}
let all_stake_accounts = rpc_client
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
progress_bar.finish_and_clear();
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?;
progress_bar.finish_and_clear();
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?;
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?;
// At v1.6, this check can be removed and simply passed as `true`
let stake_program_v2_enabled = is_stake_program_v2_enabled(rpc_client);
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
for (stake_pubkey, stake_account) in all_stake_accounts {
@ -1178,6 +1367,7 @@ pub fn process_show_stakes(
use_lamports_unit,
&stake_history,
&clock,
stake_program_v2_enabled,
),
});
}
@ -1196,6 +1386,7 @@ pub fn process_show_stakes(
use_lamports_unit,
&stake_history,
&clock,
stake_program_v2_enabled,
),
});
}
@ -1235,12 +1426,12 @@ pub fn process_show_validators(
.map(|vote_account| vote_account.activated_stake)
.sum();
let total_deliquent_stake = vote_accounts
let total_delinquent_stake = vote_accounts
.delinquent
.iter()
.map(|vote_account| vote_account.activated_stake)
.sum();
let total_current_stake = total_active_stake - total_deliquent_stake;
let total_current_stake = total_active_stake - total_delinquent_stake;
let mut current = vote_accounts.current;
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
@ -1294,7 +1485,7 @@ pub fn process_show_validators(
let cli_validators = CliValidators {
total_active_stake,
total_current_stake,
total_deliquent_stake,
total_delinquent_stake,
current_validators,
delinquent_validators,
stake_by_version,
@ -1305,41 +1496,63 @@ pub fn process_show_validators(
pub fn process_transaction_history(
rpc_client: &RpcClient,
config: &CliConfig,
address: &Pubkey,
end_slot: Option<Slot>, // None == use latest slot
slot_limit: Option<u64>,
before: Option<Signature>,
until: Option<Signature>,
limit: usize,
show_transactions: bool,
) -> ProcessResult {
let end_slot = {
if let Some(end_slot) = end_slot {
end_slot
} else {
rpc_client.get_slot_with_commitment(CommitmentConfig::max())?
}
};
let mut start_slot = match slot_limit {
Some(slot_limit) => end_slot.saturating_sub(slot_limit),
None => rpc_client.minimum_ledger_slot()?,
};
println!(
"Transactions affecting {} within slots [{},{}]",
address, start_slot, end_slot
);
let mut transaction_count = 0;
while start_slot < end_slot {
let signatures = rpc_client.get_confirmed_signatures_for_address(
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
address,
start_slot,
(start_slot + MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE).min(end_slot),
GetConfirmedSignaturesForAddress2Config {
before,
until,
limit: Some(limit),
},
)?;
for signature in &signatures {
println!("{}", signature);
let transactions_found = format!("{} transactions found", results.len());
for result in results {
if config.verbose {
println!(
"{} [slot={} status={}] {}",
result.signature,
result.slot,
match result.err {
None => "Confirmed".to_string(),
Some(err) => format!("Failed: {:?}", err),
},
result.memo.unwrap_or_else(|| "".to_string()),
);
} else {
println!("{}", result.signature);
}
transaction_count += signatures.len();
start_slot += MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE;
if show_transactions {
if let Ok(signature) = result.signature.parse::<Signature>() {
match rpc_client
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
{
Ok(confirmed_transaction) => {
println_transaction(
&confirmed_transaction
.transaction
.transaction
.decode()
.expect("Successful decode"),
&confirmed_transaction.transaction.meta,
" ",
);
}
Ok(format!("{} transactions found", transaction_count))
Err(err) => println!(" Unable to get confirmed transaction details: {}", err),
}
}
println!();
}
}
Ok(transactions_found)
}
#[cfg(test)]
@ -1360,12 +1573,16 @@ 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 {
path: default_keypair_file,
arg_name: String::new(),
};
let test_cluster_version = test_commands
.clone()
.get_matches_from(vec!["test", "cluster-date"]);
assert_eq!(
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::ClusterDate,
signers: vec![],
@ -1376,7 +1593,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "cluster-version"]);
assert_eq!(
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::ClusterVersion,
signers: vec![],
@ -1385,7 +1602,7 @@ mod tests {
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!(
parse_command(&test_fees, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_fees, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::Fees,
signers: vec![],
@ -1398,7 +1615,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "block-time", &slot.to_string()]);
assert_eq!(
parse_command(&test_get_block_time, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_get_block_time, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::GetBlockTime { slot: Some(slot) },
signers: vec![],
@ -1409,7 +1626,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "epoch"]);
assert_eq!(
parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_get_epoch, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::GetEpoch,
signers: vec![],
@ -1420,7 +1637,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "epoch-info"]);
assert_eq!(
parse_command(&test_get_epoch_info, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_get_epoch_info, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::GetEpochInfo,
signers: vec![],
@ -1431,7 +1648,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "genesis-hash"]);
assert_eq!(
parse_command(&test_get_genesis_hash, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_get_genesis_hash, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::GetGenesisHash,
signers: vec![],
@ -1440,7 +1657,7 @@ mod tests {
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
assert_eq!(
parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_get_slot, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::GetSlot,
signers: vec![],
@ -1451,7 +1668,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "total-supply"]);
assert_eq!(
parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_total_supply, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::TotalSupply,
signers: vec![],
@ -1462,7 +1679,7 @@ mod tests {
.clone()
.get_matches_from(vec!["test", "transaction-count"]);
assert_eq!(
parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_transaction_count, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::GetTransactionCount,
signers: vec![],
@ -1482,7 +1699,7 @@ mod tests {
"max",
]);
assert_eq!(
parse_command(&test_ping, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_ping, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::Ping {
lamports: 1,

367
cli/src/feature.rs Normal file
View File

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

89
cli/src/inflation.rs Normal file
View File

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

View File

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

View File

@ -5,22 +5,37 @@ use clap::{
use console::style;
use solana_clap_utils::{
commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url,
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
commitment::COMMITMENT_ARG,
input_parsers::commitment_of,
input_validators::is_url,
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
DisplayError,
};
use solana_cli::{
cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners,
use solana_cli::cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
DEFAULT_RPC_TIMEOUT_SECONDS,
},
cli_output::OutputFormat,
display::{println_name_value, println_name_value_or},
};
use solana_cli_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat};
use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration};
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
let description = match setting_type {
SettingType::Explicit => "",
SettingType::Computed => "(computed)",
SettingType::SystemDefault => "(default)",
};
println!(
"{} {} {}",
style(name).bold(),
style(value),
style(description).italic(),
);
}
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() {
("config", Some(matches)) => {
@ -139,13 +154,19 @@ pub fn parse_args<'a>(
matches.value_of("json_rpc_url").unwrap_or(""),
&config.json_rpc_url,
);
let default_signer_arg_name = "keypair".to_string();
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
matches.value_of("keypair").unwrap_or(""),
matches.value_of(&default_signer_arg_name).unwrap_or(""),
&config.keypair_path,
);
let default_signer = DefaultSigner {
arg_name: default_signer_arg_name,
path: default_signer_path.clone(),
};
let CliCommandInfo { command, signers } =
parse_command(&matches, &default_signer_path, &mut wallet_manager)?;
parse_command(&matches, &default_signer, &mut wallet_manager)?;
let output_format = matches
.value_of("output_format")

View File

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

View File

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

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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,20 @@
use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::{CliValidatorInfo, CliValidatorInfoVec},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
};
use bincode::deserialize;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use reqwest::blocking::Client;
use serde_derive::{Deserialize, Serialize};
use serde_json::{Map, Value};
use solana_account_decoder::validator_info::{
self, ValidatorInfo, MAX_LONG_FIELD_LENGTH, MAX_SHORT_FIELD_LENGTH,
};
use solana_clap_utils::{
input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url},
keypair::signer_from_path,
keypair::DefaultSigner,
};
use solana_cli_output::{CliValidatorInfo, CliValidatorInfoVec};
use solana_client::rpc_client::RpcClient;
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@ -27,23 +28,6 @@ use solana_sdk::{
};
use std::{error, sync::Arc};
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
pub const MAX_VALIDATOR_INFO: u64 = 576;
solana_sdk::declare_id!("Va1idator1nfo111111111111111111111111111111");
#[derive(Debug, Deserialize, PartialEq, Serialize, Default)]
pub struct ValidatorInfo {
info: String,
}
impl ConfigState for ValidatorInfo {
fn max_space() -> u64 {
MAX_VALIDATOR_INFO
}
}
// Return an error if a validator details are longer than the max length.
pub fn check_details_length(string: String) -> Result<(), String> {
if string.len() > MAX_LONG_FIELD_LENGTH {
@ -228,7 +212,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
pub fn parse_validator_info_command(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey");
@ -240,12 +224,7 @@ pub fn parse_validator_info_command(
force_keybase: matches.is_present("force"),
info_pubkey,
},
signers: vec![signer_from_path(
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
})
}
@ -287,10 +266,12 @@ pub fn process_set_validator_info(
let all_config = rpc_client.get_program_accounts(&solana_config_program::id())?;
let existing_account = all_config
.iter()
.filter(|(_, account)| {
let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
key_list.keys.contains(&(id(), false))
})
.filter(
|(_, account)| match deserialize::<ConfigKeys>(&account.data) {
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
Err(_) => false,
},
)
.find(|(pubkey, account)| {
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
validator_pubkey == config.signers[0].pubkey()
@ -328,7 +309,10 @@ pub fn process_set_validator_info(
};
let build_message = |lamports| {
let keys = vec![(id(), false), (config.signers[0].pubkey(), true)];
let keys = vec![
(validator_info::id(), false),
(config.signers[0].pubkey(), true),
];
if balance == 0 {
println!(
"Publishing info for Validator {:?}",
@ -398,10 +382,10 @@ pub fn process_get_validator_info(
all_config
.into_iter()
.filter(|(_, validator_info_account)| {
let key_list: ConfigKeys = deserialize(&validator_info_account.data)
.map_err(|_| false)
.unwrap();
key_list.keys.contains(&(id(), false))
match deserialize::<ConfigKeys>(&validator_info_account.data) {
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
Err(_) => false,
}
})
.collect()
};
@ -502,8 +486,8 @@ mod tests {
#[test]
fn test_parse_validator_info() {
let pubkey = Pubkey::new_rand();
let keys = vec![(id(), false), (pubkey, true)];
let pubkey = solana_sdk::pubkey::new_rand();
let keys = vec![(validator_info::id(), false), (pubkey, true)];
let config = ConfigKeys { keys };
let mut info = Map::new();

View File

@ -1,14 +1,19 @@
use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
CliConfig, CliError, ProcessResult, SignerIndex,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
},
cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount},
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::*};
use solana_clap_utils::{
commitment::commitment_arg,
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
};
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
@ -246,7 +251,7 @@ impl VoteSubCommands for App<'_, '_> {
pub fn parse_create_vote_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?;
@ -258,10 +263,9 @@ pub fn parse_create_vote_account(
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, vote_account, identity_account],
matches,
default_signer_path,
wallet_manager,
)?;
@ -280,7 +284,7 @@ pub fn parse_create_vote_account(
pub fn parse_vote_authorize(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize,
) -> Result<CliCommandInfo, CliError> {
@ -291,10 +295,9 @@ pub fn parse_vote_authorize(
let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized],
matches,
default_signer_path,
wallet_manager,
)?;
@ -310,7 +313,7 @@ pub fn parse_vote_authorize(
pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
@ -321,10 +324,9 @@ pub fn parse_vote_update_validator(
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized_withdrawer, new_identity_account],
matches,
default_signer_path,
wallet_manager,
)?;
@ -340,7 +342,7 @@ pub fn parse_vote_update_validator(
pub fn parse_vote_update_commission(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
@ -350,10 +352,9 @@ pub fn parse_vote_update_commission(
let commission = value_t_or_exit!(matches, "commission", u8);
let payer_provided = None;
let signer_info = generate_unique_signers(
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized_withdrawer],
matches,
default_signer_path,
wallet_manager,
)?;
@ -385,7 +386,7 @@ pub fn parse_vote_get_account_command(
pub fn parse_withdraw_from_vote_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
@ -398,10 +399,9 @@ pub fn parse_withdraw_from_vote_account(
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, withdraw_authority],
matches,
default_signer_path,
wallet_manager,
)?;
@ -671,11 +671,11 @@ fn get_vote_account(
pub fn process_show_vote_account(
rpc_client: &RpcClient,
config: &CliConfig,
vote_account_pubkey: &Pubkey,
vote_account_address: &Pubkey,
use_lamports_unit: bool,
) -> ProcessResult {
let (vote_account, vote_state) =
get_vote_account(rpc_client, vote_account_pubkey, config.commitment)?;
get_vote_account(rpc_client, vote_account_address, config.commitment)?;
let epoch_schedule = rpc_client.get_epoch_schedule()?;
@ -696,6 +696,12 @@ pub fn process_show_vote_account(
}
}
let epoch_rewards = Some(crate::stake::fetch_epoch_rewards(
rpc_client,
vote_account_address,
1,
)?);
let vote_account_data = CliVoteAccount {
account_balance: vote_account.lamports,
validator_identity: vote_state.node_pubkey.to_string(),
@ -708,6 +714,7 @@ pub fn process_show_vote_account(
votes,
epoch_voting_history,
use_lamports_unit,
epoch_rewards,
};
Ok(config.output_format.formatted_string(&vote_account_data))
@ -794,6 +801,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 {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
@ -803,7 +814,7 @@ mod tests {
&pubkey2_string,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
@ -826,7 +837,7 @@ mod tests {
&pubkey2_string,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
@ -856,7 +867,7 @@ mod tests {
"10",
]);
assert_eq!(
parse_command(&test_create_vote_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
@ -885,7 +896,7 @@ mod tests {
&identity_keypair_file,
]);
assert_eq!(
parse_command(&test_create_vote_account2, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account2, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
@ -904,7 +915,7 @@ mod tests {
);
// test init with an authed voter
let authed = Pubkey::new_rand();
let authed = solana_sdk::pubkey::new_rand();
let (keypair_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
@ -918,7 +929,7 @@ mod tests {
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account3, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account3, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
@ -949,7 +960,7 @@ mod tests {
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account4, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account4, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
@ -975,7 +986,7 @@ mod tests {
&keypair_file,
]);
assert_eq!(
parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_update_validator, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey,
@ -998,7 +1009,7 @@ mod tests {
&keypair_file,
]);
assert_eq!(
parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_update_commission, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteUpdateCommission {
vote_account_pubkey: pubkey,
@ -1021,12 +1032,7 @@ mod tests {
"42",
]);
assert_eq!(
parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@ -1047,12 +1053,7 @@ mod tests {
"ALL",
]);
assert_eq!(
parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@ -1078,12 +1079,7 @@ mod tests {
&withdraw_authority_file,
]);
assert_eq!(
parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),

View File

@ -1,7 +1,7 @@
use serde_json::Value;
use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
bpf_loader,
@ -55,7 +55,7 @@ fn test_cli_deploy_program() {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 3 * minimum_balance_for_rent_exemption, // min balance for rent exemption for two programs + leftover for tx processing
lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing
};
config.signers = vec![&keypair];
process_command(&config).unwrap();
@ -63,6 +63,8 @@ fn test_cli_deploy_program() {
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
};
let response = process_command(&config);
@ -96,6 +98,8 @@ fn test_cli_deploy_program() {
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
};
process_command(&config).unwrap();
let account1 = rpc_client
@ -111,6 +115,44 @@ fn test_cli_deploy_program() {
// Attempt to redeploy to the same address
process_command(&config).unwrap_err();
// Attempt to deploy to account with excess balance
let custom_address_keypair = Keypair::new();
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 2 * minimum_balance_for_rent_exemption, // Anything over minimum_balance_for_rent_exemption should trigger err
};
config.signers = vec![&custom_address_keypair];
process_command(&config).unwrap();
config.signers = vec![&keypair, &custom_address_keypair];
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
};
process_command(&config).unwrap_err();
// Use forcing parameter to deploy to account with excess balance
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: true,
};
process_command(&config).unwrap();
let account2 = rpc_client
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
assert_eq!(account2.owner, bpf_loader::id());
assert_eq!(account2.executable, true);
assert_eq!(account0.data, account2.data);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}

View File

@ -1,17 +1,16 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
parse_sign_only_reply_string,
},
spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance},
};
use solana_client::rpc_client::RpcClient;
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
use solana_client::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::contact_info::ContactInfo;
use solana_core::validator::{TestValidator, TestValidatorOptions};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
commitment_config::CommitmentConfig,
@ -173,7 +172,7 @@ fn full_battery_tests(
assert_ne!(first_nonce, third_nonce);
// Withdraw from nonce account
let payee_pubkey = Pubkey::new_rand();
let payee_pubkey = solana_sdk::pubkey::new_rand();
config_payer.signers = authorized_signers;
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
@ -232,17 +231,14 @@ fn full_battery_tests(
#[test]
fn test_create_account_with_seed() {
solana_logger::setup();
let TestValidator {
server,
leader_data,
alice: mint_keypair,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(1);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
@ -302,9 +298,12 @@ fn test_create_account_with_seed() {
check_recent_balance(0, &rpc_client, &to_address);
// Fetch nonce hash
let nonce_hash =
nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent())
.and_then(|ref a| nonce::data_from_account(a))
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_address,
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;

View File

@ -2,17 +2,16 @@ use chrono::prelude::*;
use serde_json::Value;
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
parse_sign_only_reply_string,
},
spend_utils::SpendAmount,
test_utils::check_recent_balance,
};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
use solana_client::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
commitment_config::CommitmentConfig,
@ -31,7 +30,7 @@ fn test_cli_timestamp_tx() {
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -118,7 +117,7 @@ fn test_cli_witness_tx() {
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -200,7 +199,7 @@ fn test_cli_cancel_tx() {
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -275,7 +274,7 @@ fn test_offline_pay_tx() {
ledger_path,
..
} = TestValidator::run();
let bob_pubkey = Pubkey::new_rand();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -411,16 +410,16 @@ fn test_nonced_pay_tx() {
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
let bob_pubkey = Pubkey::new_rand();
let bob_pubkey = solana_sdk::pubkey::new_rand();
config.signers = vec![&default_signer];
config.command = CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(10),
@ -438,12 +437,12 @@ fn test_nonced_pay_tx() {
check_recent_balance(10, &rpc_client, &bob_pubkey);
// Verify that nonce has been used
let nonce_hash2 = nonce::get_account_with_commitment(
let nonce_hash2 = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
assert_ne!(nonce_hash, nonce_hash2);

View File

@ -1,6 +1,6 @@
use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use std::{fs::remove_dir_all, sync::mpsc::channel};

View File

@ -1,16 +1,15 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
parse_sign_only_reply_string,
},
spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance},
};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::{TestValidator, TestValidatorOptions};
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
use solana_client::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
account_utils::StateMut,
@ -503,12 +502,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
process_command(&config).unwrap();
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -531,12 +530,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
process_command(&config).unwrap();
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -770,12 +769,12 @@ fn test_stake_authorize() {
process_command(&config).unwrap();
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -824,12 +823,12 @@ fn test_stake_authorize() {
};
assert_eq!(current_authority, online_authority_pubkey);
let new_nonce_hash = nonce::get_account_with_commitment(
let new_nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
assert_ne!(nonce_hash, new_nonce_hash);
@ -849,11 +848,7 @@ fn test_stake_authorize_with_fee_payer() {
alice,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: SIG_FEE,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(SIG_FEE);
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
@ -986,11 +981,7 @@ fn test_stake_split() {
alice,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(1);
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
@ -1069,12 +1060,12 @@ fn test_stake_split() {
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -1141,11 +1132,7 @@ fn test_stake_set_lockup() {
alice,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(1);
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
let faucet_addr = receiver.recv().unwrap();
@ -1338,12 +1325,12 @@ fn test_stake_set_lockup() {
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -1465,12 +1452,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
process_command(&config).unwrap();
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -1520,12 +1507,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
check_recent_balance(50_000, &rpc_client, &stake_pubkey);
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -1568,12 +1555,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
check_recent_balance(42, &rpc_client, &recipient_pubkey);
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;

View File

@ -1,16 +1,15 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
parse_sign_only_reply_string,
},
spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance},
};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::{TestValidator, TestValidatorOptions};
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
use solana_client::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
commitment_config::CommitmentConfig,
@ -22,17 +21,14 @@ use std::{fs::remove_dir_all, sync::mpsc::channel};
#[test]
fn test_transfer() {
solana_logger::setup();
let TestValidator {
server,
leader_data,
alice: mint_keypair,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(1);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
@ -153,12 +149,12 @@ fn test_transfer() {
check_recent_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -181,12 +177,12 @@ fn test_transfer() {
process_command(&config).unwrap();
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
check_recent_balance(30, &rpc_client, &recipient_pubkey);
let new_nonce_hash = nonce::get_account_with_commitment(
let new_nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
assert_ne!(nonce_hash, new_nonce_hash);
@ -202,12 +198,12 @@ fn test_transfer() {
check_recent_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash
let nonce_hash = nonce::get_account_with_commitment(
let nonce_hash = nonce_utils::get_account_with_commitment(
&rpc_client,
&nonce_account.pubkey(),
CommitmentConfig::recent(),
)
.and_then(|ref a| nonce::data_from_account(a))
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
@ -253,17 +249,14 @@ fn test_transfer() {
#[test]
fn test_transfer_multisession_signing() {
solana_logger::setup();
let TestValidator {
server,
leader_data,
alice: mint_keypair,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(1);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);
@ -383,17 +376,14 @@ fn test_transfer_multisession_signing() {
#[test]
fn test_transfer_all() {
solana_logger::setup();
let TestValidator {
server,
leader_data,
alice: mint_keypair,
ledger_path,
..
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
} = TestValidator::run_with_fees(1);
let (sender, receiver) = channel();
run_local_faucet(mint_keypair, sender, None);

View File

@ -1,16 +1,17 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::{blockhash_query::BlockhashQuery, *},
spend_utils::SpendAmount,
test_utils::check_recent_balance,
};
use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator;
use solana_client::{
blockhash_query::{self, BlockhashQuery},
rpc_client::RpcClient,
};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{
account_utils::StateMut,
commitment_config::CommitmentConfig,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
@ -108,7 +109,7 @@ fn test_vote_authorize_and_withdraw() {
assert_eq!(authorized_withdrawer, withdraw_authority.pubkey());
// Withdraw from vote account
let destination_account = Pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
let destination_account = solana_sdk::pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
config.signers = vec![&default_signer, &withdraw_authority];
config.command = CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey,

View File

@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "1.3.0"
version = "1.3.24"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@ -9,30 +9,36 @@ license = "Apache-2.0"
edition = "2018"
[dependencies]
base64 = "0.13.0"
bincode = "1.3.1"
bs58 = "0.3.1"
clap = "2.33.0"
indicatif = "0.15.0"
jsonrpc-core = "14.2.0"
jsonrpc-core = "15.0.0"
log = "0.4.8"
rayon = "1.3.1"
net2 = "0.2.37"
rayon = "1.4.0"
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
semver = "0.11.0"
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.3.0" }
solana-net-utils = { path = "../net-utils", version = "1.3.0" }
solana-sdk = { path = "../sdk", version = "1.3.0" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.0" }
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
solana-account-decoder = { path = "../account-decoder", version = "1.3.24" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.24" }
solana-net-utils = { path = "../net-utils", version = "1.3.24" }
solana-sdk = { path = "../sdk", version = "1.3.24" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.24" }
solana-version = { path = "../version", version = "1.3.24" }
solana-vote-program = { path = "../programs/vote", version = "1.3.24" }
thiserror = "1.0"
tungstenite = "0.10.1"
url = "2.1.1"
[dev-dependencies]
assert_matches = "1.3.0"
jsonrpc-core = "14.2.0"
jsonrpc-http-server = "14.2.0"
solana-logger = { path = "../logger", version = "1.3.0" }
jsonrpc-core = "15.0.0"
jsonrpc-http-server = "15.0.0"
solana-logger = { path = "../logger", version = "1.3.24" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

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

View File

@ -50,10 +50,10 @@ impl Into<TransportError> for ClientErrorKind {
#[derive(Error, Debug)]
#[error("{kind}")]
pub struct ClientError {
request: Option<rpc_request::RpcRequest>,
pub request: Option<rpc_request::RpcRequest>,
#[source]
kind: ClientErrorKind,
pub kind: ClientErrorKind,
}
impl ClientError {

View File

@ -27,6 +27,13 @@ impl HttpSender {
}
}
#[derive(Deserialize, Debug)]
struct RpcErrorObject {
code: i64,
message: String,
/*data field omitted*/
}
impl RpcSender for HttpSender {
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
// Concurrent requests are not supported so reuse the same request id for all requests
@ -63,11 +70,20 @@ impl RpcSender for HttpSender {
let json: serde_json::Value = serde_json::from_str(&response.text()?)?;
if json["error"].is_object() {
return Err(RpcError::RpcRequestError(format!(
"RPC Error response: {}",
serde_json::to_string(&json["error"]).unwrap()
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone())
{
Ok(rpc_error_object) => Err(RpcError::RpcResponseError {
code: rpc_error_object.code,
message: rpc_error_object.message,
}
.into()),
Err(err) => Err(RpcError::RpcRequestError(format!(
"Failed to deserialize RPC error response: {} [{}]",
serde_json::to_string(&json["error"]).unwrap(),
err
))
.into());
.into()),
};
}
return Ok(json["result"].clone());
}

View File

@ -1,13 +1,16 @@
#[macro_use]
extern crate serde_derive;
pub mod blockhash_query;
pub mod client_error;
pub mod http_sender;
pub mod mock_sender;
pub mod nonce_utils;
pub mod perf_utils;
pub mod pubsub_client;
pub mod rpc_client;
pub mod rpc_config;
pub mod rpc_custom_error;
pub mod rpc_filter;
pub mod rpc_request;
pub mod rpc_response;

View File

@ -1,17 +1,19 @@
use crate::{
client_error::Result,
rpc_request::RpcRequest,
rpc_response::{Response, RpcResponseContext},
rpc_response::{Response, RpcResponseContext, RpcVersionInfo},
rpc_sender::RpcSender,
};
use serde_json::{Number, Value};
use serde_json::{json, Number, Value};
use solana_sdk::{
epoch_info::EpochInfo,
fee_calculator::{FeeCalculator, FeeRateGovernor},
instruction::InstructionError,
signature::Signature,
transaction::{self, Transaction, TransactionError},
};
use solana_transaction_status::TransactionStatus;
use solana_version::Version;
use std::{collections::HashMap, sync::RwLock};
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
@ -57,6 +59,13 @@ impl RpcSender for MockSender {
serde_json::to_value(FeeCalculator::default()).unwrap(),
),
})?,
RpcRequest::GetEpochInfo => serde_json::to_value(EpochInfo {
epoch: 1,
slot_index: 2,
slots_in_epoch: 32,
absolute_slot: 34,
block_height: 34,
})?,
RpcRequest::GetFeeCalculatorForBlockhash => {
let value = if self.url == "blockhash_expired" {
Value::Null
@ -94,9 +103,15 @@ impl RpcSender for MockSender {
err,
})
};
let statuses: Vec<Option<TransactionStatus>> = params.as_array().unwrap()[0]
.as_array()
.unwrap()
.iter()
.map(|_| status.clone())
.collect();
serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 },
value: vec![status],
value: statuses,
})?
}
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
@ -106,13 +121,20 @@ impl RpcSender for MockSender {
Signature::new(&[8; 64]).to_string()
} else {
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
let data = bs58::decode(tx_str).into_vec().unwrap();
let data = base64::decode(tx_str).unwrap();
let tx: Transaction = bincode::deserialize(&data).unwrap();
tx.signatures[0].to_string()
};
Value::String(signature)
}
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)),
RpcRequest::GetVersion => {
let version = Version::default();
json!(RpcVersionInfo {
solana_core: version.to_string(),
feature_set: Some(version.feature_set),
})
}
_ => Value::Null,
};
Ok(val)

82
client/src/nonce_utils.rs Normal file
View File

@ -0,0 +1,82 @@
use crate::rpc_client::RpcClient;
use solana_sdk::{
account::Account,
account_utils::StateMut,
commitment_config::CommitmentConfig,
nonce::{
state::{Data, Versions},
State,
},
pubkey::Pubkey,
system_program,
};
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum Error {
#[error("invalid account owner")]
InvalidAccountOwner,
#[error("invalid account data")]
InvalidAccountData,
#[error("unexpected account data size")]
UnexpectedDataSize,
#[error("query hash does not match stored hash")]
InvalidHash,
#[error("query authority does not match account authority")]
InvalidAuthority,
#[error("invalid state for requested operation")]
InvalidStateForOperation,
#[error("client error: {0}")]
Client(String),
}
pub fn get_account(rpc_client: &RpcClient, nonce_pubkey: &Pubkey) -> Result<Account, Error> {
get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default())
}
pub fn get_account_with_commitment(
rpc_client: &RpcClient,
nonce_pubkey: &Pubkey,
commitment: CommitmentConfig,
) -> Result<Account, Error> {
rpc_client
.get_account_with_commitment(nonce_pubkey, commitment)
.map_err(|e| Error::Client(format!("{}", e)))
.and_then(|result| {
result
.value
.ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey)))
})
.and_then(|a| match account_identity_ok(&a) {
Ok(()) => Ok(a),
Err(e) => Err(e),
})
}
pub fn account_identity_ok(account: &Account) -> Result<(), Error> {
if account.owner != system_program::id() {
Err(Error::InvalidAccountOwner)
} else if account.data.is_empty() {
Err(Error::UnexpectedDataSize)
} else {
Ok(())
}
}
pub fn state_from_account(account: &Account) -> Result<State, Error> {
account_identity_ok(account)?;
StateMut::<Versions>::state(account)
.map_err(|_| Error::InvalidAccountData)
.map(|v| v.convert_to_current())
}
pub fn data_from_account(account: &Account) -> Result<Data, Error> {
account_identity_ok(account)?;
state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
}
pub fn data_from_state(state: &State) -> Result<&Data, Error> {
match state {
State::Uninitialized => Err(Error::InvalidStateForOperation),
State::Initialized(data) => Ok(data),
}
}

View File

@ -1,10 +1,12 @@
use crate::rpc_response::{Response as RpcResponse, RpcSignatureResult, SlotInfo};
use log::*;
use serde::{de::DeserializeOwned, Deserialize};
use serde::de::DeserializeOwned;
use serde_json::{
json,
value::Value::{Number, Object},
Map, Value,
};
use solana_sdk::signature::Signature;
use std::{
marker::PhantomData,
sync::{
@ -18,6 +20,8 @@ use thiserror::Error;
use tungstenite::{client::AutoStream, connect, Message, WebSocket};
use url::{ParseError, Url};
type PubsubSignatureResponse = PubsubClientSubscription<RpcResponse<RpcSignatureResult>>;
#[derive(Debug, Error)]
pub enum PubsubClientError {
#[error("url parse error")]
@ -33,13 +37,6 @@ pub enum PubsubClientError {
UnexpectedMessageError,
}
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub struct SlotInfoMessage {
pub parent: u64,
pub root: u64,
pub slot: u64,
}
pub struct PubsubClientSubscription<T>
where
T: DeserializeOwned,
@ -73,18 +70,12 @@ where
{
fn send_subscribe(
writable_socket: &Arc<RwLock<WebSocket<AutoStream>>>,
operation: &str,
body: String,
) -> Result<u64, PubsubClientError> {
let method = format!("{}Subscribe", operation);
writable_socket
.write()
.unwrap()
.write_message(Message::Text(
json!({
"jsonrpc":"2.0","id":1,"method":method,"params":[]
})
.to_string(),
))?;
.write_message(Message::Text(body))?;
let message = writable_socket.write().unwrap().read_message()?;
Self::extract_subscription_id(message)
}
@ -148,30 +139,28 @@ where
}
const SLOT_OPERATION: &str = "slot";
const SIGNATURE_OPERATION: &str = "signature";
pub struct PubsubClient {}
impl PubsubClient {
pub fn slot_subscribe(
url: &str,
) -> Result<
(
PubsubClientSubscription<SlotInfoMessage>,
Receiver<SlotInfoMessage>,
),
PubsubClientError,
> {
) -> Result<(PubsubClientSubscription<SlotInfo>, Receiver<SlotInfo>), PubsubClientError> {
let url = Url::parse(url)?;
let (socket, _response) = connect(url)?;
let (sender, receiver) = channel::<SlotInfoMessage>();
let (sender, receiver) = channel::<SlotInfo>();
let socket = Arc::new(RwLock::new(socket));
let socket_clone = socket.clone();
let exit = Arc::new(AtomicBool::new(false));
let exit_clone = exit.clone();
let subscription_id = PubsubClientSubscription::<SlotInfoMessage>::send_subscribe(
let subscription_id = PubsubClientSubscription::<SlotInfo>::send_subscribe(
&socket_clone,
SLOT_OPERATION,
json!({
"jsonrpc":"2.0","id":1,"method":format!("{}Subscribe", SLOT_OPERATION),"params":[]
})
.to_string(),
)
.unwrap();
@ -181,7 +170,80 @@ impl PubsubClient {
break;
}
let message: Result<SlotInfoMessage, PubsubClientError> =
let message: Result<SlotInfo, PubsubClientError> =
PubsubClientSubscription::read_message(&socket_clone);
if let Ok(msg) = message {
match sender.send(msg) {
Ok(_) => (),
Err(err) => {
info!("receive error: {:?}", err);
break;
}
}
} else {
info!("receive error: {:?}", message);
break;
}
}
info!("websocket - exited receive loop");
});
let result: PubsubClientSubscription<SlotInfo> = PubsubClientSubscription {
message_type: PhantomData,
operation: SLOT_OPERATION,
socket,
subscription_id,
t_cleanup: Some(t_cleanup),
exit,
};
Ok((result, receiver))
}
pub fn signature_subscribe(
url: &str,
signature: &Signature,
) -> Result<
(
PubsubSignatureResponse,
Receiver<RpcResponse<RpcSignatureResult>>,
),
PubsubClientError,
> {
let url = Url::parse(url)?;
let (socket, _response) = connect(url)?;
let (sender, receiver) = channel::<RpcResponse<RpcSignatureResult>>();
let socket = Arc::new(RwLock::new(socket));
let socket_clone = socket.clone();
let exit = Arc::new(AtomicBool::new(false));
let exit_clone = exit.clone();
let body = json!({
"jsonrpc":"2.0",
"id":1,
"method":format!("{}Subscribe", SIGNATURE_OPERATION),
"params":[
signature.to_string(),
{"enableReceivedNotification": true }
]
})
.to_string();
let subscription_id =
PubsubClientSubscription::<RpcResponse<RpcSignatureResult>>::send_subscribe(
&socket_clone,
body,
)
.unwrap();
let t_cleanup = std::thread::spawn(move || {
loop {
if exit_clone.load(Ordering::Relaxed) {
break;
}
let message: Result<RpcResponse<RpcSignatureResult>, PubsubClientError> =
PubsubClientSubscription::read_message(&socket_clone);
if let Ok(msg) = message {
@ -201,9 +263,10 @@ impl PubsubClient {
info!("websocket - exited receive loop");
});
let result: PubsubClientSubscription<SlotInfoMessage> = PubsubClientSubscription {
let result: PubsubClientSubscription<RpcResponse<RpcSignatureResult>> =
PubsubClientSubscription {
message_type: PhantomData,
operation: SLOT_OPERATION,
operation: SIGNATURE_OPERATION,
socket,
subscription_id,
t_cleanup: Some(t_cleanup),

View File

@ -2,7 +2,12 @@ use crate::{
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
http_sender::HttpSender,
mock_sender::{MockSender, Mocks},
rpc_config::{RpcLargestAccountsConfig, RpcSendTransactionConfig, RpcTokenAccountsFilter},
rpc_config::RpcAccountInfoConfig,
rpc_config::{
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
RpcTokenAccountsFilter,
},
rpc_request::{RpcError, RpcRequest, TokenAccountsFilter},
rpc_response::*,
rpc_sender::RpcSender,
@ -12,8 +17,8 @@ use indicatif::{ProgressBar, ProgressStyle};
use log::*;
use serde_json::{json, Value};
use solana_account_decoder::{
parse_token::{parse_token, TokenAccountType, UiMint, UiMultisig, UiTokenAccount},
UiAccount,
parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount},
UiAccount, UiAccountData, UiAccountEncoding,
};
use solana_sdk::{
account::Account,
@ -28,26 +33,49 @@ use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::Signature,
transaction::{self, Transaction},
transaction::{self, uses_durable_nonce, Transaction},
};
use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
};
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::{
net::SocketAddr,
sync::RwLock,
thread::sleep,
time::{Duration, Instant},
};
pub struct RpcClient {
sender: Box<dyn RpcSender + Send + Sync + 'static>,
default_cluster_transaction_encoding: RwLock<Option<UiTransactionEncoding>>,
}
fn serialize_encode_transaction(
transaction: &Transaction,
encoding: UiTransactionEncoding,
) -> ClientResult<String> {
let serialized = serialize(transaction)
.map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?;
let encoded = match encoding {
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
UiTransactionEncoding::Base64 => base64::encode(serialized),
_ => {
return Err(ClientErrorKind::Custom(format!(
"unsupported transaction encoding: {}. Supported encodings: base58, base64",
encoding
))
.into())
}
};
Ok(encoded)
}
impl RpcClient {
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(sender: T) -> Self {
Self {
sender: Box::new(sender),
default_cluster_transaction_encoding: RwLock::new(None),
}
}
@ -103,13 +131,46 @@ impl RpcClient {
self.send_transaction_with_config(transaction, RpcSendTransactionConfig::default())
}
fn default_cluster_transaction_encoding(&self) -> Result<UiTransactionEncoding, RpcError> {
let default_cluster_transaction_encoding =
self.default_cluster_transaction_encoding.read().unwrap();
if let Some(encoding) = *default_cluster_transaction_encoding {
Ok(encoding)
} else {
drop(default_cluster_transaction_encoding);
let cluster_version = self.get_version().map_err(|e| {
RpcError::RpcRequestError(format!("cluster version query failed: {}", e))
})?;
let cluster_version =
semver::Version::parse(&cluster_version.solana_core).map_err(|e| {
RpcError::RpcRequestError(format!("failed to parse cluster version: {}", e))
})?;
// Prefer base64 since 1.3.16
let encoding = if cluster_version < semver::Version::new(1, 3, 16) {
UiTransactionEncoding::Base58
} else {
UiTransactionEncoding::Base64
};
*self.default_cluster_transaction_encoding.write().unwrap() = Some(encoding);
Ok(encoding)
}
}
pub fn send_transaction_with_config(
&self,
transaction: &Transaction,
config: RpcSendTransactionConfig,
) -> ClientResult<Signature> {
let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
let encoding = if let Some(encoding) = config.encoding {
encoding
} else {
self.default_cluster_transaction_encoding()?
};
let config = RpcSendTransactionConfig {
encoding: Some(encoding),
..config
};
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
let signature_base58_str: String = self.send(
RpcRequest::SendTransaction,
json!([serialized_encoded, config]),
@ -136,12 +197,28 @@ impl RpcClient {
pub fn simulate_transaction(
&self,
transaction: &Transaction,
sig_verify: bool,
) -> RpcResult<RpcSimulateTransactionResult> {
let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
self.simulate_transaction_with_config(transaction, RpcSimulateTransactionConfig::default())
}
pub fn simulate_transaction_with_config(
&self,
transaction: &Transaction,
config: RpcSimulateTransactionConfig,
) -> RpcResult<RpcSimulateTransactionResult> {
let encoding = if let Some(encoding) = config.encoding {
encoding
} else {
self.default_cluster_transaction_encoding()?
};
let config = RpcSimulateTransactionConfig {
encoding: Some(encoding),
..config
};
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
self.send(
RpcRequest::SimulateTransaction,
json!([serialized_encoded, { "sigVerify": sig_verify }]),
json!([serialized_encoded, config]),
)
}
@ -160,6 +237,19 @@ impl RpcClient {
self.send(RpcRequest::GetSignatureStatuses, json!([signatures]))
}
pub fn get_signature_statuses_with_history(
&self,
signatures: &[Signature],
) -> RpcResult<Vec<Option<TransactionStatus>>> {
let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
self.send(
RpcRequest::GetSignatureStatuses,
json!([signatures, {
"searchTransactionHistory": true
}]),
)
}
pub fn get_signature_status_with_commitment(
&self,
signature: &Signature,
@ -244,7 +334,7 @@ impl RpcClient {
self.send(RpcRequest::GetClusterNodes, Value::Null)
}
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<ConfirmedBlock> {
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
self.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Json)
}
@ -252,7 +342,7 @@ impl RpcClient {
&self,
slot: Slot,
encoding: UiTransactionEncoding,
) -> ClientResult<ConfirmedBlock> {
) -> ClientResult<EncodedConfirmedBlock> {
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
}
@ -267,6 +357,17 @@ impl RpcClient {
)
}
pub fn get_confirmed_blocks_with_limit(
&self,
start_slot: Slot,
limit: usize,
) -> ClientResult<Vec<Slot>> {
self.send(
RpcRequest::GetConfirmedBlocksWithLimit,
json!([start_slot, limit]),
)
}
pub fn get_confirmed_signatures_for_address(
&self,
address: &Pubkey,
@ -289,11 +390,40 @@ impl RpcClient {
Ok(signatures)
}
pub fn get_confirmed_signatures_for_address2(
&self,
address: &Pubkey,
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
self.get_confirmed_signatures_for_address2_with_config(
address,
GetConfirmedSignaturesForAddress2Config::default(),
)
}
pub fn get_confirmed_signatures_for_address2_with_config(
&self,
address: &Pubkey,
config: GetConfirmedSignaturesForAddress2Config,
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
let config = RpcGetConfirmedSignaturesForAddress2Config {
before: config.before.map(|signature| signature.to_string()),
until: config.until.map(|signature| signature.to_string()),
limit: config.limit,
};
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
RpcRequest::GetConfirmedSignaturesForAddress2,
json!([address.to_string(), config]),
)?;
Ok(result)
}
pub fn get_confirmed_transaction(
&self,
signature: &Signature,
encoding: UiTransactionEncoding,
) -> ClientResult<ConfirmedTransaction> {
) -> ClientResult<EncodedConfirmedTransaction> {
self.send(
RpcRequest::GetConfirmedTransaction,
json!([signature.to_string(), encoding]),
@ -381,15 +511,25 @@ impl RpcClient {
&self,
transaction: &Transaction,
) -> ClientResult<Signature> {
let mut send_retries = 20;
loop {
let mut status_retries = 15;
let signature = self.send_transaction(transaction)?;
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
self.get_recent_blockhash_with_commitment(CommitmentConfig::recent())?
.value
.0
} else {
transaction.message.recent_blockhash
};
let status = loop {
let status = self.get_signature_status(&signature)?;
if status.is_none() {
status_retries -= 1;
if status_retries == 0 {
if self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::recent(),
)?
.value
.is_none()
{
break status;
}
} else {
@ -400,25 +540,19 @@ impl RpcClient {
sleep(Duration::from_millis(500));
}
};
send_retries = if let Some(result) = status.clone() {
if let Some(result) = status {
match result {
Ok(_) => return Ok(signature),
Err(_) => 0,
Ok(_) => Ok(signature),
Err(err) => Err(err.into()),
}
} else {
send_retries - 1
};
if send_retries == 0 {
if let Some(err) = status {
return Err(err.unwrap_err().into());
} else {
return Err(
RpcError::ForUser("unable to confirm transaction. \
Err(RpcError::ForUser(
"unable to confirm transaction. \
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds".to_string()).into(),
);
}
}
and insufficient fee-payer funds"
.to_string(),
)
.into())
}
}
@ -433,9 +567,14 @@ impl RpcClient {
pubkey: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<Option<Account>> {
let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64),
commitment: Some(commitment_config),
data_slice: None,
};
let response = self.sender.send(
RpcRequest::GetAccountInfo,
json!([pubkey.to_string(), commitment_config]),
json!([pubkey.to_string(), config]),
);
response
@ -464,6 +603,38 @@ impl RpcClient {
})?
}
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
Ok(self
.get_multiple_accounts_with_commitment(pubkeys, CommitmentConfig::default())?
.value)
}
pub fn get_multiple_accounts_with_commitment(
&self,
pubkeys: &[Pubkey],
commitment_config: CommitmentConfig,
) -> RpcResult<Vec<Option<Account>>> {
let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64),
commitment: Some(commitment_config),
data_slice: None,
};
let pubkeys: Vec<_> = pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
let response = self.send(RpcRequest::GetMultipleAccounts, json!([pubkeys, config]))?;
let Response {
context,
value: accounts,
} = serde_json::from_value::<Response<Vec<Option<UiAccount>>>>(response)?;
let accounts: Vec<Option<Account>> = accounts
.into_iter()
.map(|rpc_account| rpc_account.map(|a| a.decode()).flatten())
.collect();
Ok(Response {
context,
value: accounts,
})
}
pub fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult<Vec<u8>> {
Ok(self.get_account(pubkey)?.data)
}
@ -504,8 +675,27 @@ impl RpcClient {
}
pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult<Vec<(Pubkey, Account)>> {
let accounts: Vec<RpcKeyedAccount> =
self.send(RpcRequest::GetProgramAccounts, json!([pubkey.to_string()]))?;
self.get_program_accounts_with_config(
pubkey,
RpcProgramAccountsConfig {
filters: None,
account_config: RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64),
..RpcAccountInfoConfig::default()
},
},
)
}
pub fn get_program_accounts_with_config(
&self,
pubkey: &Pubkey,
config: RpcProgramAccountsConfig,
) -> ClientResult<Vec<(Pubkey, Account)>> {
let accounts: Vec<RpcKeyedAccount> = self.send(
RpcRequest::GetProgramAccounts,
json!([pubkey.to_string(), config]),
)?;
parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
}
@ -642,6 +832,10 @@ impl RpcClient {
.into())
}
pub fn get_first_available_block(&self) -> ClientResult<Slot> {
self.send(RpcRequest::GetFirstAvailableBlock, Value::Null)
}
pub fn get_genesis_hash(&self) -> ClientResult<Hash> {
let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null)?;
let hash = hash_str.parse().map_err(|_| {
@ -664,77 +858,57 @@ impl RpcClient {
pubkey: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<Option<UiTokenAccount>> {
let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::JsonParsed),
commitment: Some(commitment_config),
data_slice: None,
};
let response = self.sender.send(
RpcRequest::GetAccountInfo,
json!([pubkey.to_string(), config]),
);
response
.map(|result_json| {
if result_json.is_null() {
return Err(
RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into(),
);
}
let Response {
context,
value: account,
} = self.get_account_with_commitment(pubkey, commitment_config)?;
Ok(Response {
value: rpc_account,
} = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
trace!("Response account {:?} {:?}", pubkey, rpc_account);
let response = {
if let Some(rpc_account) = rpc_account {
if let UiAccountData::Json(account_data) = rpc_account.data {
let token_account_type: TokenAccountType =
serde_json::from_value(account_data.parsed)?;
if let TokenAccountType::Account(token_account) = token_account_type {
return Ok(Response {
context,
value: account
.map(|account| match parse_token(&account.data) {
Ok(TokenAccountType::Account(ui_token_account)) => Some(ui_token_account),
_ => None,
})
.flatten(),
value: Some(token_account),
});
}
}
}
Err(Into::<ClientError>::into(RpcError::ForUser(format!(
"Account could not be parsed as token account: pubkey={}",
pubkey
))))
};
response?
})
.map_err(|err| {
Into::<ClientError>::into(RpcError::ForUser(format!(
"AccountNotFound: pubkey={}: {}",
pubkey, err
)))
})?
}
pub fn get_token_mint(&self, pubkey: &Pubkey) -> ClientResult<Option<UiMint>> {
Ok(self
.get_token_mint_with_commitment(pubkey, CommitmentConfig::default())?
.value)
}
pub fn get_token_mint_with_commitment(
&self,
pubkey: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<Option<UiMint>> {
let Response {
context,
value: account,
} = self.get_account_with_commitment(pubkey, commitment_config)?;
Ok(Response {
context,
value: account
.map(|account| match parse_token(&account.data) {
Ok(TokenAccountType::Mint(ui_token_mint)) => Some(ui_token_mint),
_ => None,
})
.flatten(),
})
}
pub fn get_token_multisig(&self, pubkey: &Pubkey) -> ClientResult<Option<UiMultisig>> {
Ok(self
.get_token_multisig_with_commitment(pubkey, CommitmentConfig::default())?
.value)
}
pub fn get_token_multisig_with_commitment(
&self,
pubkey: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<Option<UiMultisig>> {
let Response {
context,
value: account,
} = self.get_account_with_commitment(pubkey, commitment_config)?;
Ok(Response {
context,
value: account
.map(|account| match parse_token(&account.data) {
Ok(TokenAccountType::Multisig(ui_token_multisig)) => Some(ui_token_multisig),
_ => None,
})
.flatten(),
})
}
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<RpcTokenAmount> {
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
Ok(self
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
.value)
@ -744,7 +918,7 @@ impl RpcClient {
&self,
pubkey: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<RpcTokenAmount> {
) -> RpcResult<UiTokenAmount> {
self.send(
RpcRequest::GetTokenAccountBalance,
json!([pubkey.to_string(), commitment_config]),
@ -755,7 +929,7 @@ impl RpcClient {
&self,
delegate: &Pubkey,
token_account_filter: TokenAccountsFilter,
) -> ClientResult<Vec<(Pubkey, UiTokenAccount)>> {
) -> ClientResult<Vec<RpcKeyedAccount>> {
Ok(self
.get_token_accounts_by_delegate_with_commitment(
delegate,
@ -770,39 +944,31 @@ impl RpcClient {
delegate: &Pubkey,
token_account_filter: TokenAccountsFilter,
commitment_config: CommitmentConfig,
) -> RpcResult<Vec<(Pubkey, UiTokenAccount)>> {
) -> RpcResult<Vec<RpcKeyedAccount>> {
let token_account_filter = match token_account_filter {
TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
TokenAccountsFilter::ProgramId(program_id) => {
RpcTokenAccountsFilter::ProgramId(program_id.to_string())
}
};
let Response {
context,
value: accounts,
} = self.send(
RpcRequest::GetTokenAccountsByDelegate,
json!([
delegate.to_string(),
token_account_filter,
commitment_config
]),
)?;
let pubkey_accounts = accounts_to_token_accounts(parse_keyed_accounts(
accounts,
RpcRequest::GetTokenAccountsByDelegate,
)?);
Ok(Response {
context,
value: pubkey_accounts,
})
let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::JsonParsed),
commitment: Some(commitment_config),
data_slice: None,
};
self.send(
RpcRequest::GetTokenAccountsByOwner,
json!([delegate.to_string(), token_account_filter, config]),
)
}
pub fn get_token_accounts_by_owner(
&self,
owner: &Pubkey,
token_account_filter: TokenAccountsFilter,
) -> ClientResult<Vec<(Pubkey, UiTokenAccount)>> {
) -> ClientResult<Vec<RpcKeyedAccount>> {
Ok(self
.get_token_accounts_by_owner_with_commitment(
owner,
@ -817,31 +983,27 @@ impl RpcClient {
owner: &Pubkey,
token_account_filter: TokenAccountsFilter,
commitment_config: CommitmentConfig,
) -> RpcResult<Vec<(Pubkey, UiTokenAccount)>> {
) -> RpcResult<Vec<RpcKeyedAccount>> {
let token_account_filter = match token_account_filter {
TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
TokenAccountsFilter::ProgramId(program_id) => {
RpcTokenAccountsFilter::ProgramId(program_id.to_string())
}
};
let Response {
context,
value: accounts,
} = self.send(
let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::JsonParsed),
commitment: Some(commitment_config),
data_slice: None,
};
self.send(
RpcRequest::GetTokenAccountsByOwner,
json!([owner.to_string(), token_account_filter, commitment_config]),
)?;
let pubkey_accounts = accounts_to_token_accounts(parse_keyed_accounts(
accounts,
RpcRequest::GetTokenAccountsByDelegate,
)?);
Ok(Response {
context,
value: pubkey_accounts,
})
json!([owner.to_string(), token_account_filter, config]),
)
}
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<RpcTokenAmount> {
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
Ok(self
.get_token_supply_with_commitment(mint, CommitmentConfig::default())?
.value)
@ -851,7 +1013,7 @@ impl RpcClient {
&self,
mint: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<RpcTokenAmount> {
) -> RpcResult<UiTokenAmount> {
self.send(
RpcRequest::GetTokenSupply,
json!([mint.to_string(), commitment_config]),
@ -1065,22 +1227,31 @@ impl RpcClient {
let progress_bar = new_spinner_progress_bar();
let mut send_retries = 20;
let signature = loop {
progress_bar.set_message(&format!(
"[{}/{}] Finalizing transaction {}",
confirmations, desired_confirmations, transaction.signatures[0],
));
let mut status_retries = 15;
let (signature, status) = loop {
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
self.get_recent_blockhash_with_commitment(CommitmentConfig::recent())?
.value
.0
} else {
transaction.message.recent_blockhash
};
let signature = self.send_transaction_with_config(transaction, config)?;
let (signature, status) = loop {
// Get recent commitment in order to count confirmations for successful transactions
let status = self
.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
let status =
self.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
if status.is_none() {
status_retries -= 1;
if status_retries == 0 {
if self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::recent(),
)?
.value
.is_none()
{
break (signature, status);
}
} else {
@ -1091,36 +1262,19 @@ impl RpcClient {
sleep(Duration::from_millis(500));
}
};
send_retries = if let Some(result) = status.clone() {
match result {
Ok(_) => 0,
// If transaction errors, return right away; no point in counting confirmations
Err(_) => 0,
}
} else {
send_retries - 1
};
if send_retries == 0 {
if let Some(result) = status {
match result {
Ok(_) => {
break signature;
}
Err(err) => {
if let Err(err) = result {
return Err(err.into());
}
}
} else {
return Err(RpcError::ForUser(
"unable to confirm transaction. \
This can happen in situations such as transaction \
expiration and insufficient fee-payer funds"
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds"
.to_string(),
)
.into());
}
}
};
let now = Instant::now();
loop {
match commitment.commitment {
@ -1181,6 +1335,13 @@ impl RpcClient {
}
}
#[derive(Debug, Default)]
pub struct GetConfirmedSignaturesForAddress2Config {
pub before: Option<Signature>,
pub until: Option<Signature>,
pub limit: Option<usize>,
}
fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
progress_bar
@ -1222,18 +1383,6 @@ fn parse_keyed_accounts(
Ok(pubkey_accounts)
}
fn accounts_to_token_accounts(
pubkey_accounts: Vec<(Pubkey, Account)>,
) -> Vec<(Pubkey, UiTokenAccount)> {
pubkey_accounts
.into_iter()
.filter_map(|(pubkey, account)| match parse_token(&account.data) {
Ok(TokenAccountType::Account(ui_token_account)) => Some((pubkey, ui_token_account)),
_ => None,
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
@ -1307,7 +1456,7 @@ mod tests {
let rpc_client = RpcClient::new_mock("succeeds".to_string());
let key = Keypair::new();
let to = Pubkey::new_rand();
let to = solana_sdk::pubkey::new_rand();
let blockhash = Hash::default();
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
@ -1360,7 +1509,7 @@ mod tests {
let rpc_client = RpcClient::new_mock("succeeds".to_string());
let key = Keypair::new();
let to = Pubkey::new_rand();
let to = solana_sdk::pubkey::new_rand();
let blockhash = Hash::default();
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
let result = rpc_client.send_and_confirm_transaction(&tx);

View File

@ -1,6 +1,10 @@
use crate::rpc_filter::RpcFilterType;
use solana_account_decoder::UiAccountEncoding;
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig};
use solana_sdk::{
clock::Epoch,
commitment_config::{CommitmentConfig, CommitmentLevel},
};
use solana_transaction_status::UiTransactionEncoding;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -11,13 +15,20 @@ pub struct RpcSignatureStatusConfig {
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSendTransactionConfig {
#[serde(default)]
pub skip_preflight: bool,
pub preflight_commitment: Option<CommitmentLevel>,
pub encoding: Option<UiTransactionEncoding>,
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSimulateTransactionConfig {
#[serde(default)]
pub sig_verify: bool,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub encoding: Option<UiTransactionEncoding>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -47,6 +58,7 @@ pub struct RpcStakeConfig {
#[serde(rename_all = "camelCase")]
pub struct RpcAccountInfoConfig {
pub encoding: Option<UiAccountEncoding>,
pub data_slice: Option<UiDataSliceConfig>,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
}
@ -65,3 +77,19 @@ pub enum RpcTokenAccountsFilter {
Mint(String),
ProgramId(String),
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSignatureSubscribeConfig {
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub enable_received_notification: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcGetConfirmedSignaturesForAddress2Config {
pub before: Option<String>, // Signature as base-58 string
pub until: Option<String>, // Signature as base-58 string
pub limit: Option<usize>,
}

View File

@ -0,0 +1,78 @@
//! Implementation defined RPC server errors
use crate::rpc_response::RpcSimulateTransactionResult;
use jsonrpc_core::{Error, ErrorCode};
use solana_sdk::clock::Slot;
pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005;
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
pub enum RpcCustomError {
BlockCleanedUp {
slot: Slot,
first_available_block: Slot,
},
SendTransactionPreflightFailure {
message: String,
result: RpcSimulateTransactionResult,
},
TransactionSignatureVerificationFailure,
BlockNotAvailable {
slot: Slot,
},
RpcNodeUnhealthy,
TransactionPrecompileVerificationFailure(solana_sdk::transaction::TransactionError),
}
impl From<RpcCustomError> for Error {
fn from(e: RpcCustomError) -> Self {
match e {
RpcCustomError::BlockCleanedUp {
slot,
first_available_block,
} => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP),
message: format!(
"Block {} cleaned up, does not exist on node. First available block: {}",
slot, first_available_block,
),
data: None,
},
RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
code: ErrorCode::ServerError(
JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
),
message,
data: Some(serde_json::json!(result)),
},
RpcCustomError::TransactionSignatureVerificationFailure => Self {
code: ErrorCode::ServerError(
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
),
message: "Transaction signature verification failure".to_string(),
data: None,
},
RpcCustomError::BlockNotAvailable { slot } => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE),
message: format!("Block not available for slot {}", slot),
data: None,
},
RpcCustomError::RpcNodeUnhealthy => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY),
message: "RPC node is unhealthy".to_string(),
data: None,
},
RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
code: ErrorCode::ServerError(
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
),
message: format!("Transaction precompile verification failure {:?}", e),
data: None,
},
}
}
}

View File

@ -13,13 +13,16 @@ pub enum RpcRequest {
GetClusterNodes,
GetConfirmedBlock,
GetConfirmedBlocks,
GetConfirmedBlocksWithLimit,
GetConfirmedSignaturesForAddress,
GetConfirmedSignaturesForAddress2,
GetConfirmedTransaction,
GetEpochInfo,
GetEpochSchedule,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetFees,
GetFirstAvailableBlock,
GetGenesisHash,
GetIdentity,
GetInflationGovernor,
@ -27,6 +30,7 @@ pub enum RpcRequest {
GetLargestAccounts,
GetLeaderSchedule,
GetMinimumBalanceForRentExemption,
GetMultipleAccounts,
GetProgramAccounts,
GetRecentBlockhash,
GetSignatureStatuses,
@ -64,13 +68,16 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetClusterNodes => "getClusterNodes",
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit",
RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress",
RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2",
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
RpcRequest::GetEpochInfo => "getEpochInfo",
RpcRequest::GetEpochSchedule => "getEpochSchedule",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetFees => "getFees",
RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
RpcRequest::GetGenesisHash => "getGenesisHash",
RpcRequest::GetIdentity => "getIdentity",
RpcRequest::GetInflationGovernor => "getInflationGovernor",
@ -78,6 +85,7 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetLargestAccounts => "getLargestAccounts",
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
RpcRequest::GetProgramAccounts => "getProgramAccounts",
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
@ -108,10 +116,12 @@ impl fmt::Display for RpcRequest {
}
}
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
// Validators that are this number of slots behind are considered delinquent
pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
@ -130,8 +140,10 @@ impl RpcRequest {
#[derive(Debug, Error)]
pub enum RpcError {
#[error("rpc request error: {0}")]
#[error("RPC request error: {0}")]
RpcRequestError(String),
#[error("RPC response error {code}: {message}")]
RpcResponseError { code: i64, message: String },
#[error("parse error: expected {0}")]
ParseError(String), /* "expected" */
// Anything in a `ForUser` needs to die. The caller should be
@ -216,7 +228,7 @@ mod tests {
// Test request with CommitmentConfig and params
let test_request = RpcRequest::GetTokenAccountsByOwner;
let mint = Pubkey::new_rand();
let mint = solana_sdk::pubkey::new_rand();
let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
let request = test_request
.build_request_json(1, json!([addr, token_account_filter, commitment_config]));

View File

@ -1,15 +1,15 @@
use crate::client_error;
use solana_account_decoder::UiAccount;
use solana_account_decoder::{parse_token::UiTokenAmount, UiAccount};
use solana_sdk::{
clock::{Epoch, Slot},
fee_calculator::{FeeCalculator, FeeRateGovernor},
inflation::Inflation,
transaction::{Result, TransactionError},
};
use std::{collections::HashMap, net::SocketAddr};
use solana_transaction_status::ConfirmedTransactionStatusWithSignature;
use std::{collections::HashMap, fmt, net::SocketAddr};
pub type RpcResult<T> = client_error::Result<Response<T>>;
pub type RpcAmount = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RpcResponseContext {
@ -94,13 +94,34 @@ pub struct RpcKeyedAccount {
pub account: UiAccount,
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
pub struct SlotInfo {
pub slot: Slot,
pub parent: Slot,
pub root: Slot,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase", untagged)]
pub enum RpcSignatureResult {
ProcessedSignature(ProcessedSignatureResult),
ReceivedSignature(ReceivedSignatureResult),
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcSignatureResult {
pub struct ProcessedSignatureResult {
pub err: Option<TransactionError>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub enum ReceivedSignatureResult {
ReceivedSignature,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcContactInfo {
/// Pubkey of the node as a base-58 string
pub pubkey: String,
@ -112,16 +133,37 @@ pub struct RpcContactInfo {
pub rpc: Option<SocketAddr>,
/// Software version
pub version: Option<String>,
/// First 4 bytes of the FeatureSet identifier
pub feature_set: Option<u32>,
}
/// Map of leader base58 identity pubkeys to the slot indices relative to the first epoch slot
pub type RpcLeaderSchedule = HashMap<String, Vec<usize>>;
#[derive(Serialize, Deserialize, Clone, Debug)]
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct RpcVersionInfo {
/// The current version of solana-core
pub solana_core: String,
/// first 4 bytes of the FeatureSet identifier
pub feature_set: Option<u32>,
}
impl fmt::Debug for RpcVersionInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.solana_core)
}
}
impl fmt::Display for RpcVersionInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(version) = self.solana_core.split_whitespace().next() {
// Display just the semver if possible
write!(f, "{}", version)
} else {
write!(f, "{}", self.solana_core)
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@ -221,18 +263,45 @@ pub struct RpcStakeActivation {
pub inactive: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RpcTokenAmount {
pub ui_amount: f64,
pub decimals: u8,
pub amount: RpcAmount,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RpcTokenAccountBalance {
pub address: String,
#[serde(flatten)]
pub amount: RpcTokenAmount,
pub amount: UiTokenAmount,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcConfirmedTransactionStatusWithSignature {
pub signature: String,
pub slot: Slot,
pub err: Option<TransactionError>,
pub memo: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcPerfSample {
pub slot: Slot,
pub num_transactions: u64,
pub num_slots: u64,
pub sample_period_secs: u16,
}
impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionStatusWithSignature {
fn from(value: ConfirmedTransactionStatusWithSignature) -> Self {
let ConfirmedTransactionStatusWithSignature {
signature,
slot,
err,
memo,
} = value;
Self {
signature: signature.to_string(),
slot,
err,
memo,
}
}
}

View File

@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "1.3.0"
version = "1.3.24"
documentation = "https://docs.rs/solana"
homepage = "https://solana.com/"
readme = "../README.md"
@ -14,6 +14,7 @@ edition = "2018"
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }
[dependencies]
base64 = "0.12.3"
bincode = "1.3.1"
bv = { version = "0.11.1", features = ["serde"] }
bs58 = "0.3.1"
@ -24,57 +25,60 @@ crossbeam-channel = "0.4"
ed25519-dalek = "=1.0.0-pre.4"
fs_extra = "1.1.0"
flate2 = "1.0"
indexmap = "1.4"
indexmap = { version = "1.5", features = ["rayon"] }
itertools = "0.9.0"
jsonrpc-core = "14.2.0"
jsonrpc-core-client = { version = "14.2.0", features = ["ws"] }
jsonrpc-derive = "14.2.1"
jsonrpc-http-server = "14.2.0"
jsonrpc-pubsub = "14.2.0"
jsonrpc-ws-server = "14.2.0"
jsonrpc-core = "15.0.0"
jsonrpc-core-client = { version = "15.0.0", features = ["ws"] }
jsonrpc-derive = "15.0.0"
jsonrpc-http-server = "15.0.0"
jsonrpc-pubsub = "15.0.0"
jsonrpc-ws-server = "15.0.0"
log = "0.4.8"
lru = "0.6.0"
miow = "0.2.2"
net2 = "0.2.37"
num_cpus = "1.13.0"
num-traits = "0.2"
rand = "0.7.0"
rand_chacha = "0.2.2"
raptorq = "1.4.2"
rayon = "1.3.1"
rayon = "1.4.0"
regex = "1.3.9"
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.3.0" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.0" }
solana-budget-program = { path = "../programs/budget", version = "1.3.0" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.0" }
solana-client = { path = "../client", version = "1.3.0" }
solana-faucet = { path = "../faucet", version = "1.3.0" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.3.0" }
solana-ledger = { path = "../ledger", version = "1.3.0" }
solana-logger = { path = "../logger", version = "1.3.0" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.3.0" }
solana-metrics = { path = "../metrics", version = "1.3.0" }
solana-measure = { path = "../measure", version = "1.3.0" }
solana-net-utils = { path = "../net-utils", version = "1.3.0" }
solana-perf = { path = "../perf", version = "1.3.0" }
solana-runtime = { path = "../runtime", version = "1.3.0" }
solana-sdk = { path = "../sdk", version = "1.3.0" }
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.3.0" }
solana-stake-program = { path = "../programs/stake", version = "1.3.0" }
solana-streamer = { path = "../streamer", version = "1.3.0" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.0" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.0" }
solana-version = { path = "../version", version = "1.3.0" }
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
solana-vote-signer = { path = "../vote-signer", version = "1.3.0" }
spl-token-v1-0 = { package = "spl-token", version = "1.0.6", features = ["skip-no-mangle"] }
solana-account-decoder = { path = "../account-decoder", version = "1.3.24" }
solana-banks-server = { path = "../banks-server", version = "1.3.24" }
solana-budget-program = { path = "../programs/budget", version = "1.3.24" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.24" }
solana-client = { path = "../client", version = "1.3.24" }
solana-faucet = { path = "../faucet", version = "1.3.24" }
solana-ledger = { path = "../ledger", version = "1.3.24" }
solana-logger = { path = "../logger", version = "1.3.24" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.3.24" }
solana-metrics = { path = "../metrics", version = "1.3.24" }
solana-measure = { path = "../measure", version = "1.3.24" }
solana-net-utils = { path = "../net-utils", version = "1.3.24" }
solana-perf = { path = "../perf", version = "1.3.24" }
solana-runtime = { path = "../runtime", version = "1.3.24" }
solana-sdk = { path = "../sdk", version = "1.3.24" }
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.3.24" }
solana-stake-program = { path = "../programs/stake", version = "1.3.24" }
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.3.24" }
solana-streamer = { path = "../streamer", version = "1.3.24" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.24" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.24" }
solana-version = { path = "../version", version = "1.3.24" }
solana-vote-program = { path = "../programs/vote", version = "1.3.24" }
solana-vote-signer = { path = "../vote-signer", version = "1.3.24" }
spl-token-v2-0 = { package = "spl-token", version = "=2.0.6", features = ["skip-no-mangle"] }
tempfile = "3.1.0"
thiserror = "1.0"
tokio = "0.1"
tokio-codec = "0.1"
tokio-fs = "0.1"
tokio-io = "0.1"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.3.0" }
tokio_01 = { version = "0.1", package = "tokio" }
tokio_fs_01 = { version = "0.1", package = "tokio-fs" }
tokio_io_01 = { version = "0.1", package = "tokio-io" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.3.24" }
tokio = { version = "0.2.22", features = ["full"] }
trees = "0.2.1"
[dev-dependencies]
@ -93,6 +97,15 @@ name = "banking_stage"
[[bench]]
name = "blockstore"
[[bench]]
name = "crds"
[[bench]]
name = "crds_gossip_pull"
[[bench]]
name = "crds_shards"
[[bench]]
name = "gen_keys"

View File

@ -20,7 +20,6 @@ use solana_runtime::bank::Bank;
use solana_sdk::genesis_config::GenesisConfig;
use solana_sdk::hash::Hash;
use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use solana_sdk::signature::Signature;
use solana_sdk::signature::Signer;
@ -56,7 +55,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
let bank = Arc::new(Bank::new(&genesis_config));
let ledger_path = get_tmp_ledger_path!();
let my_pubkey = Pubkey::new_rand();
let my_pubkey = solana_sdk::pubkey::new_rand();
{
let blockstore = Arc::new(
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
@ -73,6 +72,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
let batch_len = batch.packets.len();
packets.push((batch, vec![0usize; batch_len]));
}
let (s, _r) = unbounded();
// This tests the performance of buffering packets.
// If the packet buffers are copied, performance will be poor.
bencher.iter(move || {
@ -82,6 +82,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
&mut packets,
10_000,
None,
&s,
);
});
@ -92,15 +93,15 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
}
fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Transaction> {
let to_pubkey = Pubkey::new_rand();
let to_pubkey = solana_sdk::pubkey::new_rand();
let dummy = system_transaction::transfer(mint_keypair, &to_pubkey, 1, hash);
(0..txes)
.into_par_iter()
.map(|_| {
let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
new.message.account_keys[0] = Pubkey::new_rand();
new.message.account_keys[1] = Pubkey::new_rand();
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
new.message.account_keys[1] = solana_sdk::pubkey::new_rand();
new.signatures = vec![Signature::new(&sig[0..64])];
new
})
@ -114,7 +115,7 @@ fn make_programs_txs(txes: usize, hash: Hash) -> Vec<Transaction> {
let mut instructions = vec![];
let from_key = Keypair::new();
for _ in 1..progs {
let to_key = Pubkey::new_rand();
let to_key = solana_sdk::pubkey::new_rand();
instructions.push(system_instruction::transfer(&from_key.pubkey(), &to_key, 1));
}
let message = Message::new(&instructions, Some(&from_key.pubkey()));
@ -190,12 +191,14 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
create_test_recorder(&bank, &blockstore, None);
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let (s, _r) = unbounded();
let _banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
verified_receiver,
vote_receiver,
None,
s,
);
poh_recorder.lock().unwrap().set_bank(&bank);

View File

@ -8,7 +8,6 @@ use solana_core::broadcast_stage::{broadcast_shreds, get_broadcast_peers};
use solana_core::cluster_info::{ClusterInfo, Node};
use solana_core::contact_info::ContactInfo;
use solana_ledger::shred::Shred;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::timing::timestamp;
use std::{
collections::HashMap,
@ -20,7 +19,7 @@ use test::Bencher;
#[bench]
fn broadcast_shreds_bench(bencher: &mut Bencher) {
solana_logger::setup();
let leader_pubkey = Pubkey::new_rand();
let leader_pubkey = solana_sdk::pubkey::new_rand();
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info);
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
@ -30,7 +29,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
let mut stakes = HashMap::new();
const NUM_PEERS: usize = 200;
for _ in 0..NUM_PEERS {
let id = Pubkey::new_rand();
let id = solana_sdk::pubkey::new_rand();
let contact_info = ContactInfo::new_localhost(&id, timestamp());
cluster_info.insert_info(contact_info);
stakes.insert(id, thread_rng().gen_range(1, NUM_PEERS) as u64);

31
core/benches/crds.rs Normal file
View File

@ -0,0 +1,31 @@
#![feature(test)]
extern crate test;
use rand::{thread_rng, Rng};
use rayon::ThreadPoolBuilder;
use solana_core::crds::Crds;
use solana_core::crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
use solana_core::crds_value::CrdsValue;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
use test::Bencher;
#[bench]
fn bench_find_old_labels(bencher: &mut Bencher) {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut rng = thread_rng();
let mut crds = Crds::default();
let now = CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS + CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS / 1000;
std::iter::repeat_with(|| (CrdsValue::new_rand(&mut rng), rng.gen_range(0, now)))
.take(50_000)
.for_each(|(v, ts)| assert!(crds.insert(v, ts).is_ok()));
let mut timeouts = HashMap::new();
timeouts.insert(Pubkey::default(), CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS);
bencher.iter(|| {
let out = crds.find_old_labels(&thread_pool, now, &timeouts);
assert!(out.len() > 10);
assert!(out.len() < 250);
out
});
}

View File

@ -0,0 +1,52 @@
#![feature(test)]
extern crate test;
use rand::{thread_rng, Rng};
use rayon::ThreadPoolBuilder;
use solana_core::cluster_info::MAX_BLOOM_SIZE;
use solana_core::crds::Crds;
use solana_core::crds_gossip_pull::{CrdsFilter, CrdsGossipPull};
use solana_core::crds_value::CrdsValue;
use test::Bencher;
#[bench]
fn bench_hash_as_u64(bencher: &mut Bencher) {
let mut rng = thread_rng();
let hashes: Vec<_> = std::iter::repeat_with(|| solana_sdk::hash::new_rand(&mut rng))
.take(1000)
.collect();
bencher.iter(|| {
hashes
.iter()
.map(CrdsFilter::hash_as_u64)
.collect::<Vec<_>>()
});
}
#[bench]
fn bench_build_crds_filters(bencher: &mut Bencher) {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut rng = thread_rng();
let mut crds_gossip_pull = CrdsGossipPull::default();
let mut crds = Crds::default();
for _ in 0..50_000 {
crds_gossip_pull
.purged_values
.push_back((solana_sdk::hash::new_rand(&mut rng), rng.gen()));
}
let mut num_inserts = 0;
for _ in 0..90_000 {
if crds
.insert(CrdsValue::new_rand(&mut rng), rng.gen())
.is_ok()
{
num_inserts += 1;
}
}
assert_eq!(num_inserts, 90_000);
bencher.iter(|| {
let filters = crds_gossip_pull.build_crds_filters(&thread_pool, &crds, MAX_BLOOM_SIZE);
assert_eq!(filters.len(), 128);
});
}

View File

@ -0,0 +1,71 @@
#![feature(test)]
extern crate test;
use rand::{thread_rng, Rng};
use solana_core::contact_info::ContactInfo;
use solana_core::crds::VersionedCrdsValue;
use solana_core::crds_shards::CrdsShards;
use solana_core::crds_value::{CrdsData, CrdsValue};
use solana_sdk::timing::timestamp;
use test::Bencher;
const CRDS_SHARDS_BITS: u32 = 8;
fn new_test_crds_value() -> VersionedCrdsValue {
let data = CrdsData::ContactInfo(ContactInfo::new_localhost(
&solana_sdk::pubkey::new_rand(),
timestamp(),
));
VersionedCrdsValue::new(timestamp(), CrdsValue::new_unsigned(data))
}
fn bench_crds_shards_find(bencher: &mut Bencher, num_values: usize, mask_bits: u32) {
let values: Vec<VersionedCrdsValue> = std::iter::repeat_with(new_test_crds_value)
.take(num_values)
.collect();
let mut shards = CrdsShards::new(CRDS_SHARDS_BITS);
for (index, value) in values.iter().enumerate() {
assert!(shards.insert(index, value));
}
let mut rng = thread_rng();
bencher.iter(|| {
let mask = rng.gen();
let _hits = shards.find(mask, mask_bits).count();
});
}
#[bench]
fn bench_crds_shards_find_0(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 0);
}
#[bench]
fn bench_crds_shards_find_1(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 1);
}
#[bench]
fn bench_crds_shards_find_3(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 3);
}
#[bench]
fn bench_crds_shards_find_5(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 5);
}
#[bench]
fn bench_crds_shards_find_7(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 7);
}
#[bench]
fn bench_crds_shards_find_8(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 8);
}
#[bench]
fn bench_crds_shards_find_9(bencher: &mut Bencher) {
bench_crds_shards_find(bencher, 100_000, 9);
}

View File

@ -14,7 +14,6 @@ use solana_perf::packet::to_packets_chunked;
use solana_perf::test_tx::test_tx;
use solana_runtime::bank::Bank;
use solana_runtime::bank_forks::BankForks;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::timing::timestamp;
use std::net::UdpSocket;
use std::sync::atomic::{AtomicUsize, Ordering};
@ -33,7 +32,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
const NUM_PEERS: usize = 4;
let mut peer_sockets = Vec::new();
for _ in 0..NUM_PEERS {
let id = Pubkey::new_rand();
let id = solana_sdk::pubkey::new_rand();
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut contact_info = ContactInfo::new_localhost(&id, timestamp());
contact_info.tvu = socket.local_addr().unwrap();

View File

@ -24,12 +24,14 @@ use solana_perf::{
use solana_runtime::{
accounts_db::ErrorCounters,
bank::{Bank, TransactionBalancesSet, TransactionProcessResult},
bank_utils,
transaction_batch::TransactionBatch,
vote_sender_types::ReplayVoteSender,
};
use solana_sdk::{
clock::{
Slot, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE,
MAX_TRANSACTION_FORWARDING_DELAY, MAX_TRANSACTION_FORWARDING_DELAY_GPU,
Slot, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY,
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
},
poh_config::PohConfig,
pubkey::Pubkey,
@ -81,6 +83,7 @@ impl BankingStage {
verified_receiver: CrossbeamReceiver<Vec<Packets>>,
verified_vote_receiver: CrossbeamReceiver<Vec<Packets>>,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: ReplayVoteSender,
) -> Self {
Self::new_num_threads(
cluster_info,
@ -89,6 +92,7 @@ impl BankingStage {
verified_vote_receiver,
Self::num_threads(),
transaction_status_sender,
gossip_vote_sender,
)
}
@ -99,6 +103,7 @@ impl BankingStage {
verified_vote_receiver: CrossbeamReceiver<Vec<Packets>>,
num_threads: u32,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: ReplayVoteSender,
) -> Self {
let batch_limit = TOTAL_BUFFERED_PACKETS / ((num_threads - 1) as usize * PACKETS_PER_BATCH);
// Single thread to generate entries from many banks.
@ -119,6 +124,7 @@ impl BankingStage {
let cluster_info = cluster_info.clone();
let mut recv_start = Instant::now();
let transaction_status_sender = transaction_status_sender.clone();
let gossip_vote_sender = gossip_vote_sender.clone();
Builder::new()
.name("solana-banking-stage-tx".to_string())
.spawn(move || {
@ -132,7 +138,8 @@ impl BankingStage {
enable_forwarding,
i,
batch_limit,
transaction_status_sender.clone(),
transaction_status_sender,
gossip_vote_sender,
);
})
.unwrap()
@ -168,6 +175,7 @@ impl BankingStage {
buffered_packets: &mut Vec<PacketsAndOffsets>,
batch_limit: usize,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> UnprocessedPackets {
let mut unprocessed_packets = vec![];
let mut rebuffered_packets = 0;
@ -199,6 +207,7 @@ impl BankingStage {
&msgs,
unprocessed_indexes.to_owned(),
transaction_status_sender.clone(),
gossip_vote_sender,
);
new_tx_count += processed;
@ -283,6 +292,7 @@ impl BankingStage {
)
}
#[allow(clippy::too_many_arguments)]
fn process_buffered_packets(
my_pubkey: &Pubkey,
socket: &std::net::UdpSocket,
@ -292,6 +302,7 @@ impl BankingStage {
enable_forwarding: bool,
batch_limit: usize,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> BufferedPacketsDecision {
let (leader_at_slot_offset, poh_has_bank, would_be_leader) = {
let poh = poh_recorder.lock().unwrap();
@ -319,6 +330,7 @@ impl BankingStage {
buffered_packets,
batch_limit,
transaction_status_sender,
gossip_vote_sender,
);
buffered_packets.append(&mut unprocessed);
}
@ -352,6 +364,7 @@ impl BankingStage {
decision
}
#[allow(clippy::too_many_arguments)]
pub fn process_loop(
my_pubkey: Pubkey,
verified_receiver: &CrossbeamReceiver<Vec<Packets>>,
@ -362,6 +375,7 @@ impl BankingStage {
id: u32,
batch_limit: usize,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: ReplayVoteSender,
) {
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut buffered_packets = vec![];
@ -376,6 +390,7 @@ impl BankingStage {
enable_forwarding,
batch_limit,
transaction_status_sender.clone(),
&gossip_vote_sender,
);
if decision == BufferedPacketsDecision::Hold {
// If we are waiting on a new bank,
@ -403,6 +418,7 @@ impl BankingStage {
id,
batch_limit,
transaction_status_sender.clone(),
&gossip_vote_sender,
) {
Err(RecvTimeoutError::Timeout) => (),
Err(RecvTimeoutError::Disconnected) => break,
@ -501,6 +517,7 @@ impl BankingStage {
poh: &Arc<Mutex<PohRecorder>>,
batch: &TransactionBatch,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
let mut load_execute_time = Measure::start("load_execute_time");
// Use a shorter maximum age when adding transactions into the pipeline. This will reduce
@ -513,8 +530,20 @@ impl BankingStage {
} else {
vec![]
};
let (mut loaded_accounts, results, mut retryable_txs, tx_count, signature_count) =
bank.load_and_execute_transactions(batch, MAX_PROCESSING_AGE, None);
let (
mut loaded_accounts,
results,
inner_instructions,
transaction_logs,
mut retryable_txs,
tx_count,
signature_count,
) = bank.load_and_execute_transactions(
batch,
MAX_PROCESSING_AGE,
transaction_status_sender.is_some(),
transaction_status_sender.is_some(),
);
load_execute_time.stop();
let freeze_lock = bank.freeze_lock();
@ -533,25 +562,26 @@ impl BankingStage {
let num_to_commit = num_to_commit.unwrap();
if num_to_commit != 0 {
let transaction_statuses = bank
.commit_transactions(
let tx_results = bank.commit_transactions(
txs,
None,
&mut loaded_accounts,
&results,
tx_count,
signature_count,
)
.processing_results;
);
bank_utils::find_and_send_votes(txs, &tx_results, Some(gossip_vote_sender));
if let Some(sender) = transaction_status_sender {
let post_balances = bank.collect_balances(batch);
send_transaction_status_batch(
bank.clone(),
batch.transactions(),
batch.iteration_order_vec(),
transaction_statuses,
tx_results.processing_results,
TransactionBalancesSet::new(pre_balances, post_balances),
inner_instructions,
transaction_logs,
sender,
);
}
@ -578,6 +608,7 @@ impl BankingStage {
poh: &Arc<Mutex<PohRecorder>>,
chunk_offset: usize,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
let mut lock_time = Measure::start("lock_time");
// Once accounts are locked, other threads cannot encode transactions that will modify the
@ -590,6 +621,7 @@ impl BankingStage {
poh,
&batch,
transaction_status_sender,
gossip_vote_sender,
);
retryable_txs.iter_mut().for_each(|x| *x += chunk_offset);
@ -618,6 +650,7 @@ impl BankingStage {
transactions: &[Transaction],
poh: &Arc<Mutex<PohRecorder>>,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> (usize, Vec<usize>) {
let mut chunk_start = 0;
let mut unprocessed_txs = vec![];
@ -633,6 +666,7 @@ impl BankingStage {
poh,
chunk_start,
transaction_status_sender.clone(),
gossip_vote_sender,
);
trace!("process_transactions result: {:?}", result);
@ -704,6 +738,7 @@ impl BankingStage {
fn transactions_from_packets(
msgs: &Packets,
transaction_indexes: &[usize],
secp256k1_program_enabled: bool,
) -> (Vec<Transaction>, Vec<usize>) {
let packets = Packets::new(
transaction_indexes
@ -713,8 +748,26 @@ impl BankingStage {
);
let transactions = Self::deserialize_transactions(&packets);
let maybe_secp_verified_transactions: Vec<_> = if secp256k1_program_enabled {
transactions
.into_iter()
.map(|tx| {
if let Some(tx) = tx {
if tx.verify_precompiles().is_ok() {
Some(tx)
} else {
None
}
} else {
None
}
})
.collect()
} else {
transactions
};
Self::filter_transaction_indexes(transactions, &transaction_indexes)
Self::filter_transaction_indexes(maybe_secp_verified_transactions, &transaction_indexes)
}
/// This function filters pending packets that are still valid
@ -748,10 +801,7 @@ impl BankingStage {
&filter,
(MAX_PROCESSING_AGE)
.saturating_sub(max_tx_fwd_delay)
.saturating_sub(
(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET * bank.ticks_per_slot()
/ DEFAULT_TICKS_PER_SECOND) as usize,
),
.saturating_sub(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET as usize),
&mut error_counters,
);
@ -764,9 +814,13 @@ impl BankingStage {
msgs: &Packets,
packet_indexes: Vec<usize>,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> (usize, usize, Vec<usize>) {
let (transactions, transaction_to_packet_indexes) =
Self::transactions_from_packets(msgs, &packet_indexes);
let (transactions, transaction_to_packet_indexes) = Self::transactions_from_packets(
msgs,
&packet_indexes,
bank.secp256k1_program_enabled(),
);
debug!(
"bank: {} filtered transactions {}",
bank.slot(),
@ -775,8 +829,13 @@ impl BankingStage {
let tx_len = transactions.len();
let (processed, unprocessed_tx_indexes) =
Self::process_transactions(bank, &transactions, poh, transaction_status_sender);
let (processed, unprocessed_tx_indexes) = Self::process_transactions(
bank,
&transactions,
poh,
transaction_status_sender,
gossip_vote_sender,
);
let unprocessed_tx_count = unprocessed_tx_indexes.len();
@ -810,8 +869,11 @@ impl BankingStage {
}
}
let (transactions, transaction_to_packet_indexes) =
Self::transactions_from_packets(msgs, &transaction_indexes);
let (transactions, transaction_to_packet_indexes) = Self::transactions_from_packets(
msgs,
&transaction_indexes,
bank.secp256k1_program_enabled(),
);
let tx_count = transaction_to_packet_indexes.len();
@ -846,6 +908,7 @@ impl BankingStage {
.collect()
}
#[allow(clippy::too_many_arguments)]
/// Process the incoming packets
pub fn process_packets(
my_pubkey: &Pubkey,
@ -856,6 +919,7 @@ impl BankingStage {
id: u32,
batch_limit: usize,
transaction_status_sender: Option<TransactionStatusSender>,
gossip_vote_sender: &ReplayVoteSender,
) -> Result<UnprocessedPackets, RecvTimeoutError> {
let mut recv_time = Measure::start("process_packets_recv");
let mms = verified_receiver.recv_timeout(recv_timeout)?;
@ -898,6 +962,7 @@ impl BankingStage {
&msgs,
packet_indexes,
transaction_status_sender.clone(),
gossip_vote_sender,
);
new_tx_count += processed;
@ -1035,7 +1100,7 @@ mod tests {
system_transaction,
transaction::TransactionError,
};
use solana_transaction_status::{EncodedTransaction, TransactionWithStatusMeta};
use solana_transaction_status::TransactionWithStatusMeta;
use std::{sync::atomic::Ordering, thread::sleep};
#[test]
@ -1044,6 +1109,7 @@ mod tests {
let bank = Arc::new(Bank::new(&genesis_config));
let (verified_sender, verified_receiver) = unbounded();
let (vote_sender, vote_receiver) = unbounded();
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let ledger_path = get_tmp_ledger_path!();
{
let blockstore = Arc::new(
@ -1060,6 +1126,7 @@ mod tests {
verified_receiver,
vote_receiver,
None,
gossip_vote_sender,
);
drop(verified_sender);
drop(vote_sender);
@ -1094,12 +1161,15 @@ mod tests {
create_test_recorder(&bank, &blockstore, Some(poh_config));
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
verified_receiver,
vote_receiver,
None,
gossip_vote_sender,
);
trace!("sending bank");
drop(verified_sender);
@ -1157,12 +1227,15 @@ mod tests {
create_test_recorder(&bank, &blockstore, Some(poh_config));
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
verified_receiver,
vote_receiver,
None,
gossip_vote_sender,
);
// fund another account so we can send 2 good transactions in a single batch.
@ -1172,16 +1245,16 @@ mod tests {
bank.process_transaction(&fund_tx).unwrap();
// good tx
let to = Pubkey::new_rand();
let to = solana_sdk::pubkey::new_rand();
let tx = system_transaction::transfer(&mint_keypair, &to, 1, start_hash);
// good tx, but no verify
let to2 = Pubkey::new_rand();
let to2 = solana_sdk::pubkey::new_rand();
let tx_no_ver = system_transaction::transfer(&keypair, &to2, 2, start_hash);
// bad tx, AccountNotFound
let keypair = Keypair::new();
let to3 = Pubkey::new_rand();
let to3 = solana_sdk::pubkey::new_rand();
let tx_anf = system_transaction::transfer(&keypair, &to3, 1, start_hash);
// send 'em over
@ -1283,6 +1356,8 @@ mod tests {
let (vote_sender, vote_receiver) = unbounded();
let ledger_path = get_tmp_ledger_path!();
{
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let entry_receiver = {
// start a banking_stage to eat verified receiver
let bank = Arc::new(Bank::new(&genesis_config));
@ -1305,6 +1380,7 @@ mod tests {
vote_receiver,
2,
None,
gossip_vote_sender,
);
// wait for banking_stage to eat the packets
@ -1371,9 +1447,9 @@ mod tests {
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
poh_recorder.lock().unwrap().set_working_bank(working_bank);
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let keypair2 = Keypair::new();
let pubkey2 = Pubkey::new_rand();
let pubkey2 = solana_sdk::pubkey::new_rand();
let transactions = vec![
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
@ -1451,7 +1527,7 @@ mod tests {
mint_keypair,
..
} = create_genesis_config(10_000);
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let transactions = vec![
None,
@ -1532,7 +1608,7 @@ mod tests {
mint_keypair,
..
} = create_genesis_config(10_000);
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let transactions = vec![
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
@ -1603,8 +1679,8 @@ mod tests {
#[test]
fn test_should_process_or_forward_packets() {
let my_pubkey = Pubkey::new_rand();
let my_pubkey1 = Pubkey::new_rand();
let my_pubkey = solana_sdk::pubkey::new_rand();
let my_pubkey1 = solana_sdk::pubkey::new_rand();
assert_eq!(
BankingStage::consume_or_forward_packets(&my_pubkey, None, true, false,),
@ -1650,7 +1726,7 @@ mod tests {
..
} = create_genesis_config(10_000);
let bank = Arc::new(Bank::new(&genesis_config));
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let transactions = vec![system_transaction::transfer(
&mint_keypair,
@ -1682,6 +1758,7 @@ mod tests {
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
poh_recorder.lock().unwrap().set_working_bank(working_bank);
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
BankingStage::process_and_record_transactions(
&bank,
@ -1689,6 +1766,7 @@ mod tests {
&poh_recorder,
0,
None,
&gossip_vote_sender,
)
.0
.unwrap();
@ -1725,6 +1803,7 @@ mod tests {
&poh_recorder,
0,
None,
&gossip_vote_sender,
)
.0,
Err(PohRecorderError::MaxHeightReached)
@ -1744,8 +1823,8 @@ mod tests {
..
} = create_genesis_config(10_000);
let bank = Arc::new(Bank::new(&genesis_config));
let pubkey = Pubkey::new_rand();
let pubkey1 = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let pubkey1 = solana_sdk::pubkey::new_rand();
let transactions = vec![
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
@ -1776,12 +1855,15 @@ mod tests {
poh_recorder.lock().unwrap().set_working_bank(working_bank);
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let (result, unprocessed) = BankingStage::process_and_record_transactions(
&bank,
&transactions,
&poh_recorder,
0,
None,
&gossip_vote_sender,
);
assert!(result.is_ok());
@ -1837,7 +1919,7 @@ mod tests {
} = create_genesis_config(10_000);
let bank = Arc::new(Bank::new(&genesis_config));
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let transactions =
vec![
@ -1855,7 +1937,7 @@ mod tests {
bank.slot(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::new_rand(),
&solana_sdk::pubkey::new_rand(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(PohConfig::default()),
@ -1865,8 +1947,16 @@ mod tests {
// record
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let (processed_transactions_count, mut retryable_txs) =
BankingStage::process_transactions(&bank, &transactions, &poh_recorder, None);
BankingStage::process_transactions(
&bank,
&transactions,
&poh_recorder,
None,
&gossip_vote_sender,
);
assert_eq!(processed_transactions_count, 0,);
@ -1887,8 +1977,8 @@ mod tests {
..
} = create_genesis_config(10_000);
let bank = Arc::new(Bank::new(&genesis_config));
let pubkey = Pubkey::new_rand();
let pubkey1 = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let pubkey1 = solana_sdk::pubkey::new_rand();
let keypair1 = Keypair::new();
let success_tx =
@ -1943,36 +2033,29 @@ mod tests {
&Arc::new(AtomicBool::new(false)),
);
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let _ = BankingStage::process_and_record_transactions(
&bank,
&transactions,
&poh_recorder,
0,
Some(transaction_status_sender),
&gossip_vote_sender,
);
transaction_status_service.join().unwrap();
let confirmed_block = blockstore.get_confirmed_block(bank.slot(), None).unwrap();
let confirmed_block = blockstore.get_confirmed_block(bank.slot()).unwrap();
assert_eq!(confirmed_block.transactions.len(), 3);
for TransactionWithStatusMeta { transaction, meta } in
confirmed_block.transactions.into_iter()
{
if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == success_signature.to_string() {
if transaction.signatures[0] == success_signature {
assert_eq!(meta.unwrap().status, Ok(()));
} else if transaction.signatures[0] == ix_error_signature {
let meta = meta.unwrap();
assert_eq!(meta.err, None);
assert_eq!(meta.status, Ok(()));
} else if transaction.signatures[0] == ix_error_signature.to_string() {
let meta = meta.unwrap();
assert_eq!(
meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::Custom(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
@ -1985,7 +2068,6 @@ mod tests {
}
}
}
}
Blockstore::destroy(&ledger_path).unwrap();
}
}

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