Compare commits

...

350 Commits

Author SHA1 Message Date
Trent Nelson
c48298128d Remove spl-token-cli from 1.3.22 build 2020-11-25 01:18:50 -07:00
sakridge
db3f154b3f Fix nonces (#13800)
Co-authored-by: Trent Nelson <trent@solana.com>
2020-11-24 23:54:02 -08:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
1f693c5925 audit: Ignore RUSTSEC-2020-0071, potential SEGV in time 2020-11-18 22:30:28 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
ea2daf0cfa Bump version to 1.3.22 2020-11-13 18:03:30 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
sakridge
b9555116ec Increment version to v1.3.21 (#13528) 2020-11-11 19:30:46 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Justin Starry
27733bb4d7 Bump low end validator RAM requirement (#13406) 2020-11-05 15:24:26 +08:00
Trent Nelson
0b9c87b6ec Docs: Clarify validator disk requirements 2020-11-05 15:24:26 +08:00
sakridge
df1e62f23f Update docs to latest processors (#11613) 2020-11-05 15:24:26 +08:00
mergify[bot]
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
Jack May
64c76c2f4b Revert "check sysvar id for AccountInfo (#13175) (#13351)"
This reverts commit 290d514051.
2020-11-02 12:14:33 -08:00
mergify[bot]
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
mergify[bot]
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
Jack May
4513128c75 check sysvar id for AccountInfo (#13175) 2020-11-01 06:58:02 +00:00
Michael Vines
d01968ed21 cargo update -p futures-task / cargo update -p futures-util 2020-10-31 19:00:22 +00:00
Michael Vines
f9ac24d1f2 Switch to dirs-next 2020-10-31 19:00:22 +00:00
Michael Vines
fe5a09b50a Ignore stdweb 2020-10-31 19:00:22 +00:00
mergify[bot]
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
Jack May
74bfb00959 Update AccountInfo comments (#13302)
(cherry picked from commit 72d41e5801)
2020-10-30 08:09:42 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
884d68ddcf Add Pubkey::new_unique()/Hash::new_unique() 2020-10-23 03:19:38 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
bff820d549 Remove unused pubkey::Pubkey imports 2020-10-21 20:27:57 -07:00
Michael Vines
5ad0ccdfe1 cargo fmt 2020-10-21 20:27:57 -07:00
Michael Vines
d8c7d06737 Run codemod --extensions rs Hash::new_rand solana_sdk:#️⃣:new_rand 2020-10-21 20:27:57 -07:00
Michael Vines
422bb3c526 Run codemod --extensions rs Pubkey::new_rand solana_sdk::pubkey::new_rand 2020-10-21 20:27:57 -07:00
Michael Vines
048a2b982c Add pubkey_new_rand(), mark Pubkey::new_rand() deprecated 2020-10-21 20:27:57 -07:00
Michael Vines
76f0557462 Add hash_new_rand(), mark Hash::new_rand() as deprecated 2020-10-21 20:27:57 -07:00
Trent Nelson
6ebb933302 Bump version to 1.3.20 2020-10-22 00:08:28 +00:00
mergify[bot]
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
mergify[bot]
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
Ryo Onodera
57d8d0ab6e Add ledger-tool dead-slots and improve purge a lot (#13070) 2020-10-21 18:38:37 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
a0e2f49263 Remove unsupported metrics tarball from release artifacts
(cherry picked from commit 62f20bc170)
2020-10-20 13:14:46 -07:00
mergify[bot]
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
Tyera Eulberg
840ea326db Remove errant print 2020-10-20 08:58:54 -06:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Tyera Eulberg
7674a5fea8 Bump version to v1.3.19 (#12986) 2020-10-19 19:13:28 +00:00
Tyera Eulberg
78be777b65 Revert "CLI: Put deploy ephemeral keypair behind a flag (#12941)" (#12981)
This reverts commit c2806aa2f9.
2020-10-19 17:31:33 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
d1c51950f3 Bump version to v1.3.18 2020-10-13 03:57:13 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
d35d853e57 Cargo.lock 2020-10-12 04:21:26 +00:00
Michael Vines
03317f6bf7 Switch to tempfile 2020-10-12 04:21:26 +00:00
Michael Vines
4f0e928a0a Rework cargo audit ignores 2020-10-12 04:21:26 +00:00
mergify[bot]
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
mergify[bot]
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
Michael Vines
b13385fba6 Update gossip entrypoints 2020-10-10 08:39:54 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
a70aa28832 Bump version to 1.3.17 2020-10-10 03:22:14 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Dan Albert
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
mergify[bot]
bded162ed8 fix conflicts (#12733)
Co-authored-by: Jack May <jack@solana.com>
2020-10-08 20:40:59 +00:00
mergify[bot]
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
mergify[bot]
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
Michael Vines
fb0f0adb16 Display vote/stake account epoch rewards 2020-10-08 08:34:40 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Dan Albert
1b53f8b33d Update channel_restriction.sh 2020-10-04 10:28:12 -06:00
mergify[bot]
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
Michael Vines
bc1b95d6d0 Bump version to v1.3.16 2020-10-03 04:24:03 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
e69ee1ec64 Add GetConfirmedBlocksWithLimit RPC method
(cherry picked from commit 75b621160e)
2020-10-02 08:21:08 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
ea990fd259 Update devnet genesis hash 2020-09-30 11:37:51 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Tyera Eulberg
0965389f41 Enable json output from solana feature status (#12554) (#12559) 2020-09-29 21:00:20 +00:00
Trent Nelson
24c60cf3db Bump version to v1.3.15 2020-09-29 20:57:08 +00:00
Michael Vines
c8f4bfca90 Notify but don't abort on unexpected stake account balance 2020-09-29 11:39:11 -07:00
Michael Vines
4a6b65ce53 Switch get_program_accounts to use base64 2020-09-29 18:21:19 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
3781ad259b clap-utils: Allow nonce/offline args to be global (bp #12538) 2020-09-29 04:51:33 +00:00
Trent Nelson
5ad5f8b458 cli-output: Add a path to handling --verbose and --quiet display (bp #12531) 2020-09-29 04:44:59 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
4bd29c1b32 Add pico-inflation feature
(cherry picked from commit aa5c008fa8)
2020-09-28 09:34:35 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
6a698af235 Deerror 2020-09-25 22:19:09 -07:00
Michael Vines
7ec38bd71c Improve 'Failed to create snapshot archive' warning message
(cherry picked from commit 5dcf348098)
2020-09-25 21:06:05 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
b65a764593 Bump jsonrpc crates to 15.0.0 (bp #12491) 2020-09-25 19:49:10 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Tyera Eulberg
ec4938a9f3 Bump version to 1.3.14 (#12444) 2020-09-24 07:42:54 +00:00
Tyera Eulberg
41b45ca281 Allow publishing of secp256k1 program 2020-09-24 00:05:11 -06:00
Trent Nelson
838aaee144 CLI: Factor out offline helpers (bp #12382) 2020-09-24 04:41:30 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Tyera Eulberg
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
486b657fca validator: Add --require-tower stub arg 2020-09-23 05:01:12 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
bc774e3ea6 Bump version to v1.3.13 2020-09-20 05:40:32 +00:00
Michael Vines
64af712723 Use validator_config for RPC address instead of cluster_info for port verification checks 2020-09-20 02:27:35 +00:00
Michael Vines
ea2611f3a9 Document that testnet has a faucet 2020-09-19 17:41:13 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Trent Nelson
86554c4945 Docs: Add SPL Token exchange integration (bp #12303) 2020-09-19 00:37:25 +00:00
Tyera Eulberg
95ed3641c6 Add blocktime column to blockstore (#12336) 2020-09-18 21:42:45 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Justin Starry
37175d0cdf Fix off-by-one max payload checks
(cherry picked from commit f6cda2579f)
2020-09-16 17:05:09 -07:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Ryo Onodera
7267257073 Bump version to v1.3.12 (#12249) 2020-09-15 20:15:03 +00:00
mergify[bot]
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
mergify[bot]
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
Ryo Onodera
52ee9155b5 Bump version to v1.3.11 (#12238) 2020-09-15 10:25:22 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Michael Vines
415c80c204 Bump version to v1.3.10 2020-09-09 01:29:39 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Ryo Onodera
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
mergify[bot]
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
Justin Starry
58312655b4 Fix signature subscription panic (#12077) (#12092) 2020-09-07 10:31:25 +00:00
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
mergify[bot]
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
Josh
bd76810623 Bump version to 1.3.9 (#12034) 2020-09-03 16:55:24 -07:00
450 changed files with 33423 additions and 10430 deletions

View File

@@ -31,4 +31,9 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
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

@@ -124,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

1033
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@ members = [
"banks-server",
"clap-utils",
"cli-config",
"cli-output",
"client",
"core",
"dos",
@@ -18,7 +19,6 @@ members = [
"perf",
"validator",
"genesis",
"genesis-programs",
"gossip",
"install",
"keygen",
@@ -36,6 +36,7 @@ members = [
"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.8"
version = "1.3.22"
description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -18,11 +18,11 @@ lazy_static = "1.4.0"
serde = "1.0.112"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-config-program = { path = "../programs/config", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-stake-program = { path = "../programs/stake", version = "1.3.8" }
solana-vote-program = { path = "../programs/vote", version = "1.3.8" }
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
solana-config-program = { path = "../programs/config", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-stake-program = { path = "../programs/stake", version = "1.3.22" }
solana-vote-program = { path = "../programs/vote", version = "1.3.22" }
spl-token-v2-0 = { package = "spl-token", version = "=2.0.6", features = ["skip-no-mangle"] }
thiserror = "1.0"
[package.metadata.docs.rs]

View File

@@ -111,8 +111,8 @@ mod test {
#[test]
fn test_parse_account_data() {
let account_pubkey = Pubkey::new_rand();
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(&account_pubkey, &other_program, &data, None).is_err());

View File

@@ -117,7 +117,7 @@ mod test {
}))
.unwrap(),
};
let info_pubkey = Pubkey::new_rand();
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,

View File

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

View File

@@ -319,7 +319,7 @@ mod test {
}]),
);
let bad_pubkey = Pubkey::new_rand();
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];

View File

@@ -4,9 +4,7 @@ use crate::{
};
use solana_sdk::pubkey::Pubkey;
use spl_token_v2_0::{
option::COption,
pack::Pack,
solana_sdk::pubkey::Pubkey as SplTokenPubkey,
solana_sdk::{program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey},
state::{Account, AccountState, Mint, Multisig},
};
use std::str::FromStr;
@@ -156,6 +154,31 @@ pub struct UiTokenAmount {
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;
@@ -202,16 +225,14 @@ mod test {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
let mut account_data = vec![0; Account::get_packed_len()];
Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
account.mint = mint_pubkey;
account.owner = owner_pubkey;
account.amount = 42;
account.state = AccountState::Initialized;
account.is_native = COption::None;
account.close_authority = COption::Some(owner_pubkey);
Ok(())
})
.unwrap();
let mut account = Account::unpack_unchecked(&account_data).unwrap();
account.mint = mint_pubkey;
account.owner = owner_pubkey;
account.amount = 42;
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!(
@@ -234,15 +255,13 @@ mod test {
);
let mut mint_data = vec![0; Mint::get_packed_len()];
Mint::unpack_unchecked_mut(&mut mint_data, &mut |mint: &mut Mint| {
mint.mint_authority = COption::Some(owner_pubkey);
mint.supply = 42;
mint.decimals = 3;
mint.is_initialized = true;
mint.freeze_authority = COption::Some(owner_pubkey);
Ok(())
})
.unwrap();
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, None).unwrap(),
@@ -263,14 +282,13 @@ mod test {
signers[0] = signer1;
signers[1] = signer2;
signers[2] = signer3;
Multisig::unpack_unchecked_mut(&mut multisig_data, &mut |multisig: &mut Multisig| {
multisig.m = 2;
multisig.n = 3;
multisig.is_initialized = true;
multisig.signers = signers;
Ok(())
})
.unwrap();
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, None).unwrap(),
TokenAccountType::Multisig(UiMultisig {
@@ -293,11 +311,9 @@ mod test {
fn test_get_token_account_mint() {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let mut account_data = vec![0; Account::get_packed_len()];
Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
account.mint = mint_pubkey;
Ok(())
})
.unwrap();
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!(
@@ -305,4 +321,20 @@ mod test {
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

@@ -2,18 +2,20 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-accounts-bench"
version = "1.3.8"
version = "1.3.22"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
publish = false
[dependencies]
log = "0.4.6"
rayon = "1.4.0"
solana-logger = { path = "../logger", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.8" }
solana-measure = { path = "../measure", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-measure = { path = "../measure", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
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::{genesis_config::OperatingMode, 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], &OperatingMode::Preview);
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,10 +2,11 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-banking-bench"
version = "1.3.8"
version = "1.3.22"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
publish = false
[dependencies]
clap = "2.33.1"
@@ -13,16 +14,16 @@ crossbeam-channel = "0.4"
log = "0.4.6"
rand = "0.7.0"
rayon = "1.4.0"
solana-core = { path = "../core", version = "1.3.8" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.8" }
solana-streamer = { path = "../streamer", version = "1.3.8" }
solana-perf = { path = "../perf", version = "1.3.8" }
solana-ledger = { path = "../ledger", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.8" }
solana-measure = { path = "../measure", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-version = { path = "../version", version = "1.3.8" }
solana-core = { path = "../core", version = "1.3.22" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-streamer = { path = "../streamer", version = "1.3.22" }
solana-perf = { path = "../perf", version = "1.3.22" }
solana-ledger = { path = "../ledger", version = "1.3.22" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-measure = { path = "../measure", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
[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
})
@@ -241,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,

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-client"
version = "1.3.8"
version = "1.3.22"
description = "Solana banks client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,15 +12,15 @@ edition = "2018"
async-trait = "0.1.36"
bincode = "1.3.1"
futures = "0.3"
solana-banks-interface = { path = "../banks-interface", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-banks-interface = { path = "../banks-interface", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
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.8" }
solana-banks-server = { path = "../banks-server", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-banks-server = { path = "../banks-server", version = "1.3.22" }
[lib]
crate-type = ["lib"]

View File

@@ -71,6 +71,14 @@ pub trait BanksClientExt {
/// 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>>;
@@ -130,13 +138,18 @@ impl BanksClientExt for BanksClient {
.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_and_context(
context::current(),
address,
CommitmentLevel::default(),
)
.await
self.get_account_with_commitment(address, CommitmentLevel::default())
.await
}
async fn get_balance_with_commitment(
@@ -200,7 +213,7 @@ 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, pubkey::Pubkey, signature::Signer, system_instruction};
use solana_sdk::{message::Message, signature::Signer, system_instruction};
use std::sync::{Arc, RwLock};
use tarpc::transport;
use tokio::{runtime::Runtime, time::delay_for};
@@ -222,7 +235,7 @@ mod tests {
&genesis.genesis_config,
))));
let bob_pubkey = Pubkey::new_rand();
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));
@@ -252,7 +265,7 @@ mod tests {
))));
let mint_pubkey = &genesis.mint_keypair.pubkey();
let bob_pubkey = Pubkey::new_rand();
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));

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-interface"
version = "1.3.8"
version = "1.3.22"
description = "Solana banks RPC interface"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,7 +10,7 @@ edition = "2018"
[dependencies]
serde = { version = "1.0.112", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
tarpc = { version = "0.21.0", features = ["full"] }
[lib]

View File

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

View File

@@ -1,3 +1,4 @@
use crate::send_transaction_service::{SendTransactionService, TransactionInfo};
use bincode::{deserialize, serialize};
use futures::{
future,
@@ -8,7 +9,6 @@ use solana_runtime::{
bank::Bank,
bank_forks::BankForks,
commitment::{BlockCommitmentCache, CommitmentSlots},
send_transaction_service::{SendTransactionService, TransactionInfo},
};
use solana_sdk::{
account::Account,
@@ -23,9 +23,8 @@ use solana_sdk::{
use std::{
collections::HashMap,
io,
net::SocketAddr,
net::{Ipv4Addr, SocketAddr},
sync::{
atomic::AtomicBool,
mpsc::{channel, Receiver, Sender},
Arc, RwLock,
},
@@ -245,19 +244,18 @@ pub async fn start_tcp_server(
.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().unwrap().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();
let exit_send_transaction_service = Arc::new(AtomicBool::new(false));
SendTransactionService::new(
tpu_addr,
&bank_forks,
&exit_send_transaction_service,
receiver,
);
SendTransactionService::new(tpu_addr, &bank_forks, receiver);
let server =
BanksServer::new(bank_forks.clone(), block_commitment_cache.clone(), sender);

View File

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

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},
@@ -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,20 +68,20 @@ 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)) {
Self::send_transaction(
&send_socket,
&tpu_address,
&transaction_info.wire_transaction,
);
if transactions.len() < MAX_TRANSACTION_QUEUE_SIZE {
transactions.insert(transaction_info.signature, transaction_info);
} else {
datapoint_warn!("send_transaction_service-queue-overflow");
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,
&transaction_info.wire_transaction,
);
if transactions.len() < MAX_TRANSACTION_QUEUE_SIZE {
transactions.insert(transaction_info.signature, transaction_info);
} else {
datapoint_warn!("send_transaction_service-queue-overflow");
}
}
}
@@ -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.8"
version = "1.3.22"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -18,21 +18,21 @@ rand = "0.7.0"
rayon = "1.4.0"
serde_json = "1.0.56"
serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "1.3.8" }
solana-core = { path = "../core", version = "1.3.8" }
solana-genesis = { path = "../genesis", version = "1.3.8" }
solana-client = { path = "../client", version = "1.3.8" }
solana-faucet = { path = "../faucet", version = "1.3.8" }
solana-exchange-program = { path = "../programs/exchange", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.8" }
solana-metrics = { path = "../metrics", version = "1.3.8" }
solana-net-utils = { path = "../net-utils", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-version = { path = "../version", version = "1.3.8" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-core = { path = "../core", version = "1.3.22" }
solana-genesis = { path = "../genesis", version = "1.3.22" }
solana-client = { path = "../client", version = "1.3.22" }
solana-faucet = { path = "../faucet", version = "1.3.22" }
solana-exchange-program = { path = "../programs/exchange", version = "1.3.22" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-metrics = { path = "../metrics", version = "1.3.22" }
solana-net-utils = { path = "../net-utils", version = "1.3.22" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
[dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.3.8" }
solana-local-cluster = { path = "../local-cluster", version = "1.3.22" }
[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.8"
version = "1.3.22"
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.8" }
solana-streamer = { path = "../streamer", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.8" }
solana-net-utils = { path = "../net-utils", version = "1.3.8" }
solana-version = { path = "../version", version = "1.3.8" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-streamer = { path = "../streamer", version = "1.3.22" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-net-utils = { path = "../net-utils", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
[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,10 +2,11 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-bench-tps"
version = "1.3.8"
version = "1.3.22"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
publish = false
[dependencies]
bincode = "1.3.1"
@@ -14,23 +15,23 @@ log = "0.4.8"
rayon = "1.4.0"
serde_json = "1.0.56"
serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "1.3.8" }
solana-core = { path = "../core", version = "1.3.8" }
solana-genesis = { path = "../genesis", version = "1.3.8" }
solana-client = { path = "../client", version = "1.3.8" }
solana-faucet = { path = "../faucet", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.8" }
solana-metrics = { path = "../metrics", version = "1.3.8" }
solana-measure = { path = "../measure", version = "1.3.8" }
solana-net-utils = { path = "../net-utils", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-version = { path = "../version", version = "1.3.8" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-core = { path = "../core", version = "1.3.22" }
solana-genesis = { path = "../genesis", version = "1.3.22" }
solana-client = { path = "../client", version = "1.3.22" }
solana-faucet = { path = "../faucet", version = "1.3.22" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-metrics = { path = "../metrics", version = "1.3.22" }
solana-measure = { path = "../measure", version = "1.3.22" }
solana-net-utils = { path = "../net-utils", version = "1.3.22" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
[dev-dependencies]
serial_test = "0.4.0"
serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.3.8" }
solana-local-cluster = { path = "../local-cluster", version = "1.3.22" }
[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

@@ -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

@@ -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
@@ -56,7 +58,29 @@ _ 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
)
_ 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.8"
version = "1.3.22"
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.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
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.8"
version = "1.3.22"
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()
};
@@ -82,8 +82,8 @@ impl Config {
return "".to_string();
}
let mut url = json_rpc_url.unwrap();
let port = url.port_or_known_default().unwrap_or(80);
url.set_port(Some(port + 2)).expect("unable to set port");
let port = url.port().unwrap_or(8899);
url.set_port(Some(port + 3)).expect("unable to set port");
url.to_string()
}
@@ -138,21 +138,21 @@ mod test {
fn compute_rpc_banks_url() {
assert_eq!(
Config::compute_rpc_banks_url(&"http://devnet.solana.com"),
"http://devnet.solana.com:82/".to_string()
"http://devnet.solana.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
"https://devnet.solana.com:445/".to_string()
"https://devnet.solana.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"http://example.com:8899"),
"http://example.com:8901/".to_string()
"http://example.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://example.com:1234"),
"https://example.com:1236/".to_string()
"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.22"
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.22" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-client = { path = "../client", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-stake-program = { path = "../programs/stake", version = "1.3.22" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.22" }
solana-vote-program = { path = "../programs/vote", version = "1.3.22" }
[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.8"
version = "1.3.22"
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.8" }
solana-budget-program = { path = "../programs/budget", version = "1.3.8" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.8" }
solana-cli-config = { path = "../cli-config", version = "1.3.8" }
solana-client = { path = "../client", version = "1.3.8" }
solana-config-program = { path = "../programs/config", version = "1.3.8" }
solana-faucet = { path = "../faucet", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.8" }
solana-net-utils = { path = "../net-utils", version = "1.3.8" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-stake-program = { path = "../programs/stake", version = "1.3.8" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.8" }
solana-version = { path = "../version", version = "1.3.8" }
solana-vote-program = { path = "../programs/vote", version = "1.3.8" }
solana-vote-signer = { path = "../vote-signer", version = "1.3.8" }
solana-account-decoder = { path = "../account-decoder", version = "1.3.22" }
solana-budget-program = { path = "../programs/budget", version = "1.3.22" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.22" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-cli-config = { path = "../cli-config", version = "1.3.22" }
solana-cli-output = { path = "../cli-output", version = "1.3.22" }
solana-client = { path = "../client", version = "1.3.22" }
solana-config-program = { path = "../programs/config", version = "1.3.22" }
solana-faucet = { path = "../faucet", version = "1.3.22" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-net-utils = { path = "../net-utils", version = "1.3.22" }
solana_rbpf = "=0.1.32"
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.22" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-stake-program = { path = "../programs/stake", version = "1.3.22" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
solana-vote-program = { path = "../programs/vote", version = "1.3.22" }
solana-vote-signer = { path = "../vote-signer", version = "1.3.22" }
thiserror = "1.0.20"
tiny-bip39 = "0.7.0"
url = "2.1.1"
[dev-dependencies]
solana-core = { path = "../core", version = "1.3.8" }
solana-budget-program = { path = "../programs/budget", version = "1.3.8" }
solana-core = { path = "../core", version = "1.3.22" }
solana-budget-program = { path = "../programs/budget", version = "1.3.22" }
tempfile = "3.1.0"
[[bin]]

View File

@@ -54,12 +54,42 @@ 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)
.map_err(Into::<ClientError>::into)?
if !check_account_for_balance_with_commitment(
rpc_client,
account_pubkey,
balance + fee,
commitment,
)
.map_err(Into::<ClientError>::into)?
{
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
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,18 +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},
client_error::ClientErrorKind,
pubsub_client::PubsubClient,
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
rpc_config::{
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
RpcProgramAccountsConfig,
},
rpc_filter,
rpc_response::SlotInfo,
};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
@@ -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")
@@ -282,6 +311,12 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.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"),
)
)
}
}
@@ -305,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);
@@ -323,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![],
})
}
@@ -453,6 +491,7 @@ pub fn parse_transaction_history(
None => None,
};
let limit = value_t_or_exit!(matches, "limit", usize);
let show_transactions = matches.is_present("show_transactions");
Ok(CliCommandInfo {
command: CliCommand::TransactionHistory {
@@ -460,6 +499,7 @@ pub fn parse_transaction_history(
before,
until,
limit,
show_transactions,
},
signers: vec![],
})
@@ -502,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: {:?}",
@@ -585,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 {
@@ -602,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;
@@ -637,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,
@@ -1033,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();
@@ -1128,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()),
)
})
@@ -1143,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"),
@@ -1164,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 {
@@ -1190,6 +1367,7 @@ pub fn process_show_stakes(
use_lamports_unit,
&stake_history,
&clock,
stake_program_v2_enabled,
),
});
}
@@ -1208,6 +1386,7 @@ pub fn process_show_stakes(
use_lamports_unit,
&stake_history,
&clock,
stake_program_v2_enabled,
),
});
}
@@ -1247,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));
@@ -1306,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,
@@ -1322,6 +1501,7 @@ pub fn process_transaction_history(
before: Option<Signature>,
until: Option<Signature>,
limit: usize,
show_transactions: bool,
) -> ProcessResult {
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
address,
@@ -1349,6 +1529,28 @@ pub fn process_transaction_history(
} else {
println!("{}", result.signature);
}
if show_transactions {
if let Ok(signature) = result.signature.parse::<Signature>() {
match rpc_client
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
{
Ok(confirmed_transaction) => {
println_transaction(
&confirmed_transaction
.transaction
.transaction
.decode()
.expect("Successful decode"),
&confirmed_transaction.transaction.meta,
" ",
);
}
Err(err) => println!(" Unable to get confirmed transaction details: {}", err),
}
}
println!();
}
}
Ok(transactions_found)
}
@@ -1371,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![],
@@ -1387,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![],
@@ -1396,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![],
@@ -1409,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![],
@@ -1420,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![],
@@ -1431,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![],
@@ -1442,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![],
@@ -1451,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![],
@@ -1462,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![],
@@ -1473,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![],
@@ -1493,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,
DEFAULT_RPC_TIMEOUT_SECONDS,
},
cli_output::OutputFormat,
display::{println_name_value, println_name_value_or},
use solana_cli::cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
DEFAULT_RPC_TIMEOUT_SECONDS,
};
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,6 +1,5 @@
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;
@@ -13,8 +12,9 @@ use solana_account_decoder::validator_info::{
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;
@@ -212,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");
@@ -224,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)?],
})
}
@@ -271,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(&(validator_info::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()
@@ -385,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(&(validator_info::id(), false))
match deserialize::<ConfigKeys>(&validator_info_account.data) {
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
Err(_) => false,
}
})
.collect()
};
@@ -489,7 +486,7 @@ mod tests {
#[test]
fn test_parse_validator_info() {
let pubkey = Pubkey::new_rand();
let pubkey = solana_sdk::pubkey::new_rand();
let keys = vec![(validator_info::id(), false), (pubkey, true)];
let config = ConfigKeys { keys };

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();
@@ -64,6 +64,7 @@ fn test_cli_deploy_program() {
program_location: pathbuf.to_str().unwrap().to_string(),
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
};
let response = process_command(&config);
@@ -98,6 +99,7 @@ fn test_cli_deploy_program() {
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
@@ -113,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,11 +298,14 @@ 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))
.unwrap()
.blockhash;
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;
// Test by creating transfer TX with nonce, fully offline
let mut authority_config = CliConfig::recent_for_tests();

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.8"
version = "1.3.22"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -9,30 +9,35 @@ 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.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.8" }
solana-net-utils = { path = "../net-utils", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.8" }
solana-vote-program = { path = "../programs/vote", version = "1.3.8" }
solana-account-decoder = { path = "../account-decoder", version = "1.3.22" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-net-utils = { path = "../net-utils", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
solana-vote-program = { path = "../programs/vote", version = "1.3.22" }
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.8" }
jsonrpc-core = "15.0.0"
jsonrpc-http-server = "15.0.0"
solana-logger = { path = "../logger", version = "1.3.22" }
[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();

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()
))
.into());
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()),
};
}
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,13 +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::{
commitment_config::CommitmentConfig, signature::Signature, transaction::TransactionError,
};
use solana_sdk::signature::Signature;
use std::{
marker::PhantomData,
sync::{
@@ -21,7 +20,7 @@ use thiserror::Error;
use tungstenite::{client::AutoStream, connect, Message, WebSocket};
use url::{ParseError, Url};
type PubsubSignatureResponse = PubsubClientSubscription<RpcResponse<SignatureResult>>;
type PubsubSignatureResponse = PubsubClientSubscription<RpcResponse<RpcSignatureResult>>;
#[derive(Debug, Error)]
pub enum PubsubClientError {
@@ -38,45 +37,6 @@ pub enum PubsubClientError {
UnexpectedMessageError,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "result")]
pub enum SignatureResult {
ProcessedSignatureResult(ProcessedSignatureResult),
ReceivedSignature,
}
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub struct RpcResponseContext {
pub slot: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProcessedSignatureResult {
pub err: Option<TransactionError>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RpcResponse<T> {
pub context: RpcResponseContext,
pub value: T,
}
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub struct SlotInfoMessage {
pub parent: u64,
pub root: u64,
pub slot: u64,
}
#[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>,
}
pub struct PubsubClientSubscription<T>
where
T: DeserializeOwned,
@@ -186,22 +146,16 @@ 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,
json!({
"jsonrpc":"2.0","id":1,"method":format!("{}Subscribe", SLOT_OPERATION),"params":[]
@@ -216,11 +170,11 @@ 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.clone()) {
match sender.send(msg) {
Ok(_) => (),
Err(err) => {
info!("receive error: {:?}", err);
@@ -236,7 +190,7 @@ impl PubsubClient {
info!("websocket - exited receive loop");
});
let result: PubsubClientSubscription<SlotInfoMessage> = PubsubClientSubscription {
let result: PubsubClientSubscription<SlotInfo> = PubsubClientSubscription {
message_type: PhantomData,
operation: SLOT_OPERATION,
socket,
@@ -254,13 +208,13 @@ impl PubsubClient {
) -> Result<
(
PubsubSignatureResponse,
Receiver<RpcResponse<SignatureResult>>,
Receiver<RpcResponse<RpcSignatureResult>>,
),
PubsubClientError,
> {
let url = Url::parse(url)?;
let (socket, _response) = connect(url)?;
let (sender, receiver) = channel::<RpcResponse<SignatureResult>>();
let (sender, receiver) = channel::<RpcResponse<RpcSignatureResult>>();
let socket = Arc::new(RwLock::new(socket));
let socket_clone = socket.clone();
@@ -277,7 +231,7 @@ impl PubsubClient {
})
.to_string();
let subscription_id =
PubsubClientSubscription::<RpcResponse<SignatureResult>>::send_subscribe(
PubsubClientSubscription::<RpcResponse<RpcSignatureResult>>::send_subscribe(
&socket_clone,
body,
)
@@ -289,7 +243,7 @@ impl PubsubClient {
break;
}
let message: Result<RpcResponse<SignatureResult>, PubsubClientError> =
let message: Result<RpcResponse<RpcSignatureResult>, PubsubClientError> =
PubsubClientSubscription::read_message(&socket_clone);
if let Ok(msg) = message {
@@ -309,7 +263,7 @@ impl PubsubClient {
info!("websocket - exited receive loop");
});
let result: PubsubClientSubscription<RpcResponse<SignatureResult>> =
let result: PubsubClientSubscription<RpcResponse<RpcSignatureResult>> =
PubsubClientSubscription {
message_type: PhantomData,
operation: SIGNATURE_OPERATION,

View File

@@ -5,7 +5,8 @@ use crate::{
rpc_config::RpcAccountInfoConfig,
rpc_config::{
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
RpcSendTransactionConfig, RpcTokenAccountsFilter,
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
RpcTokenAccountsFilter,
},
rpc_request::{RpcError, RpcRequest, TokenAccountsFilter},
rpc_response::*,
@@ -15,7 +16,10 @@ use bincode::serialize;
use indicatif::{ProgressBar, ProgressStyle};
use log::*;
use serde_json::{json, Value};
use solana_account_decoder::{parse_token::UiTokenAmount, UiAccount, UiAccountEncoding};
use solana_account_decoder::{
parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount},
UiAccount, UiAccountData, UiAccountEncoding,
};
use solana_sdk::{
account::Account,
clock::{
@@ -29,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),
}
}
@@ -104,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]),
@@ -137,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]),
)
}
@@ -161,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,
@@ -245,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)
}
@@ -253,7 +342,7 @@ impl RpcClient {
&self,
slot: Slot,
encoding: UiTransactionEncoding,
) -> ClientResult<ConfirmedBlock> {
) -> ClientResult<EncodedConfirmedBlock> {
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
}
@@ -268,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,
@@ -323,7 +423,7 @@ impl RpcClient {
&self,
signature: &Signature,
encoding: UiTransactionEncoding,
) -> ClientResult<ConfirmedTransaction> {
) -> ClientResult<EncodedConfirmedTransaction> {
self.send(
RpcRequest::GetConfirmedTransaction,
json!([signature.to_string(), encoding]),
@@ -411,44 +511,48 @@ 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 status = loop {
let status = self.get_signature_status(&signature)?;
if status.is_none() {
status_retries -= 1;
if status_retries == 0 {
break status;
}
} else {
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() {
if self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::recent(),
)?
.value
.is_none()
{
break status;
}
if cfg!(not(test)) {
// Retry twice a second
sleep(Duration::from_millis(500));
}
};
send_retries = if let Some(result) = status.clone() {
match result {
Ok(_) => return Ok(signature),
Err(_) => 0,
}
} 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. \
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds".to_string()).into(),
);
}
break status;
}
if cfg!(not(test)) {
// Retry twice a second
sleep(Duration::from_millis(500));
}
};
if let Some(result) = status {
match result {
Ok(_) => Ok(signature),
Err(err) => Err(err.into()),
}
} else {
Err(RpcError::ForUser(
"unable to confirm transaction. \
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds"
.to_string(),
)
.into())
}
}
@@ -516,14 +620,14 @@ impl RpcClient {
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 = self.send(RpcRequest::GetMultipleAccounts, json!([pubkeys, config]))?;
let Response {
context,
value: accounts,
} = serde_json::from_value::<Response<Option<UiAccount>>>(response)?;
} = serde_json::from_value::<Response<Vec<Option<UiAccount>>>>(response)?;
let accounts: Vec<Option<Account>> = accounts
.iter()
.map(|rpc_account| rpc_account.decode())
.into_iter()
.map(|rpc_account| rpc_account.map(|a| a.decode()).flatten())
.collect();
Ok(Response {
context,
@@ -571,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)
}
@@ -709,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(|_| {
@@ -720,6 +847,67 @@ impl RpcClient {
Ok(hash)
}
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
Ok(self
.get_token_account_with_commitment(pubkey, CommitmentConfig::default())?
.value)
}
pub fn get_token_account_with_commitment(
&self,
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: 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: 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_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
Ok(self
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
@@ -1039,62 +1227,54 @@ 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 signature = self.send_transaction_with_config(transaction, config)?;
// Get recent commitment in order to count confirmations for successful transactions
let status = self
.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
if status.is_none() {
status_retries -= 1;
if status_retries == 0 {
break (signature, status);
}
} else {
progress_bar.set_message(&format!(
"[{}/{}] Finalizing transaction {}",
confirmations, desired_confirmations, transaction.signatures[0],
));
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())?;
if status.is_none() {
if self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::recent(),
)?
.value
.is_none()
{
break (signature, status);
}
if cfg!(not(test)) {
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) => {
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"
.to_string(),
)
.into());
}
break (signature, status);
}
if cfg!(not(test)) {
sleep(Duration::from_millis(500));
}
};
if let Some(result) = status {
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"
.to_string(),
)
.into());
}
let now = Instant::now();
loop {
match commitment.commitment {
@@ -1276,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);
@@ -1329,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, UiDataSliceConfig};
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
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,15 +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<CommitmentConfig>,
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)]

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,6 +13,7 @@ pub enum RpcRequest {
GetClusterNodes,
GetConfirmedBlock,
GetConfirmedBlocks,
GetConfirmedBlocksWithLimit,
GetConfirmedSignaturesForAddress,
GetConfirmedSignaturesForAddress2,
GetConfirmedTransaction,
@@ -21,6 +22,7 @@ pub enum RpcRequest {
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetFees,
GetFirstAvailableBlock,
GetGenesisHash,
GetIdentity,
GetInflationGovernor,
@@ -66,6 +68,7 @@ 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",
@@ -74,6 +77,7 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetFees => "getFees",
RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
RpcRequest::GetGenesisHash => "getGenesisHash",
RpcRequest::GetIdentity => "getIdentity",
RpcRequest::GetInflationGovernor => "getInflationGovernor",
@@ -116,7 +120,7 @@ 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 = 20;
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
@@ -136,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
@@ -222,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

@@ -7,7 +7,7 @@ use solana_sdk::{
transaction::{Result, TransactionError},
};
use solana_transaction_status::ConfirmedTransactionStatusWithSignature;
use std::{collections::HashMap, net::SocketAddr};
use std::{collections::HashMap, fmt, net::SocketAddr};
pub type RpcResult<T> = client_error::Result<Response<T>>;
@@ -94,11 +94,18 @@ 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", tag = "type", content = "result")]
#[serde(rename_all = "camelCase", untagged)]
pub enum RpcSignatureResult {
ProcessedSignatureResult(ProcessedSignatureResult),
ReceivedSignature,
ProcessedSignature(ProcessedSignatureResult),
ReceivedSignature(ReceivedSignatureResult),
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -108,6 +115,13 @@ pub struct ProcessedSignatureResult {
}
#[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,
@@ -119,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)]
@@ -245,6 +280,15 @@ pub struct RpcConfirmedTransactionStatusWithSignature {
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 {

View File

@@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "1.3.8"
version = "1.3.22"
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,15 +25,16 @@ 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"
num_cpus = "1.13.0"
num-traits = "0.2"
rand = "0.7.0"
@@ -43,44 +45,41 @@ 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.8" }
solana-banks-server = { path = "../banks-server", version = "1.3.8" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.8" }
solana-budget-program = { path = "../programs/budget", version = "1.3.8" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.8" }
solana-client = { path = "../client", version = "1.3.8" }
solana-faucet = { path = "../faucet", version = "1.3.8" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.3.8" }
solana-ledger = { path = "../ledger", version = "1.3.8" }
solana-logger = { path = "../logger", version = "1.3.8" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.3.8" }
solana-metrics = { path = "../metrics", version = "1.3.8" }
solana-measure = { path = "../measure", version = "1.3.8" }
solana-net-utils = { path = "../net-utils", version = "1.3.8" }
solana-perf = { path = "../perf", version = "1.3.8" }
solana-runtime = { path = "../runtime", version = "1.3.8" }
solana-sdk = { path = "../sdk", version = "1.3.8" }
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.3.8" }
solana-stake-program = { path = "../programs/stake", version = "1.3.8" }
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.3.8" }
solana-streamer = { path = "../streamer", version = "1.3.8" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.8" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.8" }
solana-version = { path = "../version", version = "1.3.8" }
solana-vote-program = { path = "../programs/vote", version = "1.3.8" }
solana-vote-signer = { path = "../vote-signer", version = "1.3.8" }
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
solana-account-decoder = { path = "../account-decoder", version = "1.3.22" }
solana-banks-server = { path = "../banks-server", version = "1.3.22" }
solana-budget-program = { path = "../programs/budget", version = "1.3.22" }
solana-clap-utils = { path = "../clap-utils", version = "1.3.22" }
solana-client = { path = "../client", version = "1.3.22" }
solana-faucet = { path = "../faucet", version = "1.3.22" }
solana-ledger = { path = "../ledger", version = "1.3.22" }
solana-logger = { path = "../logger", version = "1.3.22" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.3.22" }
solana-metrics = { path = "../metrics", version = "1.3.22" }
solana-measure = { path = "../measure", version = "1.3.22" }
solana-net-utils = { path = "../net-utils", version = "1.3.22" }
solana-perf = { path = "../perf", version = "1.3.22" }
solana-runtime = { path = "../runtime", version = "1.3.22" }
solana-sdk = { path = "../sdk", version = "1.3.22" }
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.3.22" }
solana-stake-program = { path = "../programs/stake", version = "1.3.22" }
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.3.22" }
solana-streamer = { path = "../streamer", version = "1.3.22" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.22" }
solana-transaction-status = { path = "../transaction-status", version = "1.3.22" }
solana-version = { path = "../version", version = "1.3.22" }
solana-vote-program = { path = "../programs/vote", version = "1.3.22" }
solana-vote-signer = { path = "../vote-signer", version = "1.3.22" }
spl-token-v2-0 = { package = "spl-token", version = "=2.0.6", features = ["skip-no-mangle"] }
tempfile = "3.1.0"
thiserror = "1.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.8" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.3.22" }
tokio = { version = "0.2.22", features = ["full"] }
trees = "0.2.1"
[dev-dependencies]
base64 = "0.12.3"
matches = "0.1.6"
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serial_test = "0.4.0"
@@ -96,6 +95,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"),
@@ -94,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
})
@@ -116,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()));

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

@@ -530,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();
@@ -568,6 +580,8 @@ impl BankingStage {
batch.iteration_order_vec(),
tx_results.processing_results,
TransactionBalancesSet::new(pre_balances, post_balances),
inner_instructions,
transaction_logs,
sender,
);
}
@@ -724,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
@@ -733,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
@@ -783,8 +816,11 @@ impl BankingStage {
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(),
@@ -833,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();
@@ -1061,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]
@@ -1206,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
@@ -1408,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()),
@@ -1488,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,
@@ -1569,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()),
@@ -1640,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,),
@@ -1687,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,
@@ -1784,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()),
@@ -1880,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![
@@ -1898,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()),
@@ -1938,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 =
@@ -2007,36 +2046,25 @@ mod tests {
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() {
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(
0,
InstructionError::Custom(1)
))
);
} else {
assert_eq!(meta, None);
}
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.status,
Err(TransactionError::InstructionError(
0,
InstructionError::Custom(1)
))
);
} else {
assert_eq!(meta, None);
}
}
}

View File

@@ -0,0 +1,93 @@
use solana_ledger::blockstore::Blockstore;
use solana_runtime::commitment::BlockCommitmentCache;
use std::{
sync::atomic::{AtomicBool, Ordering},
sync::{Arc, RwLock},
thread::{self, Builder, JoinHandle},
};
use tokio::runtime;
// Delay uploading the largest confirmed root for this many slots. This is done in an attempt to
// ensure that the `CacheBlockTimeService` has had enough time to add the block time for the root
// before it's uploaded to BigTable.
//
// A more direct connection between CacheBlockTimeService and BigTableUploadService would be
// preferable...
const LARGEST_CONFIRMED_ROOT_UPLOAD_DELAY: usize = 100;
pub struct BigTableUploadService {
thread: JoinHandle<()>,
}
impl BigTableUploadService {
pub fn new(
runtime_handle: runtime::Handle,
bigtable_ledger_storage: solana_storage_bigtable::LedgerStorage,
blockstore: Arc<Blockstore>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
exit: Arc<AtomicBool>,
) -> Self {
info!("Starting BigTable upload service");
let thread = Builder::new()
.name("bigtable-upload".to_string())
.spawn(move || {
Self::run(
runtime_handle,
bigtable_ledger_storage,
blockstore,
block_commitment_cache,
exit,
)
})
.unwrap();
Self { thread }
}
fn run(
runtime: runtime::Handle,
bigtable_ledger_storage: solana_storage_bigtable::LedgerStorage,
blockstore: Arc<Blockstore>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
exit: Arc<AtomicBool>,
) {
let mut start_slot = 0;
loop {
if exit.load(Ordering::Relaxed) {
break;
}
let end_slot = block_commitment_cache
.read()
.unwrap()
.highest_confirmed_root()
.saturating_sub(LARGEST_CONFIRMED_ROOT_UPLOAD_DELAY as u64);
if end_slot <= start_slot {
std::thread::sleep(std::time::Duration::from_secs(1));
continue;
}
let result = runtime.block_on(solana_ledger::bigtable_upload::upload_confirmed_blocks(
blockstore.clone(),
bigtable_ledger_storage.clone(),
start_slot,
Some(end_slot),
true,
exit.clone(),
));
match result {
Ok(()) => start_slot = end_slot,
Err(err) => {
warn!("bigtable: upload_confirmed_blocks: {}", err);
std::thread::sleep(std::time::Duration::from_secs(2));
}
}
}
}
pub fn join(self) -> thread::Result<()> {
self.thread.join()
}
}

View File

@@ -140,13 +140,12 @@ impl BroadcastRun for BroadcastFakeShredsRun {
mod tests {
use super::*;
use crate::contact_info::ContactInfo;
use solana_sdk::pubkey::Pubkey;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
#[test]
fn test_tvu_peers_ordering() {
let cluster = ClusterInfo::new_with_invalid_keypair(ContactInfo::new_localhost(
&Pubkey::new_rand(),
&solana_sdk::pubkey::new_rand(),
0,
));
cluster.insert_info(ContactInfo::new_with_socketaddr(&SocketAddr::new(

View File

@@ -92,7 +92,7 @@ mod tests {
let bank0 = Arc::new(Bank::new(&genesis_config));
let tx = system_transaction::transfer(
&mint_keypair,
&Pubkey::new_rand(),
&solana_sdk::pubkey::new_rand(),
1,
genesis_config.hash(),
);

View File

@@ -0,0 +1,76 @@
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
use solana_ledger::blockstore::Blockstore;
use solana_measure::measure::Measure;
use solana_runtime::bank::Bank;
use solana_sdk::timing::slot_duration_from_slots_per_year;
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::{self, Builder, JoinHandle},
time::Duration,
};
pub type CacheBlockTimeReceiver = Receiver<Arc<Bank>>;
pub type CacheBlockTimeSender = Sender<Arc<Bank>>;
pub struct CacheBlockTimeService {
thread_hdl: JoinHandle<()>,
}
const CACHE_BLOCK_TIME_WARNING_MS: u64 = 150;
impl CacheBlockTimeService {
#[allow(clippy::new_ret_no_self)]
pub fn new(
cache_block_time_receiver: CacheBlockTimeReceiver,
blockstore: Arc<Blockstore>,
exit: &Arc<AtomicBool>,
) -> Self {
let exit = exit.clone();
let thread_hdl = Builder::new()
.name("solana-cache-block-time".to_string())
.spawn(move || loop {
if exit.load(Ordering::Relaxed) {
break;
}
let recv_result = cache_block_time_receiver.recv_timeout(Duration::from_secs(1));
match recv_result {
Err(RecvTimeoutError::Disconnected) => {
break;
}
Ok(bank) => {
let mut cache_block_time_timer = Measure::start("cache_block_time_timer");
Self::cache_block_time(bank, &blockstore);
cache_block_time_timer.stop();
if cache_block_time_timer.as_ms() > CACHE_BLOCK_TIME_WARNING_MS {
warn!(
"cache_block_time operation took: {}ms",
cache_block_time_timer.as_ms()
);
}
}
_ => {}
}
})
.unwrap();
Self { thread_hdl }
}
fn cache_block_time(bank: Arc<Bank>, blockstore: &Arc<Blockstore>) {
let slot_duration = slot_duration_from_slots_per_year(bank.slots_per_year());
let epoch = bank.epoch_schedule().get_epoch(bank.slot());
let stakes = HashMap::new();
let stakes = bank.epoch_vote_accounts(epoch).unwrap_or(&stakes);
if let Err(e) = blockstore.cache_block_time(bank.slot(), slot_duration, stakes) {
error!("cache_block_time failed: slot {:?} {:?}", bank.slot(), e);
}
}
pub fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ use crate::{
cluster_info::{ClusterInfo, GOSSIP_SLEEP_MILLIS},
crds_value::CrdsValueLabel,
optimistic_confirmation_verifier::OptimisticConfirmationVerifier,
optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender},
poh_recorder::PohRecorder,
pubkey_references::LockedPubkeyReferences,
result::{Error, Result},
@@ -26,7 +27,7 @@ use solana_runtime::{
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
};
use solana_sdk::{
clock::{Epoch, Slot},
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT},
epoch_schedule::EpochSchedule,
hash::Hash,
pubkey::Pubkey,
@@ -97,7 +98,7 @@ impl VoteTracker {
epoch_schedule: *root_bank.epoch_schedule(),
..VoteTracker::default()
};
vote_tracker.process_new_root_bank(&root_bank);
vote_tracker.progress_with_new_root_bank(&root_bank);
assert_eq!(
*vote_tracker.leader_schedule_epoch.read().unwrap(),
root_bank.get_leader_schedule_epoch(root_bank.slot())
@@ -173,7 +174,7 @@ impl VoteTracker {
self.keys.get_or_insert(&pubkey);
}
fn update_leader_schedule_epoch(&self, root_bank: &Bank) {
fn progress_leader_schedule_epoch(&self, root_bank: &Bank) {
// Update with any newly calculated epoch state about future epochs
let start_leader_schedule_epoch = *self.leader_schedule_epoch.read().unwrap();
let mut greatest_leader_schedule_epoch = start_leader_schedule_epoch;
@@ -204,7 +205,7 @@ impl VoteTracker {
}
}
fn update_new_root(&self, root_bank: &Bank) {
fn purge_stale_state(&self, root_bank: &Bank) {
// Purge any outdated slot data
let new_root = root_bank.slot();
let root_epoch = root_bank.epoch();
@@ -219,15 +220,15 @@ impl VoteTracker {
self.epoch_authorized_voters
.write()
.unwrap()
.retain(|epoch, _| epoch >= &root_epoch);
.retain(|epoch, _| *epoch >= root_epoch);
self.keys.purge();
*self.current_epoch.write().unwrap() = root_epoch;
}
}
fn process_new_root_bank(&self, root_bank: &Bank) {
self.update_leader_schedule_epoch(root_bank);
self.update_new_root(root_bank);
fn progress_with_new_root_bank(&self, root_bank: &Bank) {
self.progress_leader_schedule_epoch(root_bank);
self.purge_stale_state(root_bank);
}
}
@@ -248,6 +249,7 @@ impl ClusterInfoVoteListener {
verified_vote_sender: VerifiedVoteSender,
replay_votes_receiver: ReplayVoteReceiver,
blockstore: Arc<Blockstore>,
bank_notification_sender: Option<BankNotificationSender>,
) -> Self {
let exit_ = exit.clone();
@@ -293,6 +295,7 @@ impl ClusterInfoVoteListener {
verified_vote_sender,
replay_votes_receiver,
blockstore,
bank_notification_sender,
);
})
.unwrap();
@@ -420,8 +423,9 @@ impl ClusterInfoVoteListener {
verified_vote_sender: VerifiedVoteSender,
replay_votes_receiver: ReplayVoteReceiver,
blockstore: Arc<Blockstore>,
bank_notification_sender: Option<BankNotificationSender>,
) -> Result<()> {
let mut optimistic_confirmation_verifier =
let mut confirmation_verifier =
OptimisticConfirmationVerifier::new(bank_forks.read().unwrap().root());
let mut last_process_root = Instant::now();
loop {
@@ -430,41 +434,40 @@ impl ClusterInfoVoteListener {
}
let root_bank = bank_forks.read().unwrap().root_bank().clone();
if last_process_root.elapsed().as_millis() > 400 {
let unrooted_optimistic_slots = optimistic_confirmation_verifier
.get_unrooted_optimistic_slots(&root_bank, &blockstore);
if last_process_root.elapsed().as_millis() > DEFAULT_MS_PER_SLOT as u128 {
let unrooted_optimistic_slots = confirmation_verifier
.verify_for_unrooted_optimistic_slots(&root_bank, &blockstore);
// SlotVoteTracker's for all `slots` in `unrooted_optimistic_slots`
// should still be available because we haven't purged in
// `process_new_root_bank()` yet, which is called below
// `progress_with_new_root_bank()` yet, which is called below
OptimisticConfirmationVerifier::log_unrooted_optimistic_slots(
&root_bank,
&vote_tracker,
&unrooted_optimistic_slots,
);
vote_tracker.process_new_root_bank(&root_bank);
vote_tracker.progress_with_new_root_bank(&root_bank);
last_process_root = Instant::now();
}
let optimistic_confirmed_slots = Self::get_and_process_votes(
let confirmed_slots = Self::listen_and_confirm_votes(
&gossip_vote_txs_receiver,
&vote_tracker,
&root_bank,
&subscriptions,
&verified_vote_sender,
&replay_votes_receiver,
&bank_notification_sender,
);
if let Err(e) = optimistic_confirmed_slots {
match e {
match confirmed_slots {
Ok(confirmed_slots) => {
confirmation_verifier.add_new_optimistic_confirmed_slots(confirmed_slots);
}
Err(e) => match e {
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout)
| Error::ReadyTimeoutError => (),
_ => {
error!("thread {:?} error {:?}", thread::current().name(), e);
}
}
} else {
let optimistic_confirmed_slots = optimistic_confirmed_slots.unwrap();
optimistic_confirmation_verifier
.add_new_optimistic_confirmed_slots(optimistic_confirmed_slots);
},
}
}
}
@@ -478,23 +481,25 @@ impl ClusterInfoVoteListener {
verified_vote_sender: &VerifiedVoteSender,
replay_votes_receiver: &ReplayVoteReceiver,
) -> Result<Vec<(Slot, Hash)>> {
Self::get_and_process_votes(
Self::listen_and_confirm_votes(
gossip_vote_txs_receiver,
vote_tracker,
root_bank,
subscriptions,
verified_vote_sender,
replay_votes_receiver,
&None,
)
}
fn get_and_process_votes(
fn listen_and_confirm_votes(
gossip_vote_txs_receiver: &VerifiedVoteTransactionsReceiver,
vote_tracker: &VoteTracker,
root_bank: &Bank,
subscriptions: &RpcSubscriptions,
verified_vote_sender: &VerifiedVoteSender,
replay_votes_receiver: &ReplayVoteReceiver,
bank_notification_sender: &Option<BankNotificationSender>,
) -> Result<Vec<(Slot, Hash)>> {
let mut sel = Select::new();
sel.recv(gossip_vote_txs_receiver);
@@ -516,13 +521,14 @@ impl ClusterInfoVoteListener {
let gossip_vote_txs: Vec<_> = gossip_vote_txs_receiver.try_iter().flatten().collect();
let replay_votes: Vec<_> = replay_votes_receiver.try_iter().collect();
if !gossip_vote_txs.is_empty() || !replay_votes.is_empty() {
return Ok(Self::process_votes(
return Ok(Self::filter_and_confirm_with_new_votes(
vote_tracker,
gossip_vote_txs,
replay_votes,
root_bank,
subscriptions,
verified_vote_sender,
bank_notification_sender,
));
} else {
remaining_wait_time = remaining_wait_time
@@ -533,7 +539,7 @@ impl ClusterInfoVoteListener {
}
#[allow(clippy::too_many_arguments)]
fn update_new_votes(
fn track_new_votes_and_notify_confirmations(
vote: Vote,
vote_pubkey: &Pubkey,
vote_tracker: &VoteTracker,
@@ -543,66 +549,69 @@ impl ClusterInfoVoteListener {
diff: &mut HashMap<Slot, HashMap<Arc<Pubkey>, bool>>,
new_optimistic_confirmed_slots: &mut Vec<(Slot, Hash)>,
is_gossip_vote: bool,
bank_notification_sender: &Option<BankNotificationSender>,
) {
if vote.slots.is_empty() {
return;
}
let last_vote_slot = vote.slots.last().unwrap();
let last_vote_slot = *vote.slots.last().unwrap();
let last_vote_hash = vote.hash;
let root = root_bank.slot();
let last_vote_hash = vote.hash;
let mut is_new_vote = false;
for slot in vote.slots.iter().rev() {
// If slot is before the root, or so far ahead we don't have
// stake information, then ignore it
let epoch = root_bank.epoch_schedule().get_epoch(*slot);
// If slot is before the root, ignore it
for slot in vote.slots.iter().filter(|slot| **slot > root).rev() {
let slot = *slot;
// if we don't have stake information, ignore it
let epoch = root_bank.epoch_schedule().get_epoch(slot);
let epoch_stakes = root_bank.epoch_stakes(epoch);
if *slot <= root || epoch_stakes.is_none() {
if epoch_stakes.is_none() {
continue;
}
let epoch_stakes = epoch_stakes.unwrap();
let epoch_vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
let total_epoch_stake = epoch_stakes.total_stake();
let unduplicated_pubkey = vote_tracker.keys.get_or_insert(&vote_pubkey);
// The last vote slot, which is the greatest slot in the stack
// of votes in a vote transaction, qualifies for optimistic confirmation.
let update_optimistic_confirmation_info = if slot == last_vote_slot {
let stake = epoch_vote_accounts
if slot == last_vote_slot {
let vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
let stake = vote_accounts
.get(&vote_pubkey)
.map(|(stake, _)| *stake)
.unwrap_or(0);
Some((stake, last_vote_hash))
} else {
None
};
.unwrap_or_default();
let total_stake = epoch_stakes.total_stake();
// If this vote for this slot qualifies for optimistic confirmation
if let Some((stake, hash)) = update_optimistic_confirmation_info {
// Fast track processing of the last slot in a vote transactions
// so that notifications for optimistic confirmation can be sent
// as soon as possible.
let (is_confirmed, is_new) = Self::add_optimistic_confirmation_vote(
let (is_confirmed, is_new) = Self::track_optimistic_confirmation_vote(
vote_tracker,
*slot,
hash,
last_vote_slot,
last_vote_hash,
unduplicated_pubkey.clone(),
stake,
total_epoch_stake,
total_stake,
);
if is_confirmed {
new_optimistic_confirmed_slots.push((*slot, last_vote_hash));
new_optimistic_confirmed_slots.push((last_vote_slot, last_vote_hash));
// Notify subscribers about new optimistic confirmation
subscriptions.notify_gossip_subscribers(*slot);
if let Some(sender) = bank_notification_sender {
sender
.send(BankNotification::OptimisticallyConfirmed(last_vote_slot))
.unwrap_or_else(|err| {
warn!("bank_notification_sender failed: {:?}", err)
});
}
}
if !is_new && !is_gossip_vote {
// By now:
// 1) The vote must have come from ReplayStage,
// 2) We've seen this vote from replay for this hash before
// (`add_optimistic_confirmation_vote()` will not set `is_new == true`
// (`track_optimistic_confirmation_vote()` will not set `is_new == true`
// for same slot different hash), so short circuit because this vote
// has no new information
@@ -614,7 +623,7 @@ impl ClusterInfoVoteListener {
is_new_vote = is_new;
}
diff.entry(*slot)
diff.entry(slot)
.or_default()
.entry(unduplicated_pubkey)
.and_modify(|seen_in_gossip_previously| {
@@ -629,55 +638,64 @@ impl ClusterInfoVoteListener {
}
}
fn process_votes(
fn filter_gossip_votes(
vote_tracker: &VoteTracker,
vote_pubkey: &Pubkey,
vote: &Vote,
gossip_tx: &Transaction,
) -> bool {
if vote.slots.is_empty() {
return false;
}
let last_vote_slot = vote.slots.last().unwrap();
// Votes from gossip need to be verified as they have not been
// verified by the replay pipeline. Determine the authorized voter
// based on the last vote slot. This will drop votes from authorized
// voters trying to make votes for slots earlier than the epoch for
// which they are authorized
let actual_authorized_voter =
vote_tracker.get_authorized_voter(&vote_pubkey, *last_vote_slot);
if actual_authorized_voter.is_none() {
return false;
}
// Voting without the correct authorized pubkey, dump the vote
if !VoteTracker::vote_contains_authorized_voter(
&gossip_tx,
&actual_authorized_voter.unwrap(),
) {
return false;
}
true
}
fn filter_and_confirm_with_new_votes(
vote_tracker: &VoteTracker,
gossip_vote_txs: Vec<Transaction>,
replayed_votes: Vec<ReplayedVote>,
root_bank: &Bank,
subscriptions: &RpcSubscriptions,
verified_vote_sender: &VerifiedVoteSender,
bank_notification_sender: &Option<BankNotificationSender>,
) -> Vec<(Slot, Hash)> {
let mut diff: HashMap<Slot, HashMap<Arc<Pubkey>, bool>> = HashMap::new();
let mut new_optimistic_confirmed_slots = vec![];
// Process votes from gossip and ReplayStage
for (i, (vote_pubkey, vote, _)) in gossip_vote_txs
for (is_gossip, (vote_pubkey, vote, _)) in gossip_vote_txs
.iter()
.filter_map(|gossip_tx| {
vote_transaction::parse_vote_transaction(gossip_tx).filter(
|(vote_pubkey, vote, _)| {
if vote.slots.is_empty() {
return false;
}
let last_vote_slot = vote.slots.last().unwrap();
// Votes from gossip need to be verified as they have not been
// verified by the replay pipeline. Determine the authorized voter
// based on the last vote slot. This will drop votes from authorized
// voters trying to make votes for slots earlier than the epoch for
// which they are authorized
let actual_authorized_voter =
vote_tracker.get_authorized_voter(&vote_pubkey, *last_vote_slot);
if actual_authorized_voter.is_none() {
return false;
}
// Voting without the correct authorized pubkey, dump the vote
if !VoteTracker::vote_contains_authorized_voter(
&gossip_tx,
&actual_authorized_voter.unwrap(),
) {
return false;
}
true
},
)
vote_transaction::parse_vote_transaction(gossip_tx)
.filter(|(vote_pubkey, vote, _)| {
Self::filter_gossip_votes(vote_tracker, vote_pubkey, vote, gossip_tx)
})
.map(|v| (true, v))
})
.chain(replayed_votes)
.enumerate()
.chain(replayed_votes.into_iter().map(|v| (false, v)))
{
Self::update_new_votes(
Self::track_new_votes_and_notify_confirmations(
vote,
&vote_pubkey,
&vote_tracker,
@@ -686,7 +704,8 @@ impl ClusterInfoVoteListener {
verified_vote_sender,
&mut diff,
&mut new_optimistic_confirmed_slots,
i < gossip_vote_txs.len(),
is_gossip,
bank_notification_sender,
);
}
@@ -741,7 +760,7 @@ impl ClusterInfoVoteListener {
// Returns if the slot was optimistically confirmed, and whether
// the slot was new
fn add_optimistic_confirmation_vote(
fn track_optimistic_confirmation_vote(
vote_tracker: &VoteTracker,
slot: Slot,
hash: Hash,
@@ -770,6 +789,7 @@ impl ClusterInfoVoteListener {
#[cfg(test)]
mod tests {
use super::*;
use crate::optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank;
use solana_perf::packet;
use solana_runtime::{
bank::Bank,
@@ -881,7 +901,7 @@ mod tests {
let (vote_tracker, bank, _, _) = setup();
// Check outdated slots are purged with new root
let new_voter = Arc::new(Pubkey::new_rand());
let new_voter = Arc::new(solana_sdk::pubkey::new_rand());
// Make separate copy so the original doesn't count toward
// the ref count, which would prevent cleanup
let new_voter_ = Arc::new(*new_voter);
@@ -892,7 +912,7 @@ mod tests {
.unwrap()
.contains_key(&bank.slot()));
let bank1 = Bank::new_from_parent(&bank, &Pubkey::default(), bank.slot() + 1);
vote_tracker.process_new_root_bank(&bank1);
vote_tracker.progress_with_new_root_bank(&bank1);
assert!(!vote_tracker
.slot_vote_trackers
.read()
@@ -909,7 +929,7 @@ mod tests {
bank.epoch_schedule()
.get_first_slot_in_epoch(current_epoch + 1),
);
vote_tracker.process_new_root_bank(&new_epoch_bank);
vote_tracker.progress_with_new_root_bank(&new_epoch_bank);
assert!(!vote_tracker.keys.0.read().unwrap().contains(&new_voter));
assert_eq!(
*vote_tracker.current_epoch.read().unwrap(),
@@ -939,7 +959,7 @@ mod tests {
);
let next_leader_schedule_bank =
Bank::new_from_parent(&bank, &Pubkey::default(), next_leader_schedule_computed);
vote_tracker.update_leader_schedule_epoch(&next_leader_schedule_bank);
vote_tracker.progress_leader_schedule_epoch(&next_leader_schedule_bank);
assert_eq!(
*vote_tracker.leader_schedule_epoch.read().unwrap(),
next_leader_schedule_epoch
@@ -990,13 +1010,14 @@ mod tests {
&votes_sender,
&replay_votes_sender,
);
ClusterInfoVoteListener::get_and_process_votes(
ClusterInfoVoteListener::listen_and_confirm_votes(
&votes_receiver,
&vote_tracker,
&bank3,
&subscriptions,
&verified_vote_sender,
&replay_votes_receiver,
&None,
)
.unwrap();
@@ -1018,13 +1039,14 @@ mod tests {
&votes_sender,
&replay_votes_sender,
);
ClusterInfoVoteListener::get_and_process_votes(
ClusterInfoVoteListener::listen_and_confirm_votes(
&votes_receiver,
&vote_tracker,
&bank3,
&subscriptions,
&verified_vote_sender,
&replay_votes_receiver,
&None,
)
.unwrap();
@@ -1095,13 +1117,14 @@ mod tests {
);
// Check that all the votes were registered for each validator correctly
ClusterInfoVoteListener::get_and_process_votes(
ClusterInfoVoteListener::listen_and_confirm_votes(
&votes_txs_receiver,
&vote_tracker,
&bank0,
&subscriptions,
&verified_vote_sender,
&replay_votes_receiver,
&None,
)
.unwrap();
@@ -1213,13 +1236,14 @@ mod tests {
}
// Read and process votes from channel `votes_receiver`
ClusterInfoVoteListener::get_and_process_votes(
ClusterInfoVoteListener::listen_and_confirm_votes(
&votes_txs_receiver,
&vote_tracker,
&bank0,
&subscriptions,
&verified_vote_sender,
&replay_votes_receiver,
&None,
)
.unwrap();
@@ -1307,13 +1331,14 @@ mod tests {
))
.unwrap();
}
let _ = ClusterInfoVoteListener::get_and_process_votes(
let _ = ClusterInfoVoteListener::listen_and_confirm_votes(
&votes_receiver,
&vote_tracker,
&bank,
&subscriptions,
&verified_vote_sender,
&replay_votes_receiver,
&None,
);
}
let slot_vote_tracker = vote_tracker.get_slot_vote_tracker(vote_slot).unwrap();
@@ -1424,13 +1449,16 @@ mod tests {
);
let bank = Bank::new(&genesis_config);
let exit = Arc::new(AtomicBool::new(false));
let bank_forks = BankForks::new(bank);
let bank = bank_forks.get(0).unwrap().clone();
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let bank = bank_forks.read().unwrap().get(0).unwrap().clone();
let vote_tracker = VoteTracker::new(&bank);
let optimistically_confirmed_bank =
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
let subscriptions = Arc::new(RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(bank_forks)),
bank_forks,
Arc::new(RwLock::new(BlockCommitmentCache::default())),
optimistically_confirmed_bank,
));
// Send a vote to process, should add a reference to the pubkey for that voter
@@ -1449,7 +1477,7 @@ mod tests {
)];
let (verified_vote_sender, _verified_vote_receiver) = unbounded();
ClusterInfoVoteListener::process_votes(
ClusterInfoVoteListener::filter_and_confirm_with_new_votes(
&vote_tracker,
vote_tx,
// Add gossip vote for same slot, should not affect outcome
@@ -1461,6 +1489,7 @@ mod tests {
&bank,
&subscriptions,
&verified_vote_sender,
&None,
);
let ref_count = Arc::strong_count(
&vote_tracker
@@ -1519,7 +1548,7 @@ mod tests {
let new_root_bank =
Bank::new_from_parent(&bank, &Pubkey::default(), first_slot_in_new_epoch - 2);
ClusterInfoVoteListener::process_votes(
ClusterInfoVoteListener::filter_and_confirm_with_new_votes(
&vote_tracker,
vote_txs,
vec![(
@@ -1530,6 +1559,7 @@ mod tests {
&new_root_bank,
&subscriptions,
&verified_vote_sender,
&None,
);
// Check new replay vote pubkey first
@@ -1579,12 +1609,15 @@ mod tests {
let bank = Bank::new(&genesis_config);
let vote_tracker = VoteTracker::new(&bank);
let exit = Arc::new(AtomicBool::new(false));
let bank_forks = BankForks::new(bank);
let bank = bank_forks.get(0).unwrap().clone();
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let bank = bank_forks.read().unwrap().get(0).unwrap().clone();
let optimistically_confirmed_bank =
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
let subscriptions = Arc::new(RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(bank_forks)),
bank_forks,
Arc::new(RwLock::new(BlockCommitmentCache::default())),
optimistically_confirmed_bank,
));
// Integrity Checks
@@ -1651,7 +1684,7 @@ mod tests {
fn run_test_verify_votes_1_pass(hash: Option<Hash>) {
let vote_tx = test_vote_tx(hash);
let votes = vec![vote_tx];
let labels = vec![CrdsValueLabel::Vote(0, Pubkey::new_rand())];
let labels = vec![CrdsValueLabel::Vote(0, solana_sdk::pubkey::new_rand())];
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
assert_eq!(vote_txs.len(), 1);
verify_packets_len(&packets, 1);
@@ -1668,7 +1701,7 @@ mod tests {
let mut bad_vote = vote_tx.clone();
bad_vote.signatures[0] = Signature::default();
let votes = vec![vote_tx.clone(), bad_vote, vote_tx];
let label = CrdsValueLabel::Vote(0, Pubkey::new_rand());
let label = CrdsValueLabel::Vote(0, solana_sdk::pubkey::new_rand());
let labels: Vec<_> = (0..votes.len()).map(|_| label.clone()).collect();
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
assert_eq!(vote_txs.len(), 2);

View File

@@ -237,8 +237,8 @@ mod tests {
let mut c1 = ContactInfo::default();
let mut c2 = ContactInfo::default();
let mut map = HashMap::new();
let k1 = Pubkey::new_rand();
let k2 = Pubkey::new_rand();
let k1 = solana_sdk::pubkey::new_rand();
let k2 = solana_sdk::pubkey::new_rand();
map.insert(Arc::new(k1), std::u64::MAX / 2);
map.insert(Arc::new(k2), 0);
cs.cluster_slots
@@ -259,8 +259,8 @@ mod tests {
let mut c1 = ContactInfo::default();
let mut c2 = ContactInfo::default();
let mut map = HashMap::new();
let k1 = Pubkey::new_rand();
let k2 = Pubkey::new_rand();
let k1 = solana_sdk::pubkey::new_rand();
let k2 = solana_sdk::pubkey::new_rand();
map.insert(Arc::new(k2), 0);
cs.cluster_slots
.write()
@@ -290,7 +290,7 @@ mod tests {
let cs = ClusterSlots::default();
let mut contact_infos = vec![ContactInfo::default(); 2];
for ci in contact_infos.iter_mut() {
ci.id = Pubkey::new_rand();
ci.id = solana_sdk::pubkey::new_rand();
}
let slot = 9;
@@ -359,7 +359,7 @@ mod tests {
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[1], 0);
cs.update_internal(0, (vec![epoch_slot], None));
let self_id = Pubkey::new_rand();
let self_id = solana_sdk::pubkey::new_rand();
assert_eq!(
cs.generate_repairs_for_missing_slots(&self_id, 0),
vec![RepairType::HighestShred(1, 0)]

View File

@@ -375,19 +375,22 @@ mod tests {
let rooted_stake_amount = 40;
let sk1 = Pubkey::new_rand();
let pk1 = Pubkey::new_rand();
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
let sk1 = solana_sdk::pubkey::new_rand();
let pk1 = solana_sdk::pubkey::new_rand();
let mut vote_account1 =
vote_state::create_account(&pk1, &solana_sdk::pubkey::new_rand(), 0, 100);
let stake_account1 =
stake_state::create_account(&sk1, &pk1, &vote_account1, &genesis_config.rent, 100);
let sk2 = Pubkey::new_rand();
let pk2 = Pubkey::new_rand();
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
let sk2 = solana_sdk::pubkey::new_rand();
let pk2 = solana_sdk::pubkey::new_rand();
let mut vote_account2 =
vote_state::create_account(&pk2, &solana_sdk::pubkey::new_rand(), 0, 50);
let stake_account2 =
stake_state::create_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
let sk3 = Pubkey::new_rand();
let pk3 = Pubkey::new_rand();
let mut vote_account3 = vote_state::create_account(&pk3, &Pubkey::new_rand(), 0, 1);
let sk3 = solana_sdk::pubkey::new_rand();
let pk3 = solana_sdk::pubkey::new_rand();
let mut vote_account3 =
vote_state::create_account(&pk3, &solana_sdk::pubkey::new_rand(), 0, 1);
let stake_account3 = stake_state::create_account(
&sk3,
&pk3,
@@ -395,9 +398,10 @@ mod tests {
&genesis_config.rent,
rooted_stake_amount,
);
let sk4 = Pubkey::new_rand();
let pk4 = Pubkey::new_rand();
let mut vote_account4 = vote_state::create_account(&pk4, &Pubkey::new_rand(), 0, 1);
let sk4 = solana_sdk::pubkey::new_rand();
let pk4 = solana_sdk::pubkey::new_rand();
let mut vote_account4 =
vote_state::create_account(&pk4, &solana_sdk::pubkey::new_rand(), 0, 1);
let stake_account4 = stake_state::create_account(
&sk4,
&pk4,

View File

@@ -1,6 +1,7 @@
use crate::rpc_subscriptions::RpcSubscriptions;
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
use solana_ledger::blockstore::{Blockstore, CompletedDataSetInfo};
use solana_ledger::entry::Entry;
use solana_sdk::signature::Signature;
use std::{
sync::{
@@ -61,10 +62,7 @@ impl CompletedDataSetsService {
} = completed_set_info;
match blockstore.get_entries_in_data_block(slot, start_index, end_index, None) {
Ok(entries) => {
let transactions = entries
.into_iter()
.flat_map(|e| e.transactions.into_iter().map(|t| t.signatures[0]))
.collect::<Vec<Signature>>();
let transactions = Self::get_transaction_signatures(entries);
if !transactions.is_empty() {
rpc_subscriptions.notify_signatures_received((slot, transactions));
}
@@ -76,7 +74,51 @@ impl CompletedDataSetsService {
Ok(())
}
fn get_transaction_signatures(entries: Vec<Entry>) -> Vec<Signature> {
entries
.into_iter()
.flat_map(|e| {
e.transactions
.into_iter()
.filter_map(|mut t| t.signatures.drain(..).next())
})
.collect::<Vec<Signature>>()
}
pub fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}
#[cfg(test)]
pub mod test {
use super::*;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
#[test]
fn test_zero_signatures() {
let tx = Transaction::new_with_payer(&[], None);
let entries = vec![Entry::new(&Hash::default(), 1, vec![tx])];
let signatures = CompletedDataSetsService::get_transaction_signatures(entries);
assert!(signatures.is_empty());
}
#[test]
fn test_multi_signatures() {
let kp = Keypair::new();
let tx =
Transaction::new_signed_with_payer(&[], Some(&kp.pubkey()), &[&kp], Hash::default());
let entries = vec![Entry::new(&Hash::default(), 1, vec![tx.clone()])];
let signatures = CompletedDataSetsService::get_transaction_signatures(entries);
assert_eq!(signatures.len(), 1);
let entries = vec![
Entry::new(&Hash::default(), 1, vec![tx.clone(), tx.clone()]),
Entry::new(&Hash::default(), 1, vec![tx]),
];
let signatures = CompletedDataSetsService::get_transaction_signatures(entries);
assert_eq!(signatures.len(), 3);
}
}

View File

@@ -13,9 +13,7 @@ use solana_sdk::{
};
use solana_vote_program::{
vote_instruction,
vote_state::{
BlockTimestamp, Lockout, Vote, VoteState, MAX_LOCKOUT_HISTORY, TIMESTAMP_SLOT_INTERVAL,
},
vote_state::{BlockTimestamp, Lockout, Vote, VoteState, MAX_LOCKOUT_HISTORY},
};
use std::{
collections::{HashMap, HashSet},
@@ -345,6 +343,22 @@ impl Tower {
last_vote
}
fn maybe_timestamp(&mut self, current_slot: Slot) -> Option<UnixTimestamp> {
if current_slot > self.last_timestamp.slot
|| self.last_timestamp.slot == 0 && current_slot == self.last_timestamp.slot
{
let timestamp = Utc::now().timestamp();
if timestamp >= self.last_timestamp.timestamp {
self.last_timestamp = BlockTimestamp {
slot: current_slot,
timestamp,
};
return Some(timestamp);
}
}
None
}
pub fn root(&self) -> Option<Slot> {
self.lockouts.root_slot
}
@@ -653,21 +667,6 @@ impl Tower {
);
}
}
fn maybe_timestamp(&mut self, current_slot: Slot) -> Option<UnixTimestamp> {
if self.last_timestamp.slot == 0
|| self.last_timestamp.slot < (current_slot - (current_slot % TIMESTAMP_SLOT_INTERVAL))
{
let timestamp = Utc::now().timestamp();
self.last_timestamp = BlockTimestamp {
slot: current_slot,
timestamp,
};
Some(timestamp)
} else {
None
}
}
}
#[cfg(test)]
@@ -694,12 +693,7 @@ pub mod test {
vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
vote_transaction,
};
use std::{
collections::HashMap,
rc::Rc,
sync::RwLock,
{thread::sleep, time::Duration},
};
use std::{collections::HashMap, rc::Rc, sync::RwLock};
use trees::{tr, Tree, TreeWalk};
pub(crate) struct VoteSimulator {
@@ -1019,7 +1013,7 @@ pub mod test {
&mut account.data,
)
.expect("serialize state");
stakes.push((Pubkey::new_rand(), (*lamports, account)));
stakes.push((solana_sdk::pubkey::new_rand(), (*lamports, account)));
}
stakes
}
@@ -1832,17 +1826,15 @@ pub mod test {
#[test]
fn test_maybe_timestamp() {
let mut tower = Tower::default();
assert!(tower.maybe_timestamp(TIMESTAMP_SLOT_INTERVAL).is_some());
let BlockTimestamp { slot, timestamp } = tower.last_timestamp;
assert!(tower.maybe_timestamp(0).is_some());
assert!(tower.maybe_timestamp(1).is_some());
assert!(tower.maybe_timestamp(0).is_none()); // Refuse to timestamp an older slot
assert!(tower.maybe_timestamp(1).is_none()); // Refuse to timestamp the same slot twice
assert_eq!(tower.maybe_timestamp(1), None);
assert_eq!(tower.maybe_timestamp(slot), None);
assert_eq!(tower.maybe_timestamp(slot + 1), None);
tower.last_timestamp.timestamp -= 1; // Move last_timestamp into the past
assert!(tower.maybe_timestamp(2).is_some()); // slot 2 gets a timestamp
sleep(Duration::from_secs(1));
assert!(tower
.maybe_timestamp(slot + TIMESTAMP_SLOT_INTERVAL + 1)
.is_some());
assert!(tower.last_timestamp.timestamp > timestamp);
tower.last_timestamp.timestamp += 1_000_000; // Move last_timestamp well into the future
assert!(tower.maybe_timestamp(3).is_none()); // slot 3 gets no timestamp
}
}

View File

@@ -130,7 +130,7 @@ impl ContactInfo {
let addr = socketaddr!("224.0.1.255:1000");
assert!(addr.ip().is_multicast());
Self {
id: Pubkey::new_rand(),
id: solana_sdk::pubkey::new_rand(),
gossip: addr,
tvu: addr,
tvu_forwards: addr,

View File

@@ -24,22 +24,25 @@
//! A value is updated to a new version if the labels match, and the value
//! wallclock is later, or the value hash is greater.
use crate::crds_gossip_pull::CrdsFilter;
use crate::crds_shards::CrdsShards;
use crate::crds_value::{CrdsValue, CrdsValueLabel};
use bincode::serialize;
use indexmap::map::IndexMap;
use indexmap::map::{Entry, IndexMap};
use rayon::{prelude::*, ThreadPool};
use solana_sdk::hash::{hash, Hash};
use solana_sdk::pubkey::Pubkey;
use std::cmp;
use std::collections::HashMap;
use std::ops::Index;
const CRDS_SHARDS_BITS: u32 = 8;
#[derive(Clone)]
pub struct Crds {
/// Stores the map of labels and values
pub table: IndexMap<CrdsValueLabel, VersionedCrdsValue>,
pub num_inserts: usize,
pub masks: IndexMap<CrdsValueLabel, u64>,
pub shards: CrdsShards,
}
#[derive(PartialEq, Debug)]
@@ -89,7 +92,7 @@ impl Default for Crds {
Crds {
table: IndexMap::new(),
num_inserts: 0,
masks: IndexMap::new(),
shards: CrdsShards::new(CRDS_SHARDS_BITS),
}
}
}
@@ -103,19 +106,13 @@ impl Crds {
&self,
value: CrdsValue,
local_timestamp: u64,
) -> Option<VersionedCrdsValue> {
) -> (bool, VersionedCrdsValue) {
let new_value = self.new_versioned(local_timestamp, value);
let label = new_value.value.label();
let would_insert = self
.table
.get(&label)
.map(|current| new_value > *current)
.unwrap_or(true);
if would_insert {
Some(new_value)
} else {
None
}
// New value is outdated and fails to insert, if it already exists in
// the table with a more recent wallclock.
let outdated = matches!(self.table.get(&label), Some(current) if new_value <= *current);
(!outdated, new_value)
}
/// insert the new value, returns the old value if insert succeeds
pub fn insert_versioned(
@@ -123,23 +120,28 @@ impl Crds {
new_value: VersionedCrdsValue,
) -> Result<Option<VersionedCrdsValue>, CrdsError> {
let label = new_value.value.label();
let wallclock = new_value.value.wallclock();
let do_insert = self
.table
.get(&label)
.map(|current| new_value > *current)
.unwrap_or(true);
if do_insert {
self.masks.insert(
label.clone(),
CrdsFilter::hash_as_u64(&new_value.value_hash),
);
let old = self.table.insert(label, new_value);
self.num_inserts += 1;
Ok(old)
} else {
trace!("INSERT FAILED data: {} new.wallclock: {}", label, wallclock,);
Err(CrdsError::InsertFailed)
match self.table.entry(label) {
Entry::Vacant(entry) => {
assert!(self.shards.insert(entry.index(), &new_value));
entry.insert(new_value);
self.num_inserts += 1;
Ok(None)
}
Entry::Occupied(mut entry) if *entry.get() < new_value => {
let index = entry.index();
assert!(self.shards.remove(index, entry.get()));
assert!(self.shards.insert(index, &new_value));
self.num_inserts += 1;
Ok(Some(entry.insert(new_value)))
}
_ => {
trace!(
"INSERT FAILED data: {} new.wallclock: {}",
new_value.value.label(),
new_value.value.wallclock(),
);
Err(CrdsError::InsertFailed)
}
}
}
pub fn insert(
@@ -175,33 +177,40 @@ impl Crds {
/// * timeouts - Pubkey specific timeouts with Pubkey::default() as the default timeout.
pub fn find_old_labels(
&self,
thread_pool: &ThreadPool,
now: u64,
timeouts: &HashMap<Pubkey, u64>,
) -> Vec<CrdsValueLabel> {
let min_ts = *timeouts
let default_timeout = *timeouts
.get(&Pubkey::default())
.expect("must have default timeout");
self.table
.iter()
.filter_map(|(k, v)| {
if now < v.local_timestamp
|| (timeouts.get(&k.pubkey()).is_some()
&& now - v.local_timestamp < timeouts[&k.pubkey()])
{
None
} else if now - v.local_timestamp >= min_ts {
Some(k)
} else {
None
}
})
.cloned()
.collect()
thread_pool.install(|| {
self.table
.par_iter()
.with_min_len(1024)
.filter_map(|(k, v)| {
let timeout = timeouts.get(&k.pubkey()).unwrap_or(&default_timeout);
if v.local_timestamp.saturating_add(*timeout) <= now {
Some(k.clone())
} else {
None
}
})
.collect()
})
}
pub fn remove(&mut self, key: &CrdsValueLabel) {
self.table.swap_remove(key);
self.masks.swap_remove(key);
pub fn remove(&mut self, key: &CrdsValueLabel) -> Option<VersionedCrdsValue> {
let (index, _, value) = self.table.swap_remove_full(key)?;
assert!(self.shards.remove(index, &value));
// The previously last element in the table is now moved to the
// 'index' position. Shards need to be updated accordingly.
if index < self.table.len() {
let value = self.table.index(index);
assert!(self.shards.remove(self.table.len(), value));
assert!(self.shards.insert(index, value));
}
Some(value)
}
}
@@ -210,6 +219,8 @@ mod test {
use super::*;
use crate::contact_info::ContactInfo;
use crate::crds_value::CrdsData;
use rand::{thread_rng, Rng};
use rayon::ThreadPoolBuilder;
#[test]
fn test_insert() {
@@ -282,30 +293,67 @@ mod test {
}
#[test]
fn test_find_old_records_default() {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut crds = Crds::default();
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
assert_eq!(crds.insert(val.clone(), 1), Ok(None));
let mut set = HashMap::new();
set.insert(Pubkey::default(), 0);
assert!(crds.find_old_labels(0, &set).is_empty());
assert!(crds.find_old_labels(&thread_pool, 0, &set).is_empty());
set.insert(Pubkey::default(), 1);
assert_eq!(crds.find_old_labels(2, &set), vec![val.label()]);
assert_eq!(
crds.find_old_labels(&thread_pool, 2, &set),
vec![val.label()]
);
set.insert(Pubkey::default(), 2);
assert_eq!(crds.find_old_labels(4, &set), vec![val.label()]);
assert_eq!(
crds.find_old_labels(&thread_pool, 4, &set),
vec![val.label()]
);
}
#[test]
fn test_find_old_records_with_override() {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut rng = thread_rng();
let mut crds = Crds::default();
let mut timeouts = HashMap::new();
let val = CrdsValue::new_rand(&mut rng);
timeouts.insert(Pubkey::default(), 3);
assert_eq!(crds.insert(val.clone(), 0), Ok(None));
assert!(crds.find_old_labels(&thread_pool, 2, &timeouts).is_empty());
timeouts.insert(val.pubkey(), 1);
assert_eq!(
crds.find_old_labels(&thread_pool, 2, &timeouts),
vec![val.label()]
);
timeouts.insert(val.pubkey(), u64::MAX);
assert!(crds.find_old_labels(&thread_pool, 2, &timeouts).is_empty());
timeouts.insert(Pubkey::default(), 1);
assert!(crds.find_old_labels(&thread_pool, 2, &timeouts).is_empty());
timeouts.remove(&val.pubkey());
assert_eq!(
crds.find_old_labels(&thread_pool, 2, &timeouts),
vec![val.label()]
);
}
#[test]
fn test_remove_default() {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut crds = Crds::default();
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
assert_matches!(crds.insert(val.clone(), 1), Ok(_));
let mut set = HashMap::new();
set.insert(Pubkey::default(), 1);
assert_eq!(crds.find_old_labels(2, &set), vec![val.label()]);
assert_eq!(
crds.find_old_labels(&thread_pool, 2, &set),
vec![val.label()]
);
crds.remove(&val.label());
assert!(crds.find_old_labels(2, &set).is_empty());
assert!(crds.find_old_labels(&thread_pool, 2, &set).is_empty());
}
#[test]
fn test_find_old_records_staked() {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut crds = Crds::default();
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
assert_eq!(crds.insert(val.clone(), 1), Ok(None));
@@ -313,24 +361,72 @@ mod test {
//now < timestamp
set.insert(Pubkey::default(), 0);
set.insert(val.pubkey(), 0);
assert!(crds.find_old_labels(0, &set).is_empty());
assert!(crds.find_old_labels(&thread_pool, 0, &set).is_empty());
//pubkey shouldn't expire since its timeout is MAX
set.insert(val.pubkey(), std::u64::MAX);
assert!(crds.find_old_labels(2, &set).is_empty());
assert!(crds.find_old_labels(&thread_pool, 2, &set).is_empty());
//default has max timeout, but pubkey should still expire
set.insert(Pubkey::default(), std::u64::MAX);
set.insert(val.pubkey(), 1);
assert_eq!(crds.find_old_labels(2, &set), vec![val.label()]);
assert_eq!(
crds.find_old_labels(&thread_pool, 2, &set),
vec![val.label()]
);
set.insert(val.pubkey(), 2);
assert!(crds.find_old_labels(2, &set).is_empty());
assert_eq!(crds.find_old_labels(3, &set), vec![val.label()]);
assert!(crds.find_old_labels(&thread_pool, 2, &set).is_empty());
assert_eq!(
crds.find_old_labels(&thread_pool, 3, &set),
vec![val.label()]
);
}
#[test]
fn test_crds_shards() {
fn check_crds_shards(crds: &Crds) {
crds.shards
.check(&crds.table.values().cloned().collect::<Vec<_>>());
}
let mut crds = Crds::default();
let pubkeys: Vec<_> = std::iter::repeat_with(solana_sdk::pubkey::new_rand)
.take(256)
.collect();
let mut rng = thread_rng();
let mut num_inserts = 0;
for _ in 0..4096 {
let pubkey = pubkeys[rng.gen_range(0, pubkeys.len())];
let value = VersionedCrdsValue::new(
rng.gen(), // local_timestamp
CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
&pubkey,
rng.gen(), // now
))),
);
if crds.insert_versioned(value).is_ok() {
check_crds_shards(&crds);
num_inserts += 1;
}
}
assert_eq!(num_inserts, crds.num_inserts);
assert!(num_inserts > 700);
assert!(crds.table.len() > 200);
assert!(num_inserts > crds.table.len());
check_crds_shards(&crds);
// Remove values one by one and assert that shards stay valid.
while !crds.table.is_empty() {
let index = rng.gen_range(0, crds.table.len());
let key = crds.table.get_index(index).unwrap().0.clone();
crds.remove(&key);
check_crds_shards(&crds);
}
}
#[test]
fn test_remove_staked() {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
let mut crds = Crds::default();
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
assert_matches!(crds.insert(val.clone(), 1), Ok(_));
@@ -339,9 +435,12 @@ mod test {
//default has max timeout, but pubkey should still expire
set.insert(Pubkey::default(), std::u64::MAX);
set.insert(val.pubkey(), 1);
assert_eq!(crds.find_old_labels(2, &set), vec![val.label()]);
assert_eq!(
crds.find_old_labels(&thread_pool, 2, &set),
vec![val.label()]
);
crds.remove(&val.label());
assert!(crds.find_old_labels(2, &set).is_empty());
assert!(crds.find_old_labels(&thread_pool, 2, &set).is_empty());
}
#[test]
@@ -421,14 +520,14 @@ mod test {
let v1 = VersionedCrdsValue::new(
1,
CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
&Pubkey::new_rand(),
&solana_sdk::pubkey::new_rand(),
0,
))),
);
let v2 = VersionedCrdsValue::new(
1,
CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
&Pubkey::new_rand(),
&solana_sdk::pubkey::new_rand(),
0,
))),
);

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