Compare commits
313 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9e42883d4b | ||
|
e41460d500 | ||
|
9aacd0f3c3 | ||
|
3f908306a3 | ||
|
8093586b78 | ||
|
a08a6d55fa | ||
|
802c5fcb00 | ||
|
8749a97b94 | ||
|
4313240b1b | ||
|
24bae00560 | ||
|
fae0a6307d | ||
|
9753f1a6ca | ||
|
8ad1554bc9 | ||
|
2367f561dc | ||
|
21d41b976b | ||
|
3303ead54d | ||
|
f91d7da5a4 | ||
|
b2dad84d05 | ||
|
a7b2939bc8 | ||
|
ea3b783b63 | ||
|
733ef4b0b8 | ||
|
0cf83887c6 | ||
|
094271be7d | ||
|
efc3c0d65f | ||
|
0300eea0d6 | ||
|
b03186e3c6 | ||
|
65e1b881f9 | ||
|
28b9e5b572 | ||
|
072e884c24 | ||
|
dc95663de7 | ||
|
a73303be22 | ||
|
330f42c375 | ||
|
5d088c7d06 | ||
|
5c9495f955 | ||
|
fb163187b5 | ||
|
970bba495f | ||
|
9761af201b | ||
|
7600be946a | ||
|
524b380a71 | ||
|
ebb5fc1285 | ||
|
4cfb3dcc7b | ||
|
e8fff4561e | ||
|
b56e66310d | ||
|
bda3bd1557 | ||
|
7723673038 | ||
|
c69e667f5e | ||
|
6157860c0a | ||
|
32fc4e3d0f | ||
|
ee0c0c4a59 | ||
|
356117819c | ||
|
834c96a374 | ||
|
2195d980a2 | ||
|
3e43b042eb | ||
|
851742e5d9 | ||
|
c6c7feb0c2 | ||
|
1fde69ef48 | ||
|
894bedcae7 | ||
|
47f15eaa03 | ||
|
b0c0739db9 | ||
|
c3dc23e84a | ||
|
cc7fc447a4 | ||
|
a401b2b4cf | ||
|
d8c66c8981 | ||
|
49a415414f | ||
|
6c540d2ada | ||
|
da62ebac1a | ||
|
25aee12502 | ||
|
d8e8528797 | ||
|
ed8c796877 | ||
|
ec750cf3eb | ||
|
4a35053fba | ||
|
9797178ad1 | ||
|
dbc58455df | ||
|
4a3f851e49 | ||
|
5a3bf5c90e | ||
|
de6ec11efc | ||
|
7aec87c086 | ||
|
eabc21c23a | ||
|
713f346211 | ||
|
a81bc0ecf8 | ||
|
a3f1580b8b | ||
|
4f20798654 | ||
|
0ecd1755a6 | ||
|
57dd8a555a | ||
|
f64cd4a75a | ||
|
2ce6c86c2a | ||
|
ff9573714b | ||
|
826111cf79 | ||
|
f31f1d0f52 | ||
|
e220f7067b | ||
|
d9726e61bc | ||
|
786fa4f22e | ||
|
5b74678e37 | ||
|
d203bd1998 | ||
|
5f5fa38d85 | ||
|
fadf1efa41 | ||
|
0269fffa5a | ||
|
50e441a9ed | ||
|
9413051053 | ||
|
13e176a633 | ||
|
9268239c75 | ||
|
e51d7af847 | ||
|
147ba1de69 | ||
|
7cc709c82a | ||
|
a2395e8730 | ||
|
ae605f8f02 | ||
|
ea2cc90215 | ||
|
e15ddbb979 | ||
|
bbd8bd2e74 | ||
|
a5794efe16 | ||
|
27095378fa | ||
|
a8836649cb | ||
|
cc81830f13 | ||
|
558a46f5d5 | ||
|
57add5366e | ||
|
5057aaddc0 | ||
|
3865219085 | ||
|
6da06654ff | ||
|
4a375acebc | ||
|
9fcd465928 | ||
|
1f8ef5e640 | ||
|
a1b0f2f681 | ||
|
f59d4f29d9 | ||
|
b379004c3b | ||
|
25491780df | ||
|
4354ad3299 | ||
|
4e94446fc3 | ||
|
d99795c000 | ||
|
fe775a9716 | ||
|
ac76a75937 | ||
|
6c1678244f | ||
|
63a9f33be1 | ||
|
c9da91cb1c | ||
|
b3488e0139 | ||
|
f3814a0478 | ||
|
5e8d8cfb49 | ||
|
ad37276d83 | ||
|
719db7eed0 | ||
|
4ddb72a32d | ||
|
ff1171338f | ||
|
28683b0ad8 | ||
|
4ef3a679a4 | ||
|
e02bcbdae2 | ||
|
7b0187a148 | ||
|
e92283c8d2 | ||
|
ef3781d4ee | ||
|
6da4bec41d | ||
|
31ed985fd0 | ||
|
cdc10712b1 | ||
|
935a836a7d | ||
|
97ba3cbeb0 | ||
|
e8ca35f9ec | ||
|
d5aae9a8af | ||
|
3bb8016a40 | ||
|
579065443a | ||
|
81d636c2bf | ||
|
6a7ce8500b | ||
|
8ee294639a | ||
|
b275f65ef1 | ||
|
37c2b68677 | ||
|
d9944c8ae3 | ||
|
6c8bbdca0a | ||
|
10e8f3ab32 | ||
|
8c0b0f235e | ||
|
ec8ba76e4d | ||
|
60fba7be75 | ||
|
24075ceeff | ||
|
f7ef1e68b0 | ||
|
723e7f11b9 | ||
|
f7211d3c07 | ||
|
6234090361 | ||
|
a001c1c8f6 | ||
|
7f62f4f621 | ||
|
8334a76e5b | ||
|
eadab5e2f0 | ||
|
8bb7b53f3b | ||
|
8c7b8e8c5d | ||
|
a2857928a4 | ||
|
f6780d72b1 | ||
|
5d003c6dab | ||
|
79ee0e06b2 | ||
|
443f132de5 | ||
|
95299e43a2 | ||
|
0cf1894ede | ||
|
f2f4f28c0b | ||
|
57da68d563 | ||
|
8d2337ccf8 | ||
|
270749185c | ||
|
6184254416 | ||
|
c8bb13b3f7 | ||
|
5da83c1491 | ||
|
b04ce80255 | ||
|
a788021181 | ||
|
553e9fb8cd | ||
|
f1bc7ec4fa | ||
|
581181e87f | ||
|
36e1f9fae8 | ||
|
f10ae394c8 | ||
|
f7905d369a | ||
|
ef079d202b | ||
|
b7efc2373c | ||
|
d3b50bc55b | ||
|
8fd3465f8a | ||
|
23b9e6eae3 | ||
|
fe1a977f9e | ||
|
5e538eff7c | ||
|
3efe4b5478 | ||
|
90e0d4fefe | ||
|
2e983fb39f | ||
|
527b20fbbd | ||
|
a0c4b4e5fc | ||
|
282315a721 | ||
|
b8198f8cc5 | ||
|
68ad2dcce1 | ||
|
e87c3421bc | ||
|
20754a7115 | ||
|
8a57ee181e | ||
|
4e6b5a9808 | ||
|
f24fbde43b | ||
|
47f60c7607 | ||
|
8b307ed409 | ||
|
cf21719a07 | ||
|
3157b464c4 | ||
|
2581db5748 | ||
|
634959b3ab | ||
|
03b21f2e9d | ||
|
cc5565b17e | ||
|
50beef0b15 | ||
|
06a54e1423 | ||
|
4d731ecd08 | ||
|
ee06789a66 | ||
|
2dabe1d706 | ||
|
3b1279a005 | ||
|
5c9f85f28d | ||
|
e12dd46ef3 | ||
|
c4fa03b478 | ||
|
9fb749deb7 | ||
|
bd48344de2 | ||
|
78e54f1d2c | ||
|
76a6576976 | ||
|
92ec1ae255 | ||
|
0d203728cc | ||
|
625773e5b8 | ||
|
a4cb1e45ae | ||
|
8aded2778e | ||
|
d940c5b1a3 | ||
|
1be045df94 | ||
|
86191911c7 | ||
|
8f852d8a6b | ||
|
68a439f8da | ||
|
e021832708 | ||
|
87b11aa187 | ||
|
7475a6f444 | ||
|
86ce650661 | ||
|
4dc5a53014 | ||
|
5e35cf3536 | ||
|
e8a8d1efb3 | ||
|
defd9238fa | ||
|
5f061dcea1 | ||
|
e6ee27a738 | ||
|
dd2d25d698 | ||
|
9096c3df02 | ||
|
9f94c2a9a0 | ||
|
34213da9f4 | ||
|
c3c4991c44 | ||
|
9d37a33dcd | ||
|
a04ca03fee | ||
|
64ce4a6203 | ||
|
7ac3c9ec76 | ||
|
7d91515e8d | ||
|
4e3f2c3d2d | ||
|
8b67ba6d3d | ||
|
c2ce68ab90 | ||
|
fe87cb1cd1 | ||
|
1c8f6a836a | ||
|
3d5ff7968e | ||
|
d6160f7744 | ||
|
5e9ce99abf | ||
|
ebd6fe7acb | ||
|
9e91a2c2fd | ||
|
899f57962a | ||
|
3176b00e57 | ||
|
08b9da8397 | ||
|
2bc21ecba2 | ||
|
5b2a65fab3 | ||
|
f5d56eabf3 | ||
|
af45efb62c | ||
|
f528cda832 | ||
|
eeef9f4e59 | ||
|
32124b59e9 | ||
|
aa9772f9c0 | ||
|
5f183bd773 | ||
|
2238e5001b | ||
|
79fa7ef55c | ||
|
07df827411 | ||
|
a259ff0e72 | ||
|
d7d3e767e7 | ||
|
6e8aa9af17 | ||
|
0236de7bc8 | ||
|
899bd1572a | ||
|
97ec4cd44e | ||
|
5500970a7e | ||
|
caea04d8d5 | ||
|
b1a90c3580 | ||
|
5bd4e38345 | ||
|
fddba08571 | ||
|
87963764fa | ||
|
b691a159dd | ||
|
5af1d48be8 | ||
|
3b3ec3313f | ||
|
be00246fb5 | ||
|
1d80ba9edf | ||
|
4bcf976ecd |
@@ -36,7 +36,4 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
|
||||
# `std:
|
||||
# "found possibly newer version of crate `std` which `xyz` depends on
|
||||
rm -rf target/bpfel-unknown-unknown
|
||||
if [[ $BUILDKITE_LABEL = "stable-perf" ]]; then
|
||||
rm -rf target/release
|
||||
fi
|
||||
)
|
||||
|
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- "lts/*"
|
||||
- "node"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
854
Cargo.lock
generated
854
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,6 @@ members = [
|
||||
"perf",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis-utils",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
@@ -32,6 +31,7 @@ members = [
|
||||
"log-analyzer",
|
||||
"merkle-root-bench",
|
||||
"merkle-tree",
|
||||
"stake-o-matic",
|
||||
"storage-bigtable",
|
||||
"storage-proto",
|
||||
"streamer",
|
||||
@@ -39,20 +39,21 @@ members = [
|
||||
"metrics",
|
||||
"net-shaper",
|
||||
"notifier",
|
||||
"poh",
|
||||
"poh-bench",
|
||||
"program-test",
|
||||
"programs/secp256k1",
|
||||
"programs/bpf_loader",
|
||||
"programs/budget",
|
||||
"programs/config",
|
||||
"programs/exchange",
|
||||
"programs/failure",
|
||||
"programs/noop",
|
||||
"programs/ownable",
|
||||
"programs/stake",
|
||||
"programs/vest",
|
||||
"programs/vote",
|
||||
"remote-wallet",
|
||||
"rpc",
|
||||
"ramp-tps",
|
||||
"runtime",
|
||||
"runtime/store-tool",
|
||||
"sdk",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="https://solana.com">
|
||||
<img alt="Solana" src="https://i.imgur.com/uBVzyX3.png" width="250" />
|
||||
<img alt="Solana" src="https://i.imgur.com/OMnvVEz.png" width="250" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -32,12 +32,6 @@ $ sudo apt-get update
|
||||
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang make
|
||||
```
|
||||
|
||||
On Mac M1s, make sure you set up your terminal & homebrew [to use](https://5balloons.info/correct-way-to-install-and-use-homebrew-on-m1-macs/) Rosetta. You can install it with:
|
||||
|
||||
```bash
|
||||
$ softwareupdate --install-rosetta
|
||||
```
|
||||
|
||||
## **2. Download the source code.**
|
||||
|
||||
```bash
|
||||
|
20
SECURITY.md
20
SECURITY.md
@@ -51,27 +51,13 @@ The following components are out of scope for the bounty program
|
||||
* Attacks that require social engineering
|
||||
|
||||
Eligibility:
|
||||
* The participant submitting the bug report shall follow the process outlined within this document
|
||||
* The participant submitting the bug bounty shall follow the process outlined within this document
|
||||
* Valid exploits can be eligible even if they are not successfully executed on the cluster
|
||||
* Multiple submissions for the same class of exploit are still eligible for compensation, though may be compensated at a lower rate, however these will be assessed on a case-by-case basis
|
||||
* Participants must complete KYC and sign the participation agreement here when the registrations are open https://solana.com/validator-registration. Security exploits will still be assessed and open for submission at all times. This needs only be done prior to distribution of tokens.
|
||||
|
||||
Payment of Bug Bounties:
|
||||
* Payments for eligible bug reports are distributed monthly.
|
||||
* Bounties for all bug reports submitted in a given month are paid out in the middle of the
|
||||
following month.
|
||||
* The SOL/USD conversion rate used for payments is the market price at the end of
|
||||
the last day of the month for the month in which the bug was submitted.
|
||||
* The reference for this price is the Closing Price given by Coingecko.com on
|
||||
that date given here:
|
||||
https://www.coingecko.com/en/coins/solana/historical_data/usd#panel
|
||||
* For example, for all bugs submitted in March 2021, the SOL/USD price for bug
|
||||
payouts is the Close price on 2021-03-31 of $19.49. This applies to all bugs
|
||||
submitted in March 2021, to be paid in mid-April 2021.
|
||||
* Bug bounties are paid out in
|
||||
[stake accounts](https://solana.com/staking) with a
|
||||
[lockup](https://docs.solana.com/staking/stake-accounts#lockups)
|
||||
expiring 12 months from the last day of the month in which the bug was submitted.
|
||||
Notes:
|
||||
* All locked tokens can be staked during the lockup period
|
||||
|
||||
<a name="process"></a>
|
||||
## Incident Response Process
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -19,10 +19,11 @@ lazy_static = "1.4.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.9" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
zstd = "0.5.1"
|
||||
|
||||
|
@@ -48,7 +48,7 @@ pub enum UiAccountData {
|
||||
Binary(String, UiAccountEncoding),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum UiAccountEncoding {
|
||||
Binary, // Legacy. Retained for RPC backwards compatibility
|
||||
@@ -62,7 +62,7 @@ pub enum UiAccountEncoding {
|
||||
impl UiAccount {
|
||||
pub fn encode<T: ReadableAccount>(
|
||||
pubkey: &Pubkey,
|
||||
account: &T,
|
||||
account: T,
|
||||
encoding: UiAccountEncoding,
|
||||
additional_data: Option<AccountAdditionalData>,
|
||||
data_slice_config: Option<UiDataSliceConfig>,
|
||||
@@ -224,7 +224,7 @@ mod test {
|
||||
fn test_base64_zstd() {
|
||||
let encoded_account = UiAccount::encode(
|
||||
&Pubkey::default(),
|
||||
&AccountSharedData::from(Account {
|
||||
AccountSharedData::from(Account {
|
||||
data: vec![0; 1024],
|
||||
..Account::default()
|
||||
}),
|
||||
|
@@ -9,14 +9,14 @@ use crate::{
|
||||
};
|
||||
use inflector::Inflector;
|
||||
use serde_json::Value;
|
||||
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, stake, system_program, sysvar};
|
||||
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program, sysvar};
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
|
||||
lazy_static! {
|
||||
static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id();
|
||||
static ref CONFIG_PROGRAM_ID: Pubkey = solana_config_program::id();
|
||||
static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id();
|
||||
static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id();
|
||||
static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
|
||||
static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id();
|
||||
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();
|
||||
|
@@ -6,10 +6,10 @@ use bincode::deserialize;
|
||||
use serde_json::Value;
|
||||
use solana_config_program::{get_config_data, ConfigKeys};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::stake::config::{self as stake_config, Config as StakeConfig};
|
||||
use solana_stake_program::config::Config as StakeConfig;
|
||||
|
||||
pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
|
||||
let parsed_account = if pubkey == &stake_config::id() {
|
||||
let parsed_account = if pubkey == &solana_stake_program::config::id() {
|
||||
get_config_data(data)
|
||||
.ok()
|
||||
.and_then(|data| deserialize::<StakeConfig>(data).ok())
|
||||
@@ -101,7 +101,11 @@ mod test {
|
||||
};
|
||||
let stake_config_account = create_config_account(vec![], &stake_config, 10);
|
||||
assert_eq!(
|
||||
parse_config(&stake_config_account.data(), &stake_config::id()).unwrap(),
|
||||
parse_config(
|
||||
&stake_config_account.data(),
|
||||
&solana_stake_program::config::id()
|
||||
)
|
||||
.unwrap(),
|
||||
ConfigAccountType::StakeConfig(UiStakeConfig {
|
||||
warmup_cooldown_rate: 0.25,
|
||||
slash_penalty: 50,
|
||||
|
@@ -4,7 +4,7 @@ use crate::{
|
||||
};
|
||||
use bincode::deserialize;
|
||||
use solana_sdk::clock::{Epoch, UnixTimestamp};
|
||||
use solana_sdk::stake::state::{Authorized, Delegation, Lockup, Meta, Stake, StakeState};
|
||||
use solana_stake_program::stake_state::{Authorized, Delegation, Lockup, Meta, Stake, StakeState};
|
||||
|
||||
pub fn parse_stake(data: &[u8]) -> Result<StakeAccountType, ParseAccountError> {
|
||||
let stake_state: StakeState = deserialize(data)
|
||||
|
@@ -14,23 +14,23 @@ use std::str::FromStr;
|
||||
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_id_v2_0() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token_v2_0::id().to_bytes())
|
||||
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_v2_0_native_mint() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token_v2_0::native_mint::id().to_bytes())
|
||||
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_v2_0_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
|
||||
SplTokenPubkey::new_from_array(pubkey.to_bytes())
|
||||
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert a spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
|
||||
pub fn pubkey_from_spl_token_v2_0(pubkey: &SplTokenPubkey) -> Pubkey {
|
||||
Pubkey::new_from_array(pubkey.to_bytes())
|
||||
Pubkey::from_str(&pubkey.to_string()).unwrap()
|
||||
}
|
||||
|
||||
pub fn parse_token(
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -11,11 +11,11 @@ publish = false
|
||||
[dependencies]
|
||||
log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
rand = "0.7.0"
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
|
@@ -6,9 +6,7 @@ use rayon::prelude::*;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::{
|
||||
accounts::{create_test_accounts, update_accounts_bench, Accounts},
|
||||
accounts_db::AccountShrinkThreshold,
|
||||
accounts_index::AccountSecondaryIndexes,
|
||||
ancestors::Ancestors,
|
||||
accounts_index::{AccountSecondaryIndexes, Ancestors},
|
||||
};
|
||||
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
@@ -65,7 +63,6 @@ fn main() {
|
||||
&ClusterType::Testnet,
|
||||
AccountSecondaryIndexes::default(),
|
||||
false,
|
||||
AccountShrinkThreshold::default(),
|
||||
);
|
||||
println!("Creating {} accounts", num_accounts);
|
||||
let mut create_time = Measure::start("create accounts");
|
||||
@@ -90,19 +87,17 @@ fn main() {
|
||||
num_slots,
|
||||
create_time
|
||||
);
|
||||
let mut ancestors = Vec::with_capacity(num_slots);
|
||||
ancestors.push(0);
|
||||
let mut ancestors: Ancestors = vec![(0, 0)].into_iter().collect();
|
||||
for i in 1..num_slots {
|
||||
ancestors.push(i as u64);
|
||||
ancestors.insert(i as u64, i - 1);
|
||||
accounts.add_root(i as u64);
|
||||
}
|
||||
let ancestors = Ancestors::from(ancestors);
|
||||
let mut elapsed = vec![0; iterations];
|
||||
let mut elapsed_store = vec![0; iterations];
|
||||
for x in 0..iterations {
|
||||
if clean {
|
||||
let mut time = Measure::start("clean");
|
||||
accounts.accounts_db.clean_accounts(None, false);
|
||||
accounts.accounts_db.clean_accounts(None);
|
||||
time.stop();
|
||||
println!("{}", time);
|
||||
for slot in 0..num_slots {
|
||||
@@ -121,7 +116,6 @@ fn main() {
|
||||
solana_sdk::clock::Slot::default(),
|
||||
&ancestors,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
time_store.stop();
|
||||
if results != results_store {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-cluster-bench"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,23 +13,22 @@ clap = "2.33.1"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.4.1"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-client = { path = "../client", version = "=1.7.2" }
|
||||
solana-core = { path = "../core", version = "=1.7.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.2" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-client = { path = "../client", version = "=1.6.9" }
|
||||
solana-core = { path = "../core", version = "=1.6.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.2" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -6,8 +6,8 @@ use rayon::prelude::*;
|
||||
use solana_account_decoder::parse_token::spl_token_v2_0_pubkey;
|
||||
use solana_clap_utils::input_parsers::pubkey_of;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::gossip_service::discover;
|
||||
use solana_faucet::faucet::{request_airdrop_transaction, FAUCET_PORT};
|
||||
use solana_gossip::gossip_service::discover;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::inline_spl_token_v2_0;
|
||||
use solana_sdk::{
|
||||
@@ -363,7 +363,7 @@ fn run_accounts_bench(
|
||||
iterations: usize,
|
||||
maybe_space: Option<u64>,
|
||||
batch_size: usize,
|
||||
close_nth_batch: u64,
|
||||
close_nth: u64,
|
||||
maybe_lamports: Option<u64>,
|
||||
num_instructions: usize,
|
||||
mint: Option<Pubkey>,
|
||||
@@ -441,7 +441,6 @@ fn run_accounts_bench(
|
||||
}
|
||||
}
|
||||
|
||||
// Create accounts
|
||||
let sigs_len = executor.num_outstanding();
|
||||
if sigs_len < batch_size {
|
||||
let num_to_create = batch_size - sigs_len;
|
||||
@@ -476,14 +475,10 @@ fn run_accounts_bench(
|
||||
}
|
||||
}
|
||||
|
||||
if close_nth_batch > 0 {
|
||||
let num_batches_to_close =
|
||||
total_accounts_created as u64 / (close_nth_batch * batch_size as u64);
|
||||
let expected_closed = num_batches_to_close * batch_size as u64;
|
||||
let max_closed_seed = seed_tracker.max_closed.load(Ordering::Relaxed);
|
||||
// Close every account we've created with seed between max_closed_seed..expected_closed
|
||||
if max_closed_seed < expected_closed {
|
||||
let txs: Vec<_> = (0..expected_closed - max_closed_seed)
|
||||
if close_nth > 0 {
|
||||
let expected_closed = total_accounts_created as u64 / close_nth;
|
||||
if expected_closed > total_accounts_closed {
|
||||
let txs: Vec<_> = (0..expected_closed - total_accounts_closed)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let message = make_close_message(
|
||||
@@ -577,14 +572,14 @@ fn main() {
|
||||
.help("Number of transactions to send per batch"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("close_nth_batch")
|
||||
Arg::with_name("close_nth")
|
||||
.long("close-frequency")
|
||||
.takes_value(true)
|
||||
.value_name("BYTES")
|
||||
.help(
|
||||
"Every `n` batches, create a batch of close transactions for
|
||||
the earliest remaining batch of accounts created.
|
||||
Note: Should be > 1 to avoid situations where the close \
|
||||
"Send close transactions after this many accounts created. \
|
||||
Note: a `close-frequency` value near or below `batch-size` \
|
||||
may result in transaction-simulation errors, as the close \
|
||||
transactions will be submitted before the corresponding \
|
||||
create transactions have been confirmed",
|
||||
),
|
||||
@@ -637,7 +632,7 @@ fn main() {
|
||||
let space = value_t!(matches, "space", u64).ok();
|
||||
let lamports = value_t!(matches, "lamports", u64).ok();
|
||||
let batch_size = value_t!(matches, "batch_size", usize).unwrap_or(4);
|
||||
let close_nth_batch = value_t!(matches, "close_nth_batch", u64).unwrap_or(0);
|
||||
let close_nth = value_t!(matches, "close_nth", u64).unwrap_or(0);
|
||||
let iterations = value_t!(matches, "iterations", usize).unwrap_or(10);
|
||||
let num_instructions = value_t!(matches, "num_instructions", usize).unwrap_or(1);
|
||||
if num_instructions == 0 || num_instructions > 500 {
|
||||
@@ -662,14 +657,14 @@ fn main() {
|
||||
let rpc_addr = if !skip_gossip {
|
||||
info!("Finding cluster entry: {:?}", entrypoint_addr);
|
||||
let (gossip_nodes, _validators) = discover(
|
||||
None, // keypair
|
||||
None,
|
||||
Some(&entrypoint_addr),
|
||||
None, // num_nodes
|
||||
Duration::from_secs(60), // timeout
|
||||
None, // find_node_by_pubkey
|
||||
Some(&entrypoint_addr), // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
0, // my_shred_version
|
||||
None,
|
||||
Some(60),
|
||||
None,
|
||||
Some(&entrypoint_addr),
|
||||
None,
|
||||
0,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
|
||||
@@ -690,7 +685,7 @@ fn main() {
|
||||
iterations,
|
||||
space,
|
||||
batch_size,
|
||||
close_nth_batch,
|
||||
close_nth,
|
||||
lamports,
|
||||
num_instructions,
|
||||
mint,
|
||||
@@ -725,7 +720,7 @@ pub mod test {
|
||||
let iterations = 10;
|
||||
let maybe_space = None;
|
||||
let batch_size = 100;
|
||||
let close_nth_batch = 100;
|
||||
let close_nth = 100;
|
||||
let maybe_lamports = None;
|
||||
let num_instructions = 2;
|
||||
let mut start = Measure::start("total accounts run");
|
||||
@@ -736,7 +731,7 @@ pub mod test {
|
||||
iterations,
|
||||
maybe_space,
|
||||
batch_size,
|
||||
close_nth_batch,
|
||||
close_nth,
|
||||
maybe_lamports,
|
||||
num_instructions,
|
||||
None,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -14,18 +14,16 @@ crossbeam-channel = "0.4"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
solana-core = { path = "../core", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.2" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.2" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.2" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-core = { path = "../core", version = "=1.6.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.9" }
|
||||
solana-perf = { path = "../perf", version = "=1.6.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -4,8 +4,13 @@ use crossbeam_channel::unbounded;
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_core::banking_stage::BankingStage;
|
||||
use solana_gossip::{cluster_info::ClusterInfo, cluster_info::Node};
|
||||
use solana_core::{
|
||||
banking_stage::{create_test_recorder, BankingStage},
|
||||
cluster_info::ClusterInfo,
|
||||
cluster_info::Node,
|
||||
poh_recorder::PohRecorder,
|
||||
poh_recorder::WorkingBankEntry,
|
||||
};
|
||||
use solana_ledger::{
|
||||
blockstore::Blockstore,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
@@ -13,7 +18,6 @@ use solana_ledger::{
|
||||
};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_poh::poh_recorder::{create_test_recorder, PohRecorder, WorkingBankEntry};
|
||||
use solana_runtime::{
|
||||
accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks,
|
||||
};
|
||||
@@ -74,7 +78,7 @@ fn make_accounts_txs(
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut new = dummy.clone();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
if !same_payer {
|
||||
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
|
||||
}
|
||||
@@ -185,7 +189,7 @@ fn main() {
|
||||
genesis_config.hash(),
|
||||
);
|
||||
// Ignore any pesky duplicate signature errors in the case we are using single-payer
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
fund.signatures = vec![Signature::new(&sig[0..64])];
|
||||
let x = bank.process_transaction(&fund);
|
||||
x.unwrap();
|
||||
@@ -351,7 +355,7 @@ fn main() {
|
||||
if bank.slot() > 0 && bank.slot() % 16 == 0 {
|
||||
for tx in transactions.iter_mut() {
|
||||
tx.message.recent_blockhash = bank.last_blockhash();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
tx.signatures[0] = Signature::new(&sig[0..64]);
|
||||
}
|
||||
verified = to_packets_chunked(&transactions.clone(), packets_per_chunk);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-client"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -15,16 +15,16 @@ borsh = "0.8.1"
|
||||
borsh-derive = "0.8.1"
|
||||
futures = "0.3"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.2" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.6.9" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.6.9" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-interface"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,7 +12,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
mio = "0.7.6"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-server"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -14,10 +14,10 @@ bincode = "1.3.1"
|
||||
futures = "0.3"
|
||||
log = "0.4.11"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.2" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.9" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -18,22 +18,21 @@ rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-core = { path = "../core", version = "=1.7.2" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.2" }
|
||||
solana-client = { path = "../client", version = "=1.7.2" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.2" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-core = { path = "../core", version = "=1.6.9" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.6.9" }
|
||||
solana-client = { path = "../client", version = "=1.6.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.9" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.2" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -5,7 +5,7 @@ pub mod order_book;
|
||||
|
||||
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
|
||||
use log::*;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_sdk::signature::Signer;
|
||||
|
||||
fn main() {
|
||||
|
@@ -1,11 +1,13 @@
|
||||
use log::*;
|
||||
use solana_bench_exchange::bench::{airdrop_lamports, do_bench_exchange, Config};
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_core::{
|
||||
gossip_service::{discover_cluster, get_multi_client},
|
||||
validator::ValidatorConfig,
|
||||
};
|
||||
use solana_exchange_program::{
|
||||
exchange_processor::process_instruction, id, solana_exchange_program,
|
||||
};
|
||||
use solana_faucet::faucet::run_local_faucet_with_port;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -10,11 +10,11 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -75,7 +75,7 @@ fn main() -> Result<()> {
|
||||
|
||||
let mut read_channels = Vec::new();
|
||||
let mut read_threads = Vec::new();
|
||||
let recycler = PacketsRecycler::default();
|
||||
let recycler = PacketsRecycler::new_without_limit("bench-streamer-recycler-shrink-stats");
|
||||
for _ in 0..num_sockets {
|
||||
let read = solana_net_utils::bind_to(ip_addr, port, false).unwrap();
|
||||
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
||||
@@ -92,7 +92,6 @@ fn main() -> Result<()> {
|
||||
recycler.clone(),
|
||||
"bench-streamer-test",
|
||||
1,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -15,23 +15,22 @@ log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-core = { path = "../core", version = "=1.7.2" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.2" }
|
||||
solana-client = { path = "../client", version = "=1.7.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.2" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-core = { path = "../core", version = "=1.6.9" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.6.9" }
|
||||
solana-client = { path = "../client", version = "=1.6.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.2" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,8 +2,8 @@
|
||||
use log::*;
|
||||
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate_keypairs};
|
||||
use solana_bench_tps::cli;
|
||||
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||
use solana_genesis::Base64Account;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_program;
|
||||
|
@@ -5,9 +5,8 @@ use solana_bench_tps::{
|
||||
cli::Config,
|
||||
};
|
||||
use solana_client::thin_client::create_client;
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_core::{cluster_info::VALIDATOR_PORT_RANGE, validator::ValidatorConfig};
|
||||
use solana_faucet::faucet::run_local_faucet_with_port;
|
||||
use solana_gossip::cluster_info::VALIDATOR_PORT_RANGE;
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
|
@@ -5,6 +5,9 @@ steps:
|
||||
- command: "ci/publish-tarball.sh"
|
||||
timeout_in_minutes: 60
|
||||
name: "publish tarball"
|
||||
- command: "ci/publish-bpf-sdk.sh"
|
||||
timeout_in_minutes: 5
|
||||
name: "publish bpf sdk"
|
||||
- wait
|
||||
- command: "sdk/docker-solana/build.sh"
|
||||
timeout_in_minutes: 60
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM solanalabs/rust:1.52.1
|
||||
FROM solanalabs/rust:1.51.0
|
||||
ARG date
|
||||
|
||||
RUN set -x \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Note: when the rust version is changed also modify
|
||||
# ci/rust-version.sh to pick up the new image tag
|
||||
FROM rust:1.52.1
|
||||
FROM rust:1.51.0
|
||||
|
||||
# Add Google Protocol Buffers for Libra's metrics library.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
27
ci/publish-bpf-sdk.sh
Executable file
27
ci/publish-bpf-sdk.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
eval "$(ci/channel-info.sh)"
|
||||
|
||||
if [[ -n "$CI_TAG" ]]; then
|
||||
CHANNEL_OR_TAG=$CI_TAG
|
||||
else
|
||||
CHANNEL_OR_TAG=$CHANNEL
|
||||
fi
|
||||
|
||||
(
|
||||
set -x
|
||||
sdk/bpf/scripts/package.sh
|
||||
[[ -f bpf-sdk.tar.bz2 ]]
|
||||
)
|
||||
|
||||
source ci/upload-ci-artifact.sh
|
||||
echo --- AWS S3 Store
|
||||
if [[ -z $CHANNEL_OR_TAG ]]; then
|
||||
echo Skipped
|
||||
else
|
||||
upload-s3-artifact "/solana/bpf-sdk.tar.bz2" "s3://solana-sdk/$CHANNEL_OR_TAG/bpf-sdk.tar.bz2"
|
||||
fi
|
||||
|
||||
exit 0
|
@@ -7,7 +7,7 @@ source multinode-demo/common.sh
|
||||
|
||||
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
|
||||
|
||||
SOLANA_RUN_SH_VALIDATOR_ARGS="--snapshot-interval-slots 200" timeout 120 ./run.sh &
|
||||
timeout 120 ./run.sh &
|
||||
pid=$!
|
||||
|
||||
attempts=20
|
||||
@@ -16,8 +16,6 @@ while [[ ! -f config/run/init-completed ]]; do
|
||||
if ((--attempts == 0)); then
|
||||
echo "Error: validator failed to boot"
|
||||
exit 1
|
||||
else
|
||||
echo "Checking init"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -26,7 +24,6 @@ snapshot_slot=1
|
||||
# wait a bit longer than snapshot_slot
|
||||
while [[ $($solana_cli --url http://localhost:8899 slot --commitment processed) -le $((snapshot_slot + 1)) ]]; do
|
||||
sleep 1
|
||||
echo "Checking slot"
|
||||
done
|
||||
|
||||
$solana_validator --ledger config/ledger exit --force || true
|
||||
|
@@ -18,13 +18,13 @@
|
||||
if [[ -n $RUST_STABLE_VERSION ]]; then
|
||||
stable_version="$RUST_STABLE_VERSION"
|
||||
else
|
||||
stable_version=1.52.1
|
||||
stable_version=1.51.0
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2021-05-18
|
||||
nightly_version=2021-04-18
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -27,7 +27,7 @@ BENCH_ARTIFACT=current_bench_results.log
|
||||
_ "$cargo" build --manifest-path=keygen/Cargo.toml
|
||||
export PATH="$PWD/target/debug":$PATH
|
||||
|
||||
# Clear the C dependency files, if dependency moves these files are not regenerated
|
||||
# Clear the C dependency files, if dependeny moves these files are not regenerated
|
||||
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
|
||||
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
||||
|
||||
@@ -45,14 +45,6 @@ _ "$cargo" nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
|
||||
_ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
||||
# Run gossip benches
|
||||
_ "$cargo" nightly bench --manifest-path gossip/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
||||
# Run poh benches
|
||||
_ "$cargo" nightly bench --manifest-path poh/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
||||
# Run core benches
|
||||
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
@@ -44,7 +44,7 @@ test-stable-perf)
|
||||
./cargo-build-bpf --manifest-path sdk/Cargo.toml
|
||||
|
||||
# BPF Program unit tests
|
||||
"$cargo" test --manifest-path programs/bpf/Cargo.toml
|
||||
"$cargo" stable test --manifest-path programs/bpf/Cargo.toml
|
||||
cargo-build-bpf --manifest-path programs/bpf/Cargo.toml --bpf-sdk sdk/bpf
|
||||
|
||||
# BPF program system tests
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,8 +12,8 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.8.0"
|
||||
uriparse = "0.6.3"
|
||||
|
@@ -17,9 +17,6 @@ use {
|
||||
std::{str::FromStr, sync::Arc},
|
||||
};
|
||||
|
||||
// Sentinel value used to indicate to write to screen instead of file
|
||||
pub const STDOUT_OUTFILE_TOKEN: &str = "-";
|
||||
|
||||
// Return parsed values from matches at `name`
|
||||
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
|
||||
where
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use {
|
||||
crate::{
|
||||
input_parsers::{pubkeys_sigs_of, STDOUT_OUTFILE_TOKEN},
|
||||
input_parsers::pubkeys_sigs_of,
|
||||
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
|
||||
ArgConstant,
|
||||
},
|
||||
@@ -24,11 +24,9 @@ use {
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
convert::TryFrom,
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
ops::Deref,
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
@@ -92,48 +90,12 @@ impl CliSignerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DefaultSigner {
|
||||
pub arg_name: String,
|
||||
pub path: String,
|
||||
is_path_checked: RefCell<bool>,
|
||||
}
|
||||
|
||||
impl DefaultSigner {
|
||||
pub fn new<AN: AsRef<str>, P: AsRef<str>>(arg_name: AN, path: P) -> Self {
|
||||
let arg_name = arg_name.as_ref().to_string();
|
||||
let path = path.as_ref().to_string();
|
||||
Self {
|
||||
arg_name,
|
||||
path,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> Result<&str, Box<dyn std::error::Error>> {
|
||||
if !self.is_path_checked.borrow().deref() {
|
||||
parse_signer_source(&self.path)
|
||||
.and_then(|s| {
|
||||
if let SignerSourceKind::Filepath(path) = &s.kind {
|
||||
std::fs::metadata(path).map(|_| ()).map_err(|e| e.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
|
||||
self.path
|
||||
),
|
||||
)
|
||||
})?;
|
||||
*self.is_path_checked.borrow_mut() = true;
|
||||
}
|
||||
Ok(&self.path)
|
||||
}
|
||||
|
||||
pub fn generate_unique_signers(
|
||||
&self,
|
||||
bulk_signers: Vec<Option<Box<dyn Signer>>>,
|
||||
@@ -163,7 +125,7 @@ impl DefaultSigner {
|
||||
matches: &ArgMatches,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path(matches, self.path()?, &self.arg_name, wallet_manager)
|
||||
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
|
||||
}
|
||||
|
||||
pub fn signer_from_path_with_config(
|
||||
@@ -172,13 +134,7 @@ impl DefaultSigner {
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
config: &SignerFromPathConfig,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path_with_config(
|
||||
matches,
|
||||
self.path()?,
|
||||
&self.arg_name,
|
||||
wallet_manager,
|
||||
config,
|
||||
)
|
||||
signer_from_path_with_config(matches, &self.path, &self.arg_name, wallet_manager, config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,17 +211,7 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||
source: S,
|
||||
) -> Result<SignerSource, SignerSourceError> {
|
||||
let source = source.as_ref();
|
||||
let source = {
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
source.replace("\\", "/")
|
||||
}
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
{
|
||||
source.to_string()
|
||||
}
|
||||
};
|
||||
match uriparse::URIReference::try_from(source.as_str()) {
|
||||
match uriparse::URIReference::try_from(source) {
|
||||
Err(_) => Err(SignerSourceError::UnrecognizedSource),
|
||||
Ok(uri) => {
|
||||
if let Some(scheme) = uri.scheme() {
|
||||
@@ -285,24 +231,18 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||
legacy: false,
|
||||
}),
|
||||
SIGNER_SOURCE_STDIN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
_ => {
|
||||
#[cfg(target_family = "windows")]
|
||||
// On Windows, an absolute path's drive letter will be parsed as the URI
|
||||
// scheme. Assume a filepath source in case of a single character shceme.
|
||||
if scheme.len() == 1 {
|
||||
return Ok(SignerSource::new(SignerSourceKind::Filepath(source)));
|
||||
}
|
||||
Err(SignerSourceError::UnrecognizedSource)
|
||||
}
|
||||
_ => Err(SignerSourceError::UnrecognizedSource),
|
||||
}
|
||||
} else {
|
||||
match source.as_str() {
|
||||
STDOUT_OUTFILE_TOKEN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
match source {
|
||||
"-" => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
|
||||
_ => match Pubkey::from_str(source.as_str()) {
|
||||
_ => match Pubkey::from_str(source) {
|
||||
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
|
||||
Err(_) => std::fs::metadata(source.as_str())
|
||||
.map(|_| SignerSource::new(SignerSourceKind::Filepath(source)))
|
||||
Err(_) => std::fs::metadata(source)
|
||||
.map(|_| {
|
||||
SignerSource::new(SignerSourceKind::Filepath(source.to_string()))
|
||||
})
|
||||
.map_err(|err| err.into()),
|
||||
},
|
||||
}
|
||||
@@ -692,7 +632,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_signer_source() {
|
||||
assert!(matches!(
|
||||
parse_signer_source(STDOUT_OUTFILE_TOKEN).unwrap(),
|
||||
parse_signer_source("-").unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Stdin,
|
||||
derivation_path: None,
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -12,19 +12,20 @@ documentation = "https://docs.rs/solana-cli-output"
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
console = "0.14.1"
|
||||
console = "0.11.3"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.15.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-client = { path = "../client", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-client = { path = "../client", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.9" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@@ -25,10 +25,10 @@ use {
|
||||
native_token::lamports_to_sol,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
stake::state::{Authorized, Lockup},
|
||||
stake_history::StakeHistoryEntry,
|
||||
transaction::{Transaction, TransactionError},
|
||||
},
|
||||
solana_stake_program::stake_state::{Authorized, Lockup},
|
||||
solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedTransaction, TransactionConfirmationStatus,
|
||||
UiTransactionStatusMeta,
|
||||
@@ -2126,9 +2126,6 @@ impl fmt::Display for CliBlock {
|
||||
if let Some(block_time) = self.encoded_confirmed_block.block_time {
|
||||
writeln!(f, "Block Time: {:?}", Local.timestamp(block_time, 0))?;
|
||||
}
|
||||
if let Some(block_height) = self.encoded_confirmed_block.block_height {
|
||||
writeln!(f, "Block Height: {:?}", block_height)?;
|
||||
}
|
||||
if !self.encoded_confirmed_block.rewards.is_empty() {
|
||||
let mut rewards = self.encoded_confirmed_block.rewards.clone();
|
||||
rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
||||
|
@@ -5,7 +5,7 @@ use {
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
|
||||
program_utils::limited_deserialize, pubkey::Pubkey, stake, transaction::Transaction,
|
||||
program_utils::limited_deserialize, pubkey::Pubkey, transaction::Transaction,
|
||||
},
|
||||
solana_transaction_status::UiTransactionStatusMeta,
|
||||
spl_memo::id as spl_memo_id,
|
||||
@@ -244,9 +244,10 @@ pub fn write_transaction<W: io::Write>(
|
||||
writeln!(w, "{} {:?}", prefix, vote_instruction)?;
|
||||
raw = false;
|
||||
}
|
||||
} else if program_pubkey == stake::program::id() {
|
||||
if let Ok(stake_instruction) =
|
||||
limited_deserialize::<stake::instruction::StakeInstruction>(&instruction.data)
|
||||
} else if program_pubkey == solana_stake_program::id() {
|
||||
if let Ok(stake_instruction) = limited_deserialize::<
|
||||
solana_stake_program::stake_instruction::StakeInstruction,
|
||||
>(&instruction.data)
|
||||
{
|
||||
writeln!(w, "{} {:?}", prefix, stake_instruction)?;
|
||||
raw = false;
|
||||
@@ -321,38 +322,7 @@ pub fn write_transaction<W: io::Write>(
|
||||
if !log_messages.is_empty() {
|
||||
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||
for log_message in log_messages {
|
||||
writeln!(w, "{} {}", prefix, log_message)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rewards) = &transaction_status.rewards {
|
||||
if !rewards.is_empty() {
|
||||
writeln!(w, "{}Rewards:", prefix,)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<15} {:<20}",
|
||||
prefix, "Address", "Type", "Amount", "New Balance"
|
||||
)?;
|
||||
for reward in rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<15} {}",
|
||||
prefix,
|
||||
reward.pubkey,
|
||||
if let Some(reward_type) = reward.reward_type {
|
||||
format!("{}", reward_type)
|
||||
} else {
|
||||
"-".to_string()
|
||||
},
|
||||
format!(
|
||||
"{}◎{:<14.9}",
|
||||
sign,
|
||||
lamports_to_sol(reward.lamports.abs() as u64)
|
||||
),
|
||||
format!("◎{:<18.9}", lamports_to_sol(reward.post_balance),)
|
||||
)?;
|
||||
writeln!(w, "{} {}", prefix, log_message,)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -16,7 +16,7 @@ chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.1"
|
||||
criterion-stats = "0.3.0"
|
||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||
console = "0.14.1"
|
||||
console = "0.11.3"
|
||||
dirs-next = "2.0.0"
|
||||
log = "0.4.11"
|
||||
Inflector = "0.11.4"
|
||||
@@ -28,29 +28,30 @@ reqwest = { version = "0.11.2", default-features = false, features = ["blocking"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.2" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.2" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.7.2" }
|
||||
solana-client = { path = "../client", version = "=1.7.2" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana_rbpf = "=0.2.11"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.9" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.6.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.6.9" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.6.9" }
|
||||
solana-client = { path = "../client", version = "=1.6.9" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.6.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana_rbpf = "=0.2.8"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.9" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "=1.7.2" }
|
||||
solana-core = { path = "../core", version = "=1.6.9" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -231,9 +231,18 @@ mod tests {
|
||||
mocks.insert(RpcRequest::GetBalance, account_balance_response);
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
|
||||
assert!(check_account_for_balance(&rpc_client, &pubkey, 1).unwrap());
|
||||
assert!(check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap());
|
||||
assert!(!check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap());
|
||||
assert_eq!(
|
||||
check_account_for_balance(&rpc_client, &pubkey, 1).unwrap(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@@ -29,11 +29,10 @@ use solana_client::{
|
||||
nonce_utils,
|
||||
rpc_client::RpcClient,
|
||||
rpc_config::{
|
||||
RpcLargestAccountsFilter, RpcSendTransactionConfig, RpcTransactionConfig,
|
||||
RpcConfirmedTransactionConfig, RpcLargestAccountsFilter, RpcSendTransactionConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_request::{RpcError, RpcResponseErrorData},
|
||||
rpc_response::{RpcKeyedAccount, RpcSimulateTransactionResult},
|
||||
rpc_response::RpcKeyedAccount,
|
||||
};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
@@ -45,15 +44,14 @@ use solana_sdk::{
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Signature, Signer, SignerError},
|
||||
stake::{
|
||||
self,
|
||||
instruction::LockupArgs,
|
||||
state::{Lockup, StakeAuthorize},
|
||||
},
|
||||
system_instruction::{self, SystemError},
|
||||
system_program,
|
||||
transaction::{Transaction, TransactionError},
|
||||
};
|
||||
use solana_stake_program::{
|
||||
stake_instruction::LockupArgs,
|
||||
stake_state::{Lockup, StakeAuthorize},
|
||||
};
|
||||
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
||||
use solana_vote_program::vote_state::VoteAuthorize;
|
||||
use std::{
|
||||
@@ -278,7 +276,6 @@ pub enum CliCommand {
|
||||
memo: Option<String>,
|
||||
fee_payer: SignerIndex,
|
||||
custodian: Option<SignerIndex>,
|
||||
no_wait: bool,
|
||||
},
|
||||
StakeSetLockup {
|
||||
stake_account_pubkey: Pubkey,
|
||||
@@ -933,7 +930,7 @@ pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
|
||||
fn resolve_derived_address_program_id(matches: &ArgMatches<'_>, arg_name: &str) -> Option<Pubkey> {
|
||||
matches.value_of(arg_name).and_then(|v| match v {
|
||||
"NONCE" => Some(system_program::id()),
|
||||
"STAKE" => Some(stake::program::id()),
|
||||
"STAKE" => Some(solana_stake_program::id()),
|
||||
"VOTE" => Some(solana_vote_program::id()),
|
||||
_ => pubkey_of(matches, arg_name),
|
||||
})
|
||||
@@ -1043,9 +1040,9 @@ fn process_confirm(
|
||||
let mut transaction = None;
|
||||
let mut get_transaction_error = None;
|
||||
if config.verbose {
|
||||
match rpc_client.get_transaction_with_config(
|
||||
match rpc_client.get_confirmed_transaction_with_config(
|
||||
signature,
|
||||
RpcTransactionConfig {
|
||||
RpcConfirmedTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
@@ -1125,7 +1122,7 @@ fn process_show_account(
|
||||
pubkey: account_pubkey.to_string(),
|
||||
account: UiAccount::encode(
|
||||
account_pubkey,
|
||||
&account,
|
||||
account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
@@ -1682,7 +1679,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
memo,
|
||||
fee_payer,
|
||||
custodian,
|
||||
no_wait,
|
||||
} => process_stake_authorize(
|
||||
&rpc_client,
|
||||
config,
|
||||
@@ -1696,7 +1692,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
*nonce_authority,
|
||||
memo.as_ref(),
|
||||
*fee_payer,
|
||||
*no_wait,
|
||||
),
|
||||
CliCommand::StakeSetLockup {
|
||||
stake_account_pubkey,
|
||||
@@ -1958,28 +1953,6 @@ where
|
||||
{
|
||||
match result {
|
||||
Err(err) => {
|
||||
// If transaction simulation returns a known Custom InstructionError, decode it
|
||||
if let ClientErrorKind::RpcError(RpcError::RpcResponseError {
|
||||
data:
|
||||
RpcResponseErrorData::SendTransactionPreflightFailure(
|
||||
RpcSimulateTransactionResult {
|
||||
err:
|
||||
Some(TransactionError::InstructionError(
|
||||
_,
|
||||
InstructionError::Custom(code),
|
||||
)),
|
||||
..
|
||||
},
|
||||
),
|
||||
..
|
||||
}) = err.kind()
|
||||
{
|
||||
if let Some(specific_error) = E::decode_custom_error_to_enum(*code) {
|
||||
return Err(specific_error.into());
|
||||
}
|
||||
}
|
||||
// If the transaction was instead submitted and returned a known Custom
|
||||
// InstructionError, decode it
|
||||
if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
|
||||
_,
|
||||
InstructionError::Custom(code),
|
||||
@@ -2323,7 +2296,10 @@ mod tests {
|
||||
let default_keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
|
||||
|
||||
let default_signer = DefaultSigner::new("keypair", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
arg_name: "keypair".to_string(),
|
||||
path: default_keypair_file,
|
||||
};
|
||||
|
||||
let signer_info = default_signer
|
||||
.generate_unique_signers(vec![], &matches, &mut None)
|
||||
@@ -2401,7 +2377,10 @@ mod tests {
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let keypair = read_keypair_file(&keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
// Test Airdrop Subcommand
|
||||
let test_airdrop =
|
||||
test_commands
|
||||
@@ -2488,7 +2467,7 @@ mod tests {
|
||||
let from_pubkey = Some(solana_sdk::pubkey::new_rand());
|
||||
let from_str = from_pubkey.unwrap().to_string();
|
||||
for (name, program_id) in &[
|
||||
("STAKE", stake::program::id()),
|
||||
("STAKE", solana_stake_program::id()),
|
||||
("VOTE", solana_vote_program::id()),
|
||||
("NONCE", system_program::id()),
|
||||
] {
|
||||
@@ -2524,7 +2503,7 @@ mod tests {
|
||||
command: CliCommand::CreateAddressWithSeed {
|
||||
from_pubkey: None,
|
||||
seed: "seed".to_string(),
|
||||
program_id: stake::program::id(),
|
||||
program_id: solana_stake_program::id(),
|
||||
},
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2787,11 +2766,11 @@ mod tests {
|
||||
config.command = CliCommand::CreateAddressWithSeed {
|
||||
from_pubkey: Some(from_pubkey),
|
||||
seed: "seed".to_string(),
|
||||
program_id: stake::program::id(),
|
||||
program_id: solana_stake_program::id(),
|
||||
};
|
||||
let address = process_command(&config);
|
||||
let expected_address =
|
||||
Pubkey::create_with_seed(&from_pubkey, "seed", &stake::program::id()).unwrap();
|
||||
Pubkey::create_with_seed(&from_pubkey, "seed", &solana_stake_program::id()).unwrap();
|
||||
assert_eq!(address.unwrap(), expected_address.to_string());
|
||||
|
||||
// Need airdrop cases
|
||||
@@ -2929,7 +2908,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let default_keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
//Test Transfer Subcommand, SOL
|
||||
let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
@@ -3178,7 +3160,7 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: Some(derived_address_seed),
|
||||
derived_address_program_id: Some(stake::program::id()),
|
||||
derived_address_program_id: Some(solana_stake_program::id()),
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||
}
|
||||
|
@@ -24,9 +24,9 @@ use solana_client::{
|
||||
pubsub_client::PubsubClient,
|
||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
||||
rpc_config::{
|
||||
RpcAccountInfoConfig, RpcBlockConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
|
||||
RpcProgramAccountsConfig, RpcTransactionConfig, RpcTransactionLogsConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
RpcAccountInfoConfig, RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
|
||||
RpcLargestAccountsConfig, RpcLargestAccountsFilter, RpcProgramAccountsConfig,
|
||||
RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_filter,
|
||||
rpc_response::SlotInfo,
|
||||
@@ -46,9 +46,7 @@ use solana_sdk::{
|
||||
rent::Rent,
|
||||
rpc_port::DEFAULT_RPC_PORT_STR,
|
||||
signature::Signature,
|
||||
slot_history,
|
||||
stake::{self, state::StakeState},
|
||||
system_instruction, system_program,
|
||||
slot_history, system_instruction, system_program,
|
||||
sysvar::{
|
||||
self,
|
||||
slot_history::SlotHistory,
|
||||
@@ -57,6 +55,7 @@ use solana_sdk::{
|
||||
timing,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_program::stake_state::StakeState;
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{
|
||||
@@ -1028,12 +1027,12 @@ pub fn process_get_block(
|
||||
};
|
||||
|
||||
let encoded_confirmed_block = rpc_client
|
||||
.get_block_with_config(
|
||||
.get_confirmed_block_with_config(
|
||||
slot,
|
||||
RpcBlockConfig {
|
||||
RpcConfirmedBlockConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
..RpcBlockConfig::default()
|
||||
..RpcConfirmedBlockConfig::default()
|
||||
},
|
||||
)?
|
||||
.into();
|
||||
@@ -1094,8 +1093,8 @@ pub fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessR
|
||||
}
|
||||
|
||||
pub fn process_get_block_height(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
let block_height = rpc_client.get_block_height()?;
|
||||
Ok(block_height.to_string())
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
Ok(epoch_info.block_height.to_string())
|
||||
}
|
||||
|
||||
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
@@ -1185,7 +1184,7 @@ pub fn process_show_block_production(
|
||||
start_slot = minimum_ledger_slot;
|
||||
}
|
||||
|
||||
let confirmed_blocks = rpc_client.get_blocks(start_slot, Some(end_slot))?;
|
||||
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
|
||||
(confirmed_blocks, start_slot)
|
||||
};
|
||||
|
||||
@@ -1677,11 +1676,11 @@ pub fn process_show_stakes(
|
||||
progress_bar.set_message("Fetching stake accounts...");
|
||||
|
||||
let mut program_accounts_config = RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
};
|
||||
|
||||
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
|
||||
@@ -1708,7 +1707,7 @@ pub fn process_show_stakes(
|
||||
}
|
||||
}
|
||||
let all_stake_accounts = rpc_client
|
||||
.get_program_accounts_with_config(&stake::program::id(), program_accounts_config)?;
|
||||
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
|
||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
||||
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
||||
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
|
||||
@@ -1915,7 +1914,7 @@ pub fn process_transaction_history(
|
||||
limit: usize,
|
||||
show_transactions: bool,
|
||||
) -> ProcessResult {
|
||||
let results = rpc_client.get_signatures_for_address_with_config(
|
||||
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
|
||||
address,
|
||||
GetConfirmedSignaturesForAddress2Config {
|
||||
before,
|
||||
@@ -1954,9 +1953,9 @@ pub fn process_transaction_history(
|
||||
|
||||
if show_transactions {
|
||||
if let Ok(signature) = result.signature.parse::<Signature>() {
|
||||
match rpc_client.get_transaction_with_config(
|
||||
match rpc_client.get_confirmed_transaction_with_config(
|
||||
&signature,
|
||||
RpcTransactionConfig {
|
||||
RpcConfirmedTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
@@ -2099,7 +2098,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file,
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_cluster_version = test_commands
|
||||
.clone()
|
||||
|
@@ -10,6 +10,7 @@ macro_rules! ACCOUNT_STRING {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
macro_rules! pubkey {
|
||||
($arg:expr, $help:expr) => {
|
||||
$arg.takes_value(true)
|
||||
|
@@ -179,7 +179,10 @@ pub fn parse_args<'a>(
|
||||
&config.keypair_path,
|
||||
);
|
||||
|
||||
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
|
||||
let default_signer = DefaultSigner {
|
||||
arg_name: default_signer_arg_name,
|
||||
path: default_signer_path.clone(),
|
||||
};
|
||||
|
||||
let CliCommandInfo {
|
||||
command,
|
||||
|
@@ -596,7 +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::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let nonce_account_keypair = Keypair::new();
|
||||
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
@@ -150,7 +150,7 @@ impl ProgramSubCommands for App<'_, '_> {
|
||||
pubkey!(Arg::with_name("program_id")
|
||||
.long("program-id")
|
||||
.value_name("PROGRAM_ID"),
|
||||
"Executable program's address, must be a keypair for initial deploys, can be a pubkey for upgrades \
|
||||
"Executable program's address, must be a signer for initial deploys, can be a pubkey for upgrades \
|
||||
[default: address of keypair at /path/to/program-keypair.json if present, otherwise a random address]"),
|
||||
)
|
||||
.arg(
|
||||
@@ -886,11 +886,6 @@ fn process_program_deploy(
|
||||
)?;
|
||||
|
||||
let result = if do_deploy {
|
||||
if program_signer.is_none() {
|
||||
return Err(
|
||||
"Initial deployments require a keypair be provided for the program id".into(),
|
||||
);
|
||||
}
|
||||
do_process_program_write_and_deploy(
|
||||
rpc_client.clone(),
|
||||
config,
|
||||
@@ -1107,7 +1102,6 @@ fn get_buffers(
|
||||
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)?;
|
||||
Ok(results)
|
||||
@@ -1412,7 +1406,6 @@ fn process_close(
|
||||
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -2136,7 +2129,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -2344,7 +2340,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
// defaults
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
@@ -2492,7 +2491,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
let program_pubkey = Pubkey::new_unique();
|
||||
let new_authority_pubkey = Pubkey::new_unique();
|
||||
@@ -2600,7 +2602,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
let new_authority_pubkey = Pubkey::new_unique();
|
||||
@@ -2657,7 +2662,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file,
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
// defaults
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
@@ -2756,7 +2764,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
// defaults
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
|
@@ -36,11 +36,6 @@ use solana_sdk::{
|
||||
feature, feature_set,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
stake::{
|
||||
self,
|
||||
instruction::{self as stake_instruction, LockupArgs, StakeError},
|
||||
state::{Authorized, Lockup, Meta, StakeAuthorize, StakeState},
|
||||
},
|
||||
system_instruction::SystemError,
|
||||
sysvar::{
|
||||
clock,
|
||||
@@ -48,6 +43,10 @@ use solana_sdk::{
|
||||
},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_program::{
|
||||
stake_instruction::{self, LockupArgs, StakeError},
|
||||
stake_state::{Authorized, Lockup, Meta, StakeAuthorize, StakeState},
|
||||
};
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
@@ -239,12 +238,6 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.nonce_args(false)
|
||||
.arg(fee_payer_arg())
|
||||
.arg(custodian_arg())
|
||||
.arg(
|
||||
Arg::with_name("no_wait")
|
||||
.long("no-wait")
|
||||
.takes_value(false)
|
||||
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deactivate-stake")
|
||||
@@ -634,7 +627,6 @@ pub fn parse_stake_authorize(
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
||||
let (custodian, custodian_pubkey) = signer_of(matches, "custodian", wallet_manager)?;
|
||||
let no_wait = matches.is_present("no_wait");
|
||||
|
||||
bulk_signers.push(fee_payer);
|
||||
if nonce_account.is_some() {
|
||||
@@ -671,7 +663,6 @@ pub fn parse_stake_authorize(
|
||||
memo,
|
||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
||||
custodian: custodian_pubkey.and_then(|_| signer_info.index_of(custodian_pubkey)),
|
||||
no_wait,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -972,7 +963,7 @@ pub fn process_create_stake_account(
|
||||
) -> ProcessResult {
|
||||
let stake_account = config.signers[stake_account];
|
||||
let stake_account_address = if let Some(seed) = seed {
|
||||
Pubkey::create_with_seed(&stake_account.pubkey(), &seed, &stake::program::id())?
|
||||
Pubkey::create_with_seed(&stake_account.pubkey(), &seed, &solana_stake_program::id())?
|
||||
} else {
|
||||
stake_account.pubkey()
|
||||
};
|
||||
@@ -1040,7 +1031,7 @@ pub fn process_create_stake_account(
|
||||
|
||||
if !sign_only {
|
||||
if let Ok(stake_account) = rpc_client.get_account(&stake_account_address) {
|
||||
let err_msg = if stake_account.owner == stake::program::id() {
|
||||
let err_msg = if stake_account.owner == solana_stake_program::id() {
|
||||
format!("Stake account {} already exists", stake_account_address)
|
||||
} else {
|
||||
format!(
|
||||
@@ -1103,7 +1094,6 @@ pub fn process_stake_authorize(
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
fee_payer: SignerIndex,
|
||||
no_wait: bool,
|
||||
) -> ProcessResult {
|
||||
let mut ixs = Vec::new();
|
||||
let custodian = custodian.map(|index| config.signers[index]);
|
||||
@@ -1167,11 +1157,7 @@ pub fn process_stake_authorize(
|
||||
&tx.message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let result = if no_wait {
|
||||
rpc_client.send_transaction(&tx)
|
||||
} else {
|
||||
rpc_client.send_and_confirm_transaction_with_spinner(&tx)
|
||||
};
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1196,7 +1182,7 @@ pub fn process_deactivate_stake_account(
|
||||
let stake_authority = config.signers[stake_authority];
|
||||
|
||||
let stake_account_address = if let Some(seed) = seed {
|
||||
Pubkey::create_with_seed(&stake_account_pubkey, seed, &stake::program::id())?
|
||||
Pubkey::create_with_seed(&stake_account_pubkey, seed, &solana_stake_program::id())?
|
||||
} else {
|
||||
*stake_account_pubkey
|
||||
};
|
||||
@@ -1274,7 +1260,7 @@ pub fn process_withdraw_stake(
|
||||
let custodian = custodian.map(|index| config.signers[index]);
|
||||
|
||||
let stake_account_address = if let Some(seed) = seed {
|
||||
Pubkey::create_with_seed(&stake_account_pubkey, seed, &stake::program::id())?
|
||||
Pubkey::create_with_seed(&stake_account_pubkey, seed, &solana_stake_program::id())?
|
||||
} else {
|
||||
*stake_account_pubkey
|
||||
};
|
||||
@@ -1395,14 +1381,18 @@ pub fn process_split_stake(
|
||||
let stake_authority = config.signers[stake_authority];
|
||||
|
||||
let split_stake_account_address = if let Some(seed) = split_stake_account_seed {
|
||||
Pubkey::create_with_seed(&split_stake_account.pubkey(), &seed, &stake::program::id())?
|
||||
Pubkey::create_with_seed(
|
||||
&split_stake_account.pubkey(),
|
||||
&seed,
|
||||
&solana_stake_program::id(),
|
||||
)?
|
||||
} else {
|
||||
split_stake_account.pubkey()
|
||||
};
|
||||
|
||||
if !sign_only {
|
||||
if let Ok(stake_account) = rpc_client.get_account(&split_stake_account_address) {
|
||||
let err_msg = if stake_account.owner == stake::program::id() {
|
||||
let err_msg = if stake_account.owner == solana_stake_program::id() {
|
||||
format!(
|
||||
"Stake account {} already exists",
|
||||
split_stake_account_address
|
||||
@@ -1537,7 +1527,7 @@ pub fn process_merge_stake(
|
||||
if !sign_only {
|
||||
for stake_account_address in &[stake_account_pubkey, source_stake_account_pubkey] {
|
||||
if let Ok(stake_account) = rpc_client.get_account(stake_account_address) {
|
||||
if stake_account.owner != stake::program::id() {
|
||||
if stake_account.owner != solana_stake_program::id() {
|
||||
return Err(CliError::BadParameter(format!(
|
||||
"Account {} is not a stake account",
|
||||
stake_account_address
|
||||
@@ -1873,7 +1863,7 @@ pub fn process_show_stake_account(
|
||||
with_rewards: Option<usize>,
|
||||
) -> ProcessResult {
|
||||
let stake_account = rpc_client.get_account(stake_account_address)?;
|
||||
if stake_account.owner != stake::program::id() {
|
||||
if stake_account.owner != solana_stake_program::id() {
|
||||
return Err(CliError::RpcRequestError(format!(
|
||||
"{:?} is not a stake account",
|
||||
stake_account_address,
|
||||
@@ -2114,7 +2104,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let stake_account_keypair = Keypair::new();
|
||||
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
@@ -2158,7 +2151,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||
},
|
||||
@@ -2196,7 +2188,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2238,7 +2229,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2269,7 +2259,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||
},
|
||||
@@ -2297,7 +2286,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2331,7 +2319,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2366,7 +2353,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||
},
|
||||
@@ -2398,7 +2384,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2409,35 +2394,6 @@ mod tests {
|
||||
},
|
||||
);
|
||||
|
||||
// Test Authorize Subcommand w/ no-wait
|
||||
let test_authorize = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"stake-authorize",
|
||||
&stake_account_string,
|
||||
"--new-stake-authority",
|
||||
&stake_account_string,
|
||||
"--no-wait",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: true,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
);
|
||||
|
||||
// Test Authorize Subcommand w/ sign-only
|
||||
let blockhash = Hash::default();
|
||||
let blockhash_string = format!("{}", blockhash);
|
||||
@@ -2465,7 +2421,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2505,7 +2460,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2555,7 +2509,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2591,7 +2544,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2632,7 +2584,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2669,7 +2620,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -2710,7 +2660,6 @@ mod tests {
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
|
@@ -826,7 +826,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
|
@@ -72,7 +72,7 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
let account0 = rpc_client.get_account(&program_id).unwrap();
|
||||
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account0.owner, bpf_loader::id());
|
||||
assert!(account0.executable);
|
||||
assert_eq!(account0.executable, true);
|
||||
let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap();
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
@@ -93,7 +93,7 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
.unwrap();
|
||||
assert_eq!(account1.lamports, minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account1.owner, bpf_loader::id());
|
||||
assert!(account1.executable);
|
||||
assert_eq!(account1.executable, true);
|
||||
assert_eq!(account1.data, account0.data);
|
||||
|
||||
// Attempt to redeploy to the same address
|
||||
@@ -129,7 +129,7 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
.unwrap();
|
||||
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account2.owner, bpf_loader::id());
|
||||
assert!(account2.executable);
|
||||
assert_eq!(account2.executable, true);
|
||||
assert_eq!(account2.data, account0.data);
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_keypair.pubkey()).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) = Pubkey::find_program_address(
|
||||
&[program_keypair.pubkey().as_ref()],
|
||||
&bpf_loader_upgradeable::id(),
|
||||
@@ -300,7 +300,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -332,7 +332,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
|
||||
@@ -341,7 +341,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -364,7 +364,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
|
||||
@@ -373,7 +373,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -418,7 +418,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
|
||||
@@ -427,7 +427,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
|
@@ -17,11 +17,10 @@ use solana_sdk::{
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
stake::{
|
||||
self,
|
||||
instruction::LockupArgs,
|
||||
state::{Lockup, StakeAuthorize, StakeState},
|
||||
},
|
||||
};
|
||||
use solana_stake_program::{
|
||||
stake_instruction::LockupArgs,
|
||||
stake_state::{Lockup, StakeAuthorize, StakeState},
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -140,7 +139,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
let stake_address = Pubkey::create_with_seed(
|
||||
&config_validator.signers[0].pubkey(),
|
||||
"hi there",
|
||||
&stake::program::id(),
|
||||
&solana_stake_program::id(),
|
||||
)
|
||||
.expect("bad seed");
|
||||
|
||||
@@ -609,7 +608,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -640,7 +638,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -666,7 +663,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -692,7 +688,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
@@ -711,7 +706,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -762,7 +756,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
let sign_only = parse_sign_only_reply_string(&sign_reply);
|
||||
@@ -785,7 +778,6 @@ fn test_stake_authorize() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -889,7 +881,6 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config` balance has not changed, despite submitting the TX
|
||||
@@ -911,7 +902,6 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
@@ -930,7 +920,6 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config`'s balance again has not changed
|
||||
@@ -1134,7 +1123,7 @@ fn test_stake_set_lockup() {
|
||||
stake_account: 1,
|
||||
seed: None,
|
||||
staker: Some(offline_pubkey),
|
||||
withdrawer: Some(config.signers[0].pubkey()),
|
||||
withdrawer: Some(offline_pubkey),
|
||||
lockup,
|
||||
amount: SpendAmount::Some(10 * minimum_stake_balance),
|
||||
sign_only: false,
|
||||
@@ -1558,6 +1547,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let seed_address =
|
||||
Pubkey::create_with_seed(&stake_pubkey, seed, &stake::program::id()).unwrap();
|
||||
Pubkey::create_with_seed(&stake_pubkey, seed, &solana_stake_program::id()).unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &seed_address);
|
||||
}
|
||||
|
@@ -16,7 +16,6 @@ use solana_sdk::{
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
|
||||
stake,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -514,7 +513,7 @@ fn test_transfer_with_seed() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
let derived_address_seed = "seed".to_string();
|
||||
let derived_address_program_id = stake::program::id();
|
||||
let derived_address_program_id = solana_stake_program::id();
|
||||
let derived_address = Pubkey::create_with_seed(
|
||||
&sender_pubkey,
|
||||
&derived_address_seed,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -24,14 +24,14 @@ semver = "0.11.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.9" }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tungstenite = "0.10.1"
|
||||
@@ -40,7 +40,7 @@ url = "2.1.1"
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -361,7 +361,7 @@ mod tests {
|
||||
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
||||
let rpc_nonce_account = UiAccount::encode(
|
||||
&nonce_pubkey,
|
||||
&nonce_account,
|
||||
nonce_account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
|
@@ -13,7 +13,6 @@ pub mod rpc_cache;
|
||||
pub mod rpc_client;
|
||||
pub mod rpc_config;
|
||||
pub mod rpc_custom_error;
|
||||
pub mod rpc_deprecated_config;
|
||||
pub mod rpc_filter;
|
||||
pub mod rpc_request;
|
||||
pub mod rpc_response;
|
||||
|
@@ -1,8 +1,3 @@
|
||||
#[allow(deprecated)]
|
||||
use crate::rpc_deprecated_config::{
|
||||
RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
|
||||
RpcGetConfirmedSignaturesForAddress2Config,
|
||||
};
|
||||
use {
|
||||
crate::{
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
@@ -182,23 +177,6 @@ impl RpcClient {
|
||||
Ok(requested_commitment)
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn maybe_map_request(&self, mut request: RpcRequest) -> Result<RpcRequest, RpcError> {
|
||||
if self.get_node_version()? < semver::Version::new(1, 7, 0) {
|
||||
request = match request {
|
||||
RpcRequest::GetBlock => RpcRequest::GetConfirmedBlock,
|
||||
RpcRequest::GetBlocks => RpcRequest::GetConfirmedBlocks,
|
||||
RpcRequest::GetBlocksWithLimit => RpcRequest::GetConfirmedBlocksWithLimit,
|
||||
RpcRequest::GetSignaturesForAddress => {
|
||||
RpcRequest::GetConfirmedSignaturesForAddress2
|
||||
}
|
||||
RpcRequest::GetTransaction => RpcRequest::GetConfirmedTransaction,
|
||||
_ => request,
|
||||
};
|
||||
}
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
|
||||
Ok(self
|
||||
.confirm_transaction_with_commitment(signature, self.commitment_config)?
|
||||
@@ -426,20 +404,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_block_height(&self) -> ClientResult<u64> {
|
||||
self.get_block_height_with_commitment(self.commitment_config)
|
||||
}
|
||||
|
||||
pub fn get_block_height_with_commitment(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<u64> {
|
||||
self.send(
|
||||
RpcRequest::GetBlockHeight,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_slot_leaders(&self, start_slot: Slot, limit: u64) -> ClientResult<Vec<Pubkey>> {
|
||||
self.send(RpcRequest::GetSlotLeaders, json!([start_slot, limit]))
|
||||
.and_then(|slot_leaders: Vec<String>| {
|
||||
@@ -501,6 +465,27 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.5.19", note = "Please use RpcClient::supply() instead")]
|
||||
#[allow(deprecated)]
|
||||
pub fn total_supply(&self) -> ClientResult<u64> {
|
||||
self.total_supply_with_commitment(self.commitment_config)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.5.19",
|
||||
note = "Please use RpcClient::supply_with_commitment() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn total_supply_with_commitment(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<u64> {
|
||||
self.send(
|
||||
RpcRequest::GetTotalSupply,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_largest_accounts_with_config(
|
||||
&self,
|
||||
config: RpcLargestAccountsConfig,
|
||||
@@ -571,43 +556,10 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetClusterNodes, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
|
||||
self.get_block_with_encoding(slot, UiTransactionEncoding::Json)
|
||||
}
|
||||
|
||||
pub fn get_block_with_encoding(
|
||||
&self,
|
||||
slot: Slot,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<EncodedConfirmedBlock> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlock)?,
|
||||
json!([slot, encoding]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_block_with_config(
|
||||
&self,
|
||||
slot: Slot,
|
||||
config: RpcBlockConfig,
|
||||
) -> ClientResult<UiConfirmedBlock> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlock)?,
|
||||
json!([slot, config]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcClient::get_block() instead")]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
|
||||
self.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Json)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_block_with_encoding() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_block_with_encoding(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@@ -616,11 +568,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_block_with_config() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_block_with_config(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@@ -629,56 +576,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, config]))
|
||||
}
|
||||
|
||||
pub fn get_blocks(&self, start_slot: Slot, end_slot: Option<Slot>) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlocks)?,
|
||||
json!([start_slot, end_slot]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_blocks_with_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
end_slot: Option<Slot>,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
let json = if end_slot.is_some() {
|
||||
json!([
|
||||
start_slot,
|
||||
end_slot,
|
||||
self.maybe_map_commitment(commitment_config)?
|
||||
])
|
||||
} else {
|
||||
json!([start_slot, self.maybe_map_commitment(commitment_config)?])
|
||||
};
|
||||
self.send(self.maybe_map_request(RpcRequest::GetBlocks)?, json)
|
||||
}
|
||||
|
||||
pub fn get_blocks_with_limit(&self, start_slot: Slot, limit: usize) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlocksWithLimit)?,
|
||||
json!([start_slot, limit]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_blocks_with_limit_and_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
limit: usize,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlocksWithLimit)?,
|
||||
json!([
|
||||
start_slot,
|
||||
limit,
|
||||
self.maybe_map_commitment(commitment_config)?
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcClient::get_blocks() instead")]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -690,11 +587,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_blocks_with_commitment() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks_with_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -713,11 +605,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlocks, json)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_blocks_with_limit() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks_with_limit(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -729,11 +616,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_blocks_with_limit_and_commitment() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks_with_limit_and_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -750,41 +632,33 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_signatures_for_address(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
|
||||
self.get_signatures_for_address_with_config(
|
||||
address,
|
||||
GetConfirmedSignaturesForAddress2Config::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_signatures_for_address_with_config(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
config: GetConfirmedSignaturesForAddress2Config,
|
||||
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
|
||||
let config = RpcSignaturesForAddressConfig {
|
||||
before: config.before.map(|signature| signature.to_string()),
|
||||
until: config.until.map(|signature| signature.to_string()),
|
||||
limit: config.limit,
|
||||
commitment: config.commitment,
|
||||
};
|
||||
|
||||
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
|
||||
self.maybe_map_request(RpcRequest::GetSignaturesForAddress)?,
|
||||
json!([address.to_string(), config]),
|
||||
)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_signatures_for_address() instead"
|
||||
since = "1.5.19",
|
||||
note = "Please use RpcClient::get_confirmed_signatures_for_address2() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_signatures_for_address(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
start_slot: Slot,
|
||||
end_slot: Slot,
|
||||
) -> ClientResult<Vec<Signature>> {
|
||||
let signatures_base58_str: Vec<String> = self.send(
|
||||
RpcRequest::GetConfirmedSignaturesForAddress,
|
||||
json!([address.to_string(), start_slot, end_slot]),
|
||||
)?;
|
||||
|
||||
let mut signatures = vec![];
|
||||
for signature_base58_str in signatures_base58_str {
|
||||
signatures.push(
|
||||
signature_base58_str.parse::<Signature>().map_err(|err| {
|
||||
Into::<ClientError>::into(RpcError::ParseError(err.to_string()))
|
||||
})?,
|
||||
);
|
||||
}
|
||||
Ok(signatures)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_signatures_for_address2(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
@@ -795,11 +669,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_signatures_for_address_with_config() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_signatures_for_address2_with_config(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
@@ -820,33 +689,6 @@ impl RpcClient {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn get_transaction(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<EncodedConfirmedTransaction> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetTransaction)?,
|
||||
json!([signature.to_string(), encoding]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_transaction_with_config(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
config: RpcTransactionConfig,
|
||||
) -> ClientResult<EncodedConfirmedTransaction> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetTransaction)?,
|
||||
json!([signature.to_string(), config]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_transaction() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_transaction(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
@@ -858,11 +700,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_transaction_with_config() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_transaction_with_config(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
@@ -1056,7 +893,7 @@ impl RpcClient {
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<Account>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
data_slice: None,
|
||||
};
|
||||
@@ -1111,7 +948,7 @@ impl RpcClient {
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Vec<Option<Account>>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
data_slice: None,
|
||||
};
|
||||
@@ -1177,11 +1014,12 @@ impl RpcClient {
|
||||
self.get_program_accounts_with_config(
|
||||
pubkey,
|
||||
RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.commitment_config),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1191,10 +1029,7 @@ impl RpcClient {
|
||||
pubkey: &Pubkey,
|
||||
config: RpcProgramAccountsConfig,
|
||||
) -> ClientResult<Vec<(Pubkey, Account)>> {
|
||||
let commitment = config
|
||||
.account_config
|
||||
.commitment
|
||||
.unwrap_or_else(|| self.commitment());
|
||||
let commitment = config.account_config.commitment.unwrap_or_default();
|
||||
let commitment = self.maybe_map_commitment(commitment)?;
|
||||
let account_config = RpcAccountInfoConfig {
|
||||
commitment: Some(commitment),
|
||||
@@ -1244,7 +1079,6 @@ impl RpcClient {
|
||||
blockhash,
|
||||
fee_calculator,
|
||||
last_valid_slot,
|
||||
..
|
||||
},
|
||||
}) = self
|
||||
.send::<Response<RpcFees>>(
|
||||
@@ -1252,19 +1086,6 @@ impl RpcClient {
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
) {
|
||||
(context, blockhash, fee_calculator, last_valid_slot)
|
||||
} else if let Ok(Response {
|
||||
context,
|
||||
value:
|
||||
DeprecatedRpcFees {
|
||||
blockhash,
|
||||
fee_calculator,
|
||||
last_valid_slot,
|
||||
},
|
||||
}) = self.send::<Response<DeprecatedRpcFees>>(
|
||||
RpcRequest::GetFees,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
) {
|
||||
(context, blockhash, fee_calculator, last_valid_slot)
|
||||
} else if let Ok(Response {
|
||||
context,
|
||||
value:
|
||||
@@ -2062,7 +1883,7 @@ mod tests {
|
||||
// Send erroneous parameter
|
||||
let blockhash: ClientResult<String> =
|
||||
rpc_client.send(RpcRequest::GetRecentBlockhash, json!(["parameter"]));
|
||||
assert!(blockhash.is_err());
|
||||
assert_eq!(blockhash.is_err(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@@ -23,24 +23,14 @@ pub struct RpcSendTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionAccountsConfig {
|
||||
pub encoding: Option<UiAccountEncoding>,
|
||||
pub addresses: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub sig_verify: bool,
|
||||
#[serde(default)]
|
||||
pub replace_recent_blockhash: bool,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub accounts: Option<RpcSimulateTransactionAccountsConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -137,7 +127,6 @@ pub struct RpcProgramAccountsConfig {
|
||||
pub filters: Option<Vec<RpcFilterType>>,
|
||||
#[serde(flatten)]
|
||||
pub account_config: RpcAccountInfoConfig,
|
||||
pub with_context: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -172,7 +161,7 @@ pub struct RpcSignatureSubscribeConfig {
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSignaturesForAddressConfig {
|
||||
pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<String>, // Signature as base-58 string
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
@@ -194,17 +183,6 @@ impl<T: EncodingConfig + Default + Copy> RpcEncodingConfigWrapper<T> {
|
||||
RpcEncodingConfigWrapper::Current(config) => config.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert<U: EncodingConfig + From<T>>(&self) -> RpcEncodingConfigWrapper<U> {
|
||||
match self {
|
||||
RpcEncodingConfigWrapper::Deprecated(encoding) => {
|
||||
RpcEncodingConfigWrapper::Deprecated(*encoding)
|
||||
}
|
||||
RpcEncodingConfigWrapper::Current(config) => {
|
||||
RpcEncodingConfigWrapper::Current(config.map(|config| config.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EncodingConfig {
|
||||
@@ -213,7 +191,7 @@ pub trait EncodingConfig {
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockConfig {
|
||||
pub struct RpcConfirmedBlockConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub transaction_details: Option<TransactionDetails>,
|
||||
pub rewards: Option<bool>,
|
||||
@@ -221,7 +199,7 @@ pub struct RpcBlockConfig {
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcBlockConfig {
|
||||
impl EncodingConfig for RpcConfirmedBlockConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
@@ -230,7 +208,7 @@ impl EncodingConfig for RpcBlockConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcBlockConfig {
|
||||
impl RpcConfirmedBlockConfig {
|
||||
pub fn rewards_only() -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
@@ -247,21 +225,21 @@ impl RpcBlockConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcBlockConfig> for RpcEncodingConfigWrapper<RpcBlockConfig> {
|
||||
fn from(config: RpcBlockConfig) -> Self {
|
||||
impl From<RpcConfirmedBlockConfig> for RpcEncodingConfigWrapper<RpcConfirmedBlockConfig> {
|
||||
fn from(config: RpcConfirmedBlockConfig) -> Self {
|
||||
RpcEncodingConfigWrapper::Current(Some(config))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcTransactionConfig {
|
||||
pub struct RpcConfirmedTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcTransactionConfig {
|
||||
impl EncodingConfig for RpcConfirmedTransactionConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
@@ -272,16 +250,16 @@ impl EncodingConfig for RpcTransactionConfig {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcBlocksConfigWrapper {
|
||||
pub enum RpcConfirmedBlocksConfigWrapper {
|
||||
EndSlotOnly(Option<Slot>),
|
||||
CommitmentOnly(Option<CommitmentConfig>),
|
||||
}
|
||||
|
||||
impl RpcBlocksConfigWrapper {
|
||||
impl RpcConfirmedBlocksConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<CommitmentConfig>) {
|
||||
match &self {
|
||||
RpcBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
RpcConfirmedBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcConfirmedBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,6 @@ pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007;
|
||||
pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008;
|
||||
pub const JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: i64 = -32009;
|
||||
pub const JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: i64 = -32010;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
|
||||
|
||||
pub enum RpcCustomError {
|
||||
BlockCleanedUp {
|
||||
@@ -45,7 +44,6 @@ pub enum RpcCustomError {
|
||||
KeyExcludedFromSecondaryIndex {
|
||||
index_key: String,
|
||||
},
|
||||
TransactionHistoryNotAvailable,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -134,13 +132,6 @@ impl From<RpcCustomError> for Error {
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::TransactionHistoryNotAvailable => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,
|
||||
),
|
||||
message: "Transaction history is not available from this node".to_string(),
|
||||
data: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,120 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
use {
|
||||
crate::rpc_config::{
|
||||
EncodingConfig, RpcBlockConfig, RpcEncodingConfigWrapper, RpcTransactionConfig,
|
||||
},
|
||||
solana_sdk::{clock::Slot, commitment_config::CommitmentConfig},
|
||||
solana_transaction_status::{TransactionDetails, UiTransactionEncoding},
|
||||
};
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcSignaturesForAddressConfig instead"
|
||||
)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<String>, // Signature as base-58 string
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcBlockConfig instead")]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedBlockConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub transaction_details: Option<TransactionDetails>,
|
||||
pub rewards: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedBlockConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcConfirmedBlockConfig {
|
||||
pub fn rewards_only() -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewards_with_commitment(commitment: Option<CommitmentConfig>) -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
commitment,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedBlockConfig> for RpcEncodingConfigWrapper<RpcConfirmedBlockConfig> {
|
||||
fn from(config: RpcConfirmedBlockConfig) -> Self {
|
||||
RpcEncodingConfigWrapper::Current(Some(config))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedBlockConfig> for RpcBlockConfig {
|
||||
fn from(config: RpcConfirmedBlockConfig) -> Self {
|
||||
Self {
|
||||
encoding: config.encoding,
|
||||
transaction_details: config.transaction_details,
|
||||
rewards: config.rewards,
|
||||
commitment: config.commitment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcTransactionConfig instead")]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedTransactionConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedTransactionConfig> for RpcTransactionConfig {
|
||||
fn from(config: RpcConfirmedTransactionConfig) -> Self {
|
||||
Self {
|
||||
encoding: config.encoding,
|
||||
commitment: config.commitment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcBlocksConfigWrapper instead")]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcConfirmedBlocksConfigWrapper {
|
||||
EndSlotOnly(Option<Slot>),
|
||||
CommitmentOnly(Option<CommitmentConfig>),
|
||||
}
|
||||
|
||||
impl RpcConfirmedBlocksConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<CommitmentConfig>) {
|
||||
match &self {
|
||||
RpcConfirmedBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcConfirmedBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,34 +11,21 @@ pub enum RpcRequest {
|
||||
DeregisterNode,
|
||||
GetAccountInfo,
|
||||
GetBalance,
|
||||
GetBlock,
|
||||
GetBlockHeight,
|
||||
GetBlockProduction,
|
||||
GetBlocks,
|
||||
GetBlocksWithLimit,
|
||||
GetBlockTime,
|
||||
GetClusterNodes,
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlock instead")]
|
||||
GetConfirmedBlock,
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlocks instead")]
|
||||
GetConfirmedBlocks,
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcRequest::GetBlocksWithLimit instead"
|
||||
)]
|
||||
GetConfirmedBlocksWithLimit,
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcRequest::GetSignaturesForAddress instead"
|
||||
)]
|
||||
GetConfirmedSignaturesForAddress2,
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcRequest::GetTransaction instead"
|
||||
)]
|
||||
GetConfirmedTransaction,
|
||||
|
||||
#[deprecated(
|
||||
since = "1.5.19",
|
||||
note = "Please use RpcRequest::GetConfirmedSignaturesForAddress2 instead"
|
||||
)]
|
||||
GetConfirmedSignaturesForAddress,
|
||||
|
||||
GetConfirmedSignaturesForAddress2,
|
||||
GetConfirmedTransaction,
|
||||
GetEpochInfo,
|
||||
GetEpochSchedule,
|
||||
GetFeeCalculatorForBlockhash,
|
||||
@@ -61,7 +48,6 @@ pub enum RpcRequest {
|
||||
GetRecentBlockhash,
|
||||
GetRecentPerformanceSamples,
|
||||
GetSnapshotSlot,
|
||||
GetSignaturesForAddress,
|
||||
GetSignatureStatuses,
|
||||
GetSlot,
|
||||
GetSlotLeader,
|
||||
@@ -76,7 +62,10 @@ pub enum RpcRequest {
|
||||
GetTokenAccountsByDelegate,
|
||||
GetTokenAccountsByOwner,
|
||||
GetTokenSupply,
|
||||
GetTransaction,
|
||||
|
||||
#[deprecated(since = "1.5.19", note = "Please use RpcRequest::GetSupply instead")]
|
||||
GetTotalSupply,
|
||||
|
||||
GetTransactionCount,
|
||||
GetVersion,
|
||||
GetVoteAccounts,
|
||||
@@ -95,16 +84,13 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::DeregisterNode => "deregisterNode",
|
||||
RpcRequest::GetAccountInfo => "getAccountInfo",
|
||||
RpcRequest::GetBalance => "getBalance",
|
||||
RpcRequest::GetBlock => "getBlock",
|
||||
RpcRequest::GetBlockHeight => "getBlockHeight",
|
||||
RpcRequest::GetBlockProduction => "getBlockProduction",
|
||||
RpcRequest::GetBlocks => "getBlocks",
|
||||
RpcRequest::GetBlocksWithLimit => "getBlocksWithLimit",
|
||||
RpcRequest::GetBlockTime => "getBlockTime",
|
||||
RpcRequest::GetClusterNodes => "getClusterNodes",
|
||||
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
|
||||
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
|
||||
RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit",
|
||||
RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress",
|
||||
RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2",
|
||||
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
|
||||
RpcRequest::GetEpochInfo => "getEpochInfo",
|
||||
@@ -129,7 +115,6 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
||||
RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
|
||||
RpcRequest::GetSnapshotSlot => "getSnapshotSlot",
|
||||
RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
|
||||
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
|
||||
RpcRequest::GetSlot => "getSlot",
|
||||
RpcRequest::GetSlotLeader => "getSlotLeader",
|
||||
@@ -144,7 +129,7 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
|
||||
RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
|
||||
RpcRequest::GetTokenSupply => "getTokenSupply",
|
||||
RpcRequest::GetTransaction => "getTransaction",
|
||||
RpcRequest::GetTotalSupply => "getTotalSupply",
|
||||
RpcRequest::GetTransactionCount => "getTransactionCount",
|
||||
RpcRequest::GetVersion => "getVersion",
|
||||
RpcRequest::GetVoteAccounts => "getVoteAccounts",
|
||||
|
@@ -46,15 +46,6 @@ pub struct RpcFees {
|
||||
pub blockhash: String,
|
||||
pub fee_calculator: FeeCalculator,
|
||||
pub last_valid_slot: Slot,
|
||||
pub last_valid_block_height: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeprecatedRpcFees {
|
||||
pub blockhash: String,
|
||||
pub fee_calculator: FeeCalculator,
|
||||
pub last_valid_slot: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -318,7 +309,6 @@ pub struct RpcSignatureConfirmation {
|
||||
pub struct RpcSimulateTransactionResult {
|
||||
pub err: Option<TransactionError>,
|
||||
pub logs: Option<Vec<String>>,
|
||||
pub accounts: Option<Vec<Option<UiAccount>>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.2"
|
||||
version = "1.6.9"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-core"
|
||||
readme = "../README.md"
|
||||
@@ -22,12 +22,19 @@ bv = { version = "0.11.1", features = ["serde"] }
|
||||
bs58 = "0.3.1"
|
||||
byteorder = "1.3.4"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
core_affinity = "0.5.10"
|
||||
crossbeam-channel = "0.4"
|
||||
ed25519-dalek = "=1.0.1"
|
||||
fs_extra = "1.2.0"
|
||||
flate2 = "1.0"
|
||||
indexmap = { version = "1.5", features = ["rayon"] }
|
||||
itertools = "0.9.0"
|
||||
jsonrpc-core = "17.0.0"
|
||||
jsonrpc-core-client = { version = "17.0.0", features = ["ipc", "ws"] }
|
||||
jsonrpc-derive = "17.0.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
jsonrpc-pubsub = "17.0.0"
|
||||
jsonrpc-ws-server = "17.0.0"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
lru = "0.6.1"
|
||||
@@ -39,52 +46,52 @@ rand_chacha = "0.2.2"
|
||||
rand_core = "0.6.2"
|
||||
raptorq = "1.4.2"
|
||||
rayon = "1.5.0"
|
||||
regex = "1.3.9"
|
||||
retain_mut = "0.1.2"
|
||||
serde = "1.0.122"
|
||||
serde_bytes = "0.11"
|
||||
serde_derive = "1.0.103"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.2" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.2" }
|
||||
solana-client = { path = "../client", version = "=1.7.2" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.2" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.2" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.7.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.2" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.2" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.2" }
|
||||
solana-program-test = { path = "../program-test", version = "=1.7.2" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.7.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.2" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.2" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.2" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.9" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.6.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.9" }
|
||||
solana-client = { path = "../client", version = "=1.6.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.6.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.9" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.6.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.9" }
|
||||
solana-perf = { path = "../perf", version = "=1.6.9" }
|
||||
solana-program-test = { path = "../program-test", version = "=1.6.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.9" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.6.9" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.6.9" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.9" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.6.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.9" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "=1.6.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.9" }
|
||||
solana-version = { path = "../version", version = "=1.6.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.9" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.2" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
|
||||
tokio-util = { version = "0.3", features = ["codec"] } # This crate needs to stay in sync with tokio_02, until that dependency can be removed
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.6.9" }
|
||||
trees = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
jsonrpc-core = "17.1.0"
|
||||
jsonrpc-core-client = { version = "17.1.0", features = ["ipc", "ws"] }
|
||||
matches = "0.1.6"
|
||||
num_cpus = "1.13.0"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde_json = "1.0.56"
|
||||
serial_test = "0.4.0"
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.2" }
|
||||
solana-version = { path = "../version", version = "=1.7.2" }
|
||||
symlink = "0.1.0"
|
||||
systemstat = "0.1.5"
|
||||
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
@@ -96,7 +103,13 @@ name = "banking_stage"
|
||||
name = "blockstore"
|
||||
|
||||
[[bench]]
|
||||
name = "cluster_info"
|
||||
name = "crds"
|
||||
|
||||
[[bench]]
|
||||
name = "crds_gossip_pull"
|
||||
|
||||
[[bench]]
|
||||
name = "crds_shards"
|
||||
|
||||
[[bench]]
|
||||
name = "gen_keys"
|
||||
@@ -104,8 +117,14 @@ name = "gen_keys"
|
||||
[[bench]]
|
||||
name = "sigverify_stage"
|
||||
|
||||
[[bench]]
|
||||
name = "poh"
|
||||
|
||||
[[bench]]
|
||||
name = "retransmit_stage"
|
||||
|
||||
[[bench]]
|
||||
name = "cluster_info"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -7,16 +7,16 @@ use crossbeam_channel::unbounded;
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_core::banking_stage::{BankingStage, BankingStageStats};
|
||||
use solana_gossip::cluster_info::ClusterInfo;
|
||||
use solana_gossip::cluster_info::Node;
|
||||
use solana_core::banking_stage::{create_test_recorder, BankingStage, BankingStageStats};
|
||||
use solana_core::cluster_info::ClusterInfo;
|
||||
use solana_core::cluster_info::Node;
|
||||
use solana_core::poh_recorder::WorkingBankEntry;
|
||||
use solana_ledger::blockstore_processor::process_entries;
|
||||
use solana_ledger::entry::{next_hash, Entry};
|
||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_poh::poh_recorder::{create_test_recorder, WorkingBankEntry};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::genesis_config::GenesisConfig;
|
||||
use solana_sdk::hash::Hash;
|
||||
|
@@ -5,8 +5,8 @@ extern crate test;
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_core::broadcast_stage::broadcast_metrics::TransmitShredsStats;
|
||||
use solana_core::broadcast_stage::{broadcast_shreds, get_broadcast_peers};
|
||||
use solana_gossip::cluster_info::{ClusterInfo, Node};
|
||||
use solana_gossip::contact_info::ContactInfo;
|
||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_ledger::shred::Shred;
|
||||
use solana_sdk::pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
|
@@ -2,16 +2,14 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{thread_rng, Rng},
|
||||
rayon::ThreadPoolBuilder,
|
||||
solana_gossip::{
|
||||
crds::Crds, crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS, crds_value::CrdsValue,
|
||||
},
|
||||
solana_sdk::pubkey::Pubkey,
|
||||
std::collections::HashMap,
|
||||
test::Bencher,
|
||||
};
|
||||
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) {
|
@@ -2,18 +2,14 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{thread_rng, Rng},
|
||||
rayon::ThreadPoolBuilder,
|
||||
solana_gossip::{
|
||||
cluster_info::MAX_BLOOM_SIZE,
|
||||
crds::Crds,
|
||||
crds_gossip_pull::{CrdsFilter, CrdsGossipPull},
|
||||
crds_value::CrdsValue,
|
||||
},
|
||||
solana_sdk::hash,
|
||||
test::Bencher,
|
||||
};
|
||||
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 solana_sdk::hash;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_hash_as_u64(bencher: &mut Bencher) {
|
||||
@@ -33,8 +29,13 @@ fn bench_hash_as_u64(bencher: &mut Bencher) {
|
||||
fn bench_build_crds_filters(bencher: &mut Bencher) {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let mut rng = thread_rng();
|
||||
let crds_gossip_pull = CrdsGossipPull::default();
|
||||
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
|
@@ -2,17 +2,15 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{thread_rng, Rng},
|
||||
solana_gossip::{
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_shards::CrdsShards,
|
||||
crds_value::CrdsValue,
|
||||
},
|
||||
solana_sdk::timing::timestamp,
|
||||
std::iter::repeat_with,
|
||||
test::Bencher,
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_core::{
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_shards::CrdsShards,
|
||||
crds_value::CrdsValue,
|
||||
};
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::iter::repeat_with;
|
||||
use test::Bencher;
|
||||
|
||||
const CRDS_SHARDS_BITS: u32 = 8;
|
||||
|
||||
@@ -21,7 +19,7 @@ fn new_test_crds_value<R: Rng>(rng: &mut R) -> VersionedCrdsValue {
|
||||
let label = value.label();
|
||||
let mut crds = Crds::default();
|
||||
crds.insert(value, timestamp()).unwrap();
|
||||
crds.get(&label).cloned().unwrap()
|
||||
crds.remove(&label).unwrap()
|
||||
}
|
||||
|
||||
fn bench_crds_shards_find(bencher: &mut Bencher, num_values: usize, mask_bits: u32) {
|
@@ -3,16 +3,12 @@
|
||||
#![feature(test)]
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
solana_ledger::poh::Poh,
|
||||
solana_poh::poh_service::DEFAULT_HASHES_PER_BATCH,
|
||||
solana_sdk::hash::Hash,
|
||||
std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
test::Bencher,
|
||||
};
|
||||
use solana_core::poh_service::DEFAULT_HASHES_PER_BATCH;
|
||||
use solana_ledger::poh::Poh;
|
||||
use solana_sdk::hash::Hash;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use test::Bencher;
|
||||
|
||||
const NUM_HASHES: u64 = 30_000; // Should require ~10ms on a 2017 MacBook Pro
|
||||
|
@@ -1,15 +1,11 @@
|
||||
#![feature(test)]
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
solana_ledger::entry::{next_entry_mut, Entry, EntrySlice},
|
||||
solana_sdk::{
|
||||
hash::{hash, Hash},
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
},
|
||||
test::Bencher,
|
||||
};
|
||||
use solana_ledger::entry::{next_entry_mut, Entry, EntrySlice};
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use test::Bencher;
|
||||
|
||||
const NUM_HASHES: u64 = 400;
|
||||
const NUM_ENTRIES: usize = 800;
|
@@ -4,16 +4,16 @@ extern crate solana_core;
|
||||
extern crate test;
|
||||
|
||||
use log::*;
|
||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::max_slots::MaxSlots;
|
||||
use solana_core::retransmit_stage::retransmitter;
|
||||
use solana_gossip::cluster_info::{ClusterInfo, Node};
|
||||
use solana_gossip::contact_info::ContactInfo;
|
||||
use solana_ledger::entry::Entry;
|
||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
use solana_ledger::leader_schedule_cache::LeaderScheduleCache;
|
||||
use solana_ledger::shred::Shredder;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_perf::packet::{Packet, Packets};
|
||||
use solana_rpc::max_slots::MaxSlots;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use solana_sdk::hash::Hash;
|
||||
@@ -39,12 +39,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
||||
const NUM_PEERS: usize = 4;
|
||||
let mut peer_sockets = Vec::new();
|
||||
for _ in 0..NUM_PEERS {
|
||||
// This ensures that cluster_info.id() is the root of turbine
|
||||
// retransmit tree and so the shreds are retransmited to all other
|
||||
// nodes in the cluster.
|
||||
let id = std::iter::repeat_with(pubkey::new_rand)
|
||||
.find(|pk| cluster_info.id() < *pk)
|
||||
.unwrap();
|
||||
let id = 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();
|
||||
|
@@ -8,7 +8,7 @@ use raptorq::{Decoder, Encoder};
|
||||
use solana_ledger::entry::{create_ticks, Entry};
|
||||
use solana_ledger::shred::{
|
||||
max_entries_per_n_shred, max_ticks_per_n_shreds, ProcessShredsStats, Shred, Shredder,
|
||||
MAX_DATA_SHREDS_PER_FEC_BLOCK, SHRED_PAYLOAD_SIZE, SIZE_OF_CODING_SHRED_HEADERS,
|
||||
MAX_DATA_SHREDS_PER_FEC_BLOCK, SHRED_PAYLOAD_SIZE, SIZE_OF_DATA_SHRED_IGNORED_TAIL,
|
||||
SIZE_OF_DATA_SHRED_PAYLOAD,
|
||||
};
|
||||
use solana_perf::test_tx;
|
||||
@@ -55,7 +55,7 @@ fn make_shreds(num_shreds: usize) -> Vec<Shred> {
|
||||
|
||||
fn make_concatenated_shreds(num_shreds: usize) -> Vec<u8> {
|
||||
let data_shreds = make_shreds(num_shreds);
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_CODING_SHRED_HEADERS) as usize;
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL) as usize;
|
||||
let mut data: Vec<u8> = vec![0; num_shreds * valid_shred_data_len];
|
||||
for (i, shred) in (data_shreds[0..num_shreds]).iter().enumerate() {
|
||||
data[i * valid_shred_data_len..(i + 1) * valid_shred_data_len]
|
||||
@@ -163,7 +163,7 @@ fn bench_shredder_decoding(bencher: &mut Bencher) {
|
||||
fn bench_shredder_coding_raptorq(bencher: &mut Bencher) {
|
||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK;
|
||||
let data = make_concatenated_shreds(symbol_count as usize);
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_CODING_SHRED_HEADERS) as usize;
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL) as usize;
|
||||
bencher.iter(|| {
|
||||
let encoder = Encoder::with_defaults(&data, valid_shred_data_len as u16);
|
||||
encoder.get_encoded_packets(symbol_count);
|
||||
@@ -174,7 +174,7 @@ fn bench_shredder_coding_raptorq(bencher: &mut Bencher) {
|
||||
fn bench_shredder_decoding_raptorq(bencher: &mut Bencher) {
|
||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK;
|
||||
let data = make_concatenated_shreds(symbol_count as usize);
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_CODING_SHRED_HEADERS) as usize;
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL) as usize;
|
||||
let encoder = Encoder::with_defaults(&data, valid_shred_data_len as u16);
|
||||
let mut packets = encoder.get_encoded_packets(symbol_count as u32);
|
||||
packets.shuffle(&mut rand::thread_rng());
|
||||
|
@@ -4,9 +4,11 @@
|
||||
// hash on gossip. Monitor gossip for messages from validators in the --trusted-validators
|
||||
// set and halt the node if a mismatch is detected.
|
||||
|
||||
use crate::snapshot_packager_service::PendingSnapshotPackage;
|
||||
use crate::{
|
||||
cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES},
|
||||
snapshot_packager_service::PendingSnapshotPackage,
|
||||
};
|
||||
use rayon::ThreadPool;
|
||||
use solana_gossip::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
|
||||
use solana_runtime::{
|
||||
accounts_db,
|
||||
snapshot_package::{AccountsPackage, AccountsPackagePre, AccountsPackageReceiver},
|
||||
@@ -41,7 +43,7 @@ impl AccountsHashVerifier {
|
||||
let exit = exit.clone();
|
||||
let cluster_info = cluster_info.clone();
|
||||
let t_accounts_hash_verifier = Builder::new()
|
||||
.name("solana-hash-accounts".to_string())
|
||||
.name("solana-accounts-hash".to_string())
|
||||
.spawn(move || {
|
||||
let mut hashes = vec![];
|
||||
let mut thread_pool_storage = None;
|
||||
@@ -216,7 +218,8 @@ impl AccountsHashVerifier {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_gossip::{cluster_info::make_accounts_hashes_message, contact_info::ContactInfo};
|
||||
use crate::cluster_info::make_accounts_hashes_message;
|
||||
use crate::contact_info::ContactInfo;
|
||||
use solana_runtime::bank_forks::ArchiveFormat;
|
||||
use solana_runtime::snapshot_utils::SnapshotVersion;
|
||||
use solana_sdk::{
|
||||
|
@@ -1,21 +1,27 @@
|
||||
//! The `banking_stage` processes Transaction messages. It is intended to be used
|
||||
//! to contruct a software pipeline. The stage uses all available CPU cores and
|
||||
//! can do its processing in parallel with signature verification on the GPU.
|
||||
use crate::packet_hasher::PacketHasher;
|
||||
use crate::{
|
||||
cluster_info::ClusterInfo,
|
||||
packet_hasher::PacketHasher,
|
||||
poh_recorder::{PohRecorder, PohRecorderError, TransactionRecorder, WorkingBankEntry},
|
||||
poh_service::{self, PohService},
|
||||
};
|
||||
use crossbeam_channel::{Receiver as CrossbeamReceiver, RecvTimeoutError};
|
||||
use itertools::Itertools;
|
||||
use lru::LruCache;
|
||||
use retain_mut::RetainMut;
|
||||
use solana_gossip::cluster_info::ClusterInfo;
|
||||
use solana_ledger::{blockstore_processor::TransactionStatusSender, entry::hash_transactions};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_ledger::{
|
||||
blockstore::Blockstore, blockstore_processor::TransactionStatusSender,
|
||||
entry::hash_transactions, leader_schedule_cache::LeaderScheduleCache,
|
||||
};
|
||||
use solana_measure::{measure::Measure, thread_mem_usage};
|
||||
use solana_metrics::{inc_new_counter_debug, inc_new_counter_info};
|
||||
use solana_perf::{
|
||||
cuda_runtime::PinnedVec,
|
||||
packet::{limited_deserialize, Packet, Packets, PACKETS_PER_BATCH},
|
||||
perf_libs,
|
||||
};
|
||||
use solana_poh::poh_recorder::{PohRecorder, PohRecorderError, TransactionRecorder};
|
||||
use solana_runtime::{
|
||||
accounts_db::ErrorCounters,
|
||||
bank::{
|
||||
@@ -33,6 +39,7 @@ use solana_sdk::{
|
||||
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
||||
},
|
||||
message::Message,
|
||||
poh_config::PohConfig,
|
||||
pubkey::Pubkey,
|
||||
short_vec::decode_shortu16_len,
|
||||
signature::Signature,
|
||||
@@ -50,7 +57,8 @@ use std::{
|
||||
mem::size_of,
|
||||
net::UdpSocket,
|
||||
ops::DerefMut,
|
||||
sync::atomic::{AtomicU64, AtomicUsize, Ordering},
|
||||
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
|
||||
sync::mpsc::Receiver,
|
||||
sync::{Arc, Mutex},
|
||||
thread::{self, Builder, JoinHandle},
|
||||
time::Duration,
|
||||
@@ -271,6 +279,7 @@ impl BankingStage {
|
||||
Builder::new()
|
||||
.name("solana-banking-stage-tx".to_string())
|
||||
.spawn(move || {
|
||||
thread_mem_usage::datapoint("solana-banking-stage-tx");
|
||||
Self::process_loop(
|
||||
my_pubkey,
|
||||
&verified_receiver,
|
||||
@@ -807,7 +816,6 @@ impl BankingStage {
|
||||
TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances),
|
||||
inner_instructions,
|
||||
transaction_logs,
|
||||
tx_results.rent_debits,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1384,29 +1392,65 @@ fn next_leader_tpu_forwards(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_test_recorder(
|
||||
bank: &Arc<Bank>,
|
||||
blockstore: &Arc<Blockstore>,
|
||||
poh_config: Option<PohConfig>,
|
||||
) -> (
|
||||
Arc<AtomicBool>,
|
||||
Arc<Mutex<PohRecorder>>,
|
||||
PohService,
|
||||
Receiver<WorkingBankEntry>,
|
||||
) {
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let poh_config = Arc::new(poh_config.unwrap_or_default());
|
||||
let (mut poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
|
||||
bank.tick_height(),
|
||||
bank.last_blockhash(),
|
||||
bank.slot(),
|
||||
Some((4, 4)),
|
||||
bank.ticks_per_slot(),
|
||||
&Pubkey::default(),
|
||||
blockstore,
|
||||
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
||||
&poh_config,
|
||||
exit.clone(),
|
||||
);
|
||||
poh_recorder.set_bank(&bank);
|
||||
|
||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||
let poh_service = PohService::new(
|
||||
poh_recorder.clone(),
|
||||
&poh_config,
|
||||
&exit,
|
||||
bank.ticks_per_slot(),
|
||||
poh_service::DEFAULT_PINNED_CPU_CORE,
|
||||
poh_service::DEFAULT_HASHES_PER_BATCH,
|
||||
record_receiver,
|
||||
);
|
||||
|
||||
(exit, poh_recorder, poh_service, entry_receiver)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
cluster_info::Node, poh_recorder::Record, poh_recorder::WorkingBank,
|
||||
transaction_status_service::TransactionStatusService,
|
||||
};
|
||||
use crossbeam_channel::unbounded;
|
||||
use itertools::Itertools;
|
||||
use solana_gossip::cluster_info::Node;
|
||||
use solana_ledger::{
|
||||
blockstore::{entries_to_test_shreds, Blockstore},
|
||||
blockstore::entries_to_test_shreds,
|
||||
entry::{next_entry, Entry, EntrySlice},
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
get_tmp_ledger_path,
|
||||
leader_schedule_cache::LeaderScheduleCache,
|
||||
};
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_poh::{
|
||||
poh_recorder::{create_test_recorder, Record, WorkingBank, WorkingBankEntry},
|
||||
poh_service::PohService,
|
||||
};
|
||||
use solana_rpc::transaction_status_service::TransactionStatusService;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
instruction::InstructionError,
|
||||
poh_config::PohConfig,
|
||||
signature::{Keypair, Signer},
|
||||
system_instruction::SystemError,
|
||||
system_transaction,
|
||||
@@ -1416,10 +1460,7 @@ mod tests {
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
path::Path,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::Receiver,
|
||||
},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
thread::sleep,
|
||||
};
|
||||
|
||||
@@ -1507,7 +1548,7 @@ mod tests {
|
||||
.collect();
|
||||
trace!("done");
|
||||
assert_eq!(entries.len(), genesis_config.ticks_per_slot as usize);
|
||||
assert!(entries.verify(&start_hash));
|
||||
assert_eq!(entries.verify(&start_hash), true);
|
||||
assert_eq!(entries[entries.len() - 1].hash, bank.last_blockhash());
|
||||
banking_stage.join().unwrap();
|
||||
}
|
||||
@@ -1616,7 +1657,7 @@ mod tests {
|
||||
.map(|(_bank, (entry, _tick_height))| entry)
|
||||
.collect();
|
||||
|
||||
assert!(entries.verify(&blockhash));
|
||||
assert_eq!(entries.verify(&blockhash), true);
|
||||
if !entries.is_empty() {
|
||||
blockhash = entries.last().unwrap().hash;
|
||||
for entry in entries {
|
||||
@@ -2084,7 +2125,7 @@ mod tests {
|
||||
}
|
||||
trace!("done ticking");
|
||||
|
||||
assert!(done);
|
||||
assert_eq!(done, true);
|
||||
|
||||
let transactions = vec![system_transaction::transfer(
|
||||
&mint_keypair,
|
||||
|
@@ -1,19 +1,17 @@
|
||||
use {
|
||||
crate::{bigtable_upload, blockstore::Blockstore},
|
||||
solana_runtime::commitment::BlockCommitmentCache,
|
||||
std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
sync::{Arc, RwLock},
|
||||
thread::{self, Builder, JoinHandle},
|
||||
},
|
||||
tokio::runtime::Runtime,
|
||||
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::Runtime;
|
||||
|
||||
// Delay uploading the largest confirmed root for this many slots. This is done in an attempt to
|
||||
// ensure that the `CacheBlockMetaService` has had enough time to add the block time for the root
|
||||
// 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 CacheBlockMetaService and BigTableUploadService would be
|
||||
// A more direct connection between CacheBlockTimeService and BigTableUploadService would be
|
||||
// preferable...
|
||||
const LARGEST_CONFIRMED_ROOT_UPLOAD_DELAY: usize = 100;
|
||||
|
||||
@@ -70,7 +68,7 @@ impl BigTableUploadService {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = runtime.block_on(bigtable_upload::upload_confirmed_blocks(
|
||||
let result = runtime.block_on(solana_ledger::bigtable_upload::upload_confirmed_blocks(
|
||||
blockstore.clone(),
|
||||
bigtable_ledger_storage.clone(),
|
||||
start_slot,
|
@@ -1,26 +1,25 @@
|
||||
//! A stage to broadcast data from a leader node to validators
|
||||
#![allow(clippy::rc_buffer)]
|
||||
use self::{
|
||||
broadcast_duplicates_run::BroadcastDuplicatesRun,
|
||||
broadcast_fake_shreds_run::BroadcastFakeShredsRun, broadcast_metrics::*,
|
||||
fail_entry_verification_broadcast_run::FailEntryVerificationBroadcastRun,
|
||||
standard_broadcast_run::StandardBroadcastRun,
|
||||
};
|
||||
use crate::result::{Error, Result};
|
||||
use crate::contact_info::ContactInfo;
|
||||
use crate::crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
|
||||
use crate::weighted_shuffle::weighted_best;
|
||||
use crate::{
|
||||
cluster_info::{ClusterInfo, ClusterInfoError},
|
||||
poh_recorder::WorkingBankEntry,
|
||||
result::{Error, Result},
|
||||
};
|
||||
use crossbeam_channel::{
|
||||
Receiver as CrossbeamReceiver, RecvTimeoutError as CrossbeamRecvTimeoutError,
|
||||
Sender as CrossbeamSender,
|
||||
};
|
||||
use solana_gossip::{
|
||||
cluster_info::{self, ClusterInfo, ClusterInfoError},
|
||||
contact_info::ContactInfo,
|
||||
crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
|
||||
weighted_shuffle::weighted_best,
|
||||
};
|
||||
use solana_ledger::{blockstore::Blockstore, shred::Shred};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_metrics::{inc_new_counter_error, inc_new_counter_info};
|
||||
use solana_poh::poh_recorder::WorkingBankEntry;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||
@@ -36,7 +35,6 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
mod broadcast_duplicates_run;
|
||||
mod broadcast_fake_shreds_run;
|
||||
pub mod broadcast_metrics;
|
||||
pub(crate) mod broadcast_utils;
|
||||
@@ -54,20 +52,11 @@ pub enum BroadcastStageReturnType {
|
||||
ChannelDisconnected,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct BroadcastDuplicatesConfig {
|
||||
/// Percentage of stake to send different version of slots to
|
||||
pub stake_partition: u8,
|
||||
/// Number of slots to wait before sending duplicate shreds
|
||||
pub duplicate_send_delay: usize,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum BroadcastStageType {
|
||||
Standard,
|
||||
FailEntryVerification,
|
||||
BroadcastFakeShreds,
|
||||
BroadcastDuplicates(BroadcastDuplicatesConfig),
|
||||
}
|
||||
|
||||
impl BroadcastStageType {
|
||||
@@ -112,16 +101,6 @@ impl BroadcastStageType {
|
||||
blockstore,
|
||||
BroadcastFakeShredsRun::new(keypair, 0, shred_version),
|
||||
),
|
||||
|
||||
BroadcastStageType::BroadcastDuplicates(config) => BroadcastStage::new(
|
||||
sock,
|
||||
cluster_info,
|
||||
receiver,
|
||||
retransmit_slots_receiver,
|
||||
exit_sender,
|
||||
blockstore,
|
||||
BroadcastDuplicatesRun::new(keypair, shred_version, config.clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,6 +363,7 @@ pub fn get_broadcast_peers(
|
||||
cluster_info: &ClusterInfo,
|
||||
stakes: Option<&HashMap<Pubkey, u64>>,
|
||||
) -> (Vec<ContactInfo>, Vec<(u64, usize)>) {
|
||||
use crate::cluster_info;
|
||||
let mut peers = cluster_info.tvu_peers();
|
||||
let peers_and_stakes = cluster_info::stake_weight_peers(&mut peers, stakes);
|
||||
(peers, peers_and_stakes)
|
||||
@@ -460,8 +440,8 @@ fn num_live_peers(peers: &[ContactInfo]) -> i64 {
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::cluster_info::{ClusterInfo, Node};
|
||||
use crossbeam_channel::unbounded;
|
||||
use solana_gossip::cluster_info::{ClusterInfo, Node};
|
||||
use solana_ledger::{
|
||||
blockstore::{make_slot_entries, Blockstore},
|
||||
entry::create_ticks,
|
||||
|
@@ -1,333 +0,0 @@
|
||||
use super::broadcast_utils::ReceiveResults;
|
||||
use super::*;
|
||||
use log::*;
|
||||
use solana_ledger::entry::{create_ticks, Entry, EntrySlice};
|
||||
use solana_ledger::shred::Shredder;
|
||||
use solana_runtime::blockhash_queue::BlockhashQueue;
|
||||
use solana_sdk::clock::Slot;
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Mutex;
|
||||
|
||||
// Queue which facilitates delivering shreds with a delay
|
||||
type DelayedQueue = VecDeque<(Option<Pubkey>, Option<Vec<Shred>>)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct BroadcastDuplicatesRun {
|
||||
config: BroadcastDuplicatesConfig,
|
||||
// Local queue for broadcast to track which duplicate blockhashes we've sent
|
||||
duplicate_queue: BlockhashQueue,
|
||||
// Shared queue between broadcast and transmit threads
|
||||
delayed_queue: Arc<Mutex<DelayedQueue>>,
|
||||
// Buffer for duplicate entries
|
||||
duplicate_entries_buffer: Vec<Entry>,
|
||||
last_duplicate_entry_hash: Hash,
|
||||
last_broadcast_slot: Slot,
|
||||
next_shred_index: u32,
|
||||
shred_version: u16,
|
||||
keypair: Arc<Keypair>,
|
||||
}
|
||||
|
||||
impl BroadcastDuplicatesRun {
|
||||
pub(super) fn new(
|
||||
keypair: Arc<Keypair>,
|
||||
shred_version: u16,
|
||||
config: BroadcastDuplicatesConfig,
|
||||
) -> Self {
|
||||
let mut delayed_queue = DelayedQueue::new();
|
||||
delayed_queue.resize(config.duplicate_send_delay, (None, None));
|
||||
Self {
|
||||
config,
|
||||
delayed_queue: Arc::new(Mutex::new(delayed_queue)),
|
||||
duplicate_queue: BlockhashQueue::default(),
|
||||
duplicate_entries_buffer: vec![],
|
||||
next_shred_index: u32::MAX,
|
||||
last_broadcast_slot: 0,
|
||||
last_duplicate_entry_hash: Hash::default(),
|
||||
shred_version,
|
||||
keypair,
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_or_create_duplicate_entries(
|
||||
&mut self,
|
||||
bank: &Arc<Bank>,
|
||||
receive_results: &ReceiveResults,
|
||||
) -> (Vec<Entry>, u32) {
|
||||
// If the last entry hash is default, grab the last blockhash from the parent bank
|
||||
if self.last_duplicate_entry_hash == Hash::default() {
|
||||
self.last_duplicate_entry_hash = bank.last_blockhash();
|
||||
}
|
||||
|
||||
// Create duplicate entries by..
|
||||
// 1) rearranging real entries so that all transaction entries are moved to
|
||||
// the front and tick entries are moved to the back.
|
||||
// 2) setting all transaction entries to zero hashes and all tick entries to `hashes_per_tick`.
|
||||
// 3) removing any transactions which reference blockhashes which aren't in the
|
||||
// duplicate blockhash queue.
|
||||
let (duplicate_entries, next_shred_index) = if bank.slot() > MINIMUM_DUPLICATE_SLOT {
|
||||
let mut tx_entries: Vec<Entry> = receive_results
|
||||
.entries
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
if entry.is_tick() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let transactions: Vec<Transaction> = entry
|
||||
.transactions
|
||||
.iter()
|
||||
.filter(|tx| {
|
||||
self.duplicate_queue
|
||||
.get_hash_age(&tx.message.recent_blockhash)
|
||||
.is_some()
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
if !transactions.is_empty() {
|
||||
Some(Entry::new_mut(
|
||||
&mut self.last_duplicate_entry_hash,
|
||||
&mut 0,
|
||||
transactions,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut tick_entries = create_ticks(
|
||||
receive_results.entries.tick_count(),
|
||||
bank.hashes_per_tick().unwrap_or_default(),
|
||||
self.last_duplicate_entry_hash,
|
||||
);
|
||||
self.duplicate_entries_buffer.append(&mut tx_entries);
|
||||
self.duplicate_entries_buffer.append(&mut tick_entries);
|
||||
|
||||
// Only send out duplicate entries when the block is finished otherwise the
|
||||
// recipient will start repairing for shreds they haven't received yet and
|
||||
// hit duplicate slot issues before we want them to.
|
||||
let entries = if receive_results.last_tick_height == bank.max_tick_height() {
|
||||
self.duplicate_entries_buffer.drain(..).collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// Set next shred index to 0 since we are sending the full slot
|
||||
(entries, 0)
|
||||
} else {
|
||||
// Send real entries until we hit min duplicate slot
|
||||
(receive_results.entries.clone(), self.next_shred_index)
|
||||
};
|
||||
|
||||
// Save last duplicate entry hash to avoid invalid entry hash errors
|
||||
if let Some(last_duplicate_entry) = duplicate_entries.last() {
|
||||
self.last_duplicate_entry_hash = last_duplicate_entry.hash;
|
||||
}
|
||||
|
||||
(duplicate_entries, next_shred_index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Duplicate slots should only be sent once all validators have started.
|
||||
/// This constant is intended to be used as a buffer so that all validators
|
||||
/// are live before sending duplicate slots.
|
||||
pub const MINIMUM_DUPLICATE_SLOT: Slot = 20;
|
||||
|
||||
impl BroadcastRun for BroadcastDuplicatesRun {
|
||||
fn run(
|
||||
&mut self,
|
||||
blockstore: &Arc<Blockstore>,
|
||||
receiver: &Receiver<WorkingBankEntry>,
|
||||
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
|
||||
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
|
||||
) -> Result<()> {
|
||||
// 1) Pull entries from banking stage
|
||||
let receive_results = broadcast_utils::recv_slot_entries(receiver)?;
|
||||
let bank = receive_results.bank.clone();
|
||||
let last_tick_height = receive_results.last_tick_height;
|
||||
|
||||
if self.next_shred_index == u32::MAX {
|
||||
self.next_shred_index = blockstore
|
||||
.meta(bank.slot())
|
||||
.expect("Database error")
|
||||
.map(|meta| meta.consumed)
|
||||
.unwrap_or(0) as u32
|
||||
}
|
||||
|
||||
// We were not the leader, but just became leader again
|
||||
if bank.slot() > self.last_broadcast_slot + 1 {
|
||||
self.last_duplicate_entry_hash = Hash::default();
|
||||
}
|
||||
self.last_broadcast_slot = bank.slot();
|
||||
|
||||
let shredder = Shredder::new(
|
||||
bank.slot(),
|
||||
bank.parent().unwrap().slot(),
|
||||
self.keypair.clone(),
|
||||
(bank.tick_height() % bank.ticks_per_slot()) as u8,
|
||||
self.shred_version,
|
||||
)
|
||||
.expect("Expected to create a new shredder");
|
||||
|
||||
let (data_shreds, coding_shreds, last_shred_index) = shredder.entries_to_shreds(
|
||||
&receive_results.entries,
|
||||
last_tick_height == bank.max_tick_height(),
|
||||
self.next_shred_index,
|
||||
);
|
||||
|
||||
let (duplicate_entries, next_duplicate_shred_index) =
|
||||
self.queue_or_create_duplicate_entries(&bank, &receive_results);
|
||||
let (duplicate_data_shreds, duplicate_coding_shreds, _) = if !duplicate_entries.is_empty() {
|
||||
shredder.entries_to_shreds(
|
||||
&duplicate_entries,
|
||||
last_tick_height == bank.max_tick_height(),
|
||||
next_duplicate_shred_index,
|
||||
)
|
||||
} else {
|
||||
(vec![], vec![], 0)
|
||||
};
|
||||
|
||||
// Manually track the shred index because relying on slot meta consumed is racy
|
||||
if last_tick_height == bank.max_tick_height() {
|
||||
self.next_shred_index = 0;
|
||||
self.duplicate_queue
|
||||
.register_hash(&self.last_duplicate_entry_hash, &FeeCalculator::default());
|
||||
} else {
|
||||
self.next_shred_index = last_shred_index;
|
||||
}
|
||||
|
||||
// Partition network with duplicate and real shreds based on stake
|
||||
let bank_epoch = bank.get_leader_schedule_epoch(bank.slot());
|
||||
let mut duplicate_recipients = HashMap::new();
|
||||
let mut real_recipients = HashMap::new();
|
||||
|
||||
let mut stakes: Vec<(Pubkey, u64)> = bank
|
||||
.epoch_staked_nodes(bank_epoch)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter(|(pubkey, _)| *pubkey != self.keypair.pubkey())
|
||||
.collect();
|
||||
stakes.sort_by(|(l_key, l_stake), (r_key, r_stake)| {
|
||||
if r_stake == l_stake {
|
||||
l_key.cmp(&r_key)
|
||||
} else {
|
||||
r_stake.cmp(&l_stake)
|
||||
}
|
||||
});
|
||||
|
||||
let highest_staked_node = stakes.first().cloned().map(|x| x.0);
|
||||
let stake_total: u64 = stakes.iter().map(|(_, stake)| *stake).sum();
|
||||
let mut cumulative_stake: u64 = 0;
|
||||
for (pubkey, stake) in stakes.into_iter().rev() {
|
||||
cumulative_stake += stake;
|
||||
if (100 * cumulative_stake / stake_total) as u8 <= self.config.stake_partition {
|
||||
duplicate_recipients.insert(pubkey, stake);
|
||||
} else {
|
||||
real_recipients.insert(pubkey, stake);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(highest_staked_node) = highest_staked_node {
|
||||
if bank.slot() > MINIMUM_DUPLICATE_SLOT && last_tick_height == bank.max_tick_height() {
|
||||
warn!(
|
||||
"{} sent duplicate slot {} to nodes: {:?}",
|
||||
self.keypair.pubkey(),
|
||||
bank.slot(),
|
||||
&duplicate_recipients,
|
||||
);
|
||||
warn!(
|
||||
"Duplicate shreds for slot {} will be broadcast in {} slot(s)",
|
||||
bank.slot(),
|
||||
self.config.duplicate_send_delay
|
||||
);
|
||||
|
||||
let delayed_shreds: Option<Vec<Shred>> = vec![
|
||||
duplicate_data_shreds.last().cloned(),
|
||||
data_shreds.last().cloned(),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
self.delayed_queue
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back((Some(highest_staked_node), delayed_shreds));
|
||||
}
|
||||
}
|
||||
|
||||
let duplicate_recipients = Arc::new(duplicate_recipients);
|
||||
let real_recipients = Arc::new(real_recipients);
|
||||
|
||||
let data_shreds = Arc::new(data_shreds);
|
||||
blockstore_sender.send((data_shreds.clone(), None))?;
|
||||
|
||||
// 3) Start broadcast step
|
||||
socket_sender.send((
|
||||
(
|
||||
Some(duplicate_recipients.clone()),
|
||||
Arc::new(duplicate_data_shreds),
|
||||
),
|
||||
None,
|
||||
))?;
|
||||
socket_sender.send((
|
||||
(
|
||||
Some(duplicate_recipients),
|
||||
Arc::new(duplicate_coding_shreds),
|
||||
),
|
||||
None,
|
||||
))?;
|
||||
socket_sender.send(((Some(real_recipients.clone()), data_shreds), None))?;
|
||||
socket_sender.send(((Some(real_recipients), Arc::new(coding_shreds)), None))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn transmit(
|
||||
&mut self,
|
||||
receiver: &Arc<Mutex<TransmitReceiver>>,
|
||||
cluster_info: &ClusterInfo,
|
||||
sock: &UdpSocket,
|
||||
) -> Result<()> {
|
||||
// Check the delay queue for shreds that are ready to be sent
|
||||
let (delayed_recipient, delayed_shreds) = {
|
||||
let mut delayed_deque = self.delayed_queue.lock().unwrap();
|
||||
if delayed_deque.len() > self.config.duplicate_send_delay {
|
||||
delayed_deque.pop_front().unwrap()
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
};
|
||||
|
||||
let ((stakes, shreds), _) = receiver.lock().unwrap().recv()?;
|
||||
let stakes = stakes.unwrap();
|
||||
for peer in cluster_info.tvu_peers() {
|
||||
// Forward shreds to circumvent gossip
|
||||
if stakes.get(&peer.id).is_some() {
|
||||
shreds.iter().for_each(|shred| {
|
||||
sock.send_to(&shred.payload, &peer.tvu_forwards).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// After a delay, broadcast duplicate shreds to a single node
|
||||
if let Some(shreds) = delayed_shreds.as_ref() {
|
||||
if Some(peer.id) == delayed_recipient {
|
||||
shreds.iter().for_each(|shred| {
|
||||
sock.send_to(&shred.payload, &peer.tvu).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn record(
|
||||
&mut self,
|
||||
receiver: &Arc<Mutex<RecordReceiver>>,
|
||||
blockstore: &Arc<Blockstore>,
|
||||
) -> Result<()> {
|
||||
let (data_shreds, _) = receiver.lock().unwrap().recv()?;
|
||||
blockstore.insert_shreds(data_shreds.to_vec(), None, true)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -138,7 +138,7 @@ impl BroadcastRun for BroadcastFakeShredsRun {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_gossip::contact_info::ContactInfo;
|
||||
use crate::contact_info::ContactInfo;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
#[test]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::poh_recorder::WorkingBankEntry;
|
||||
use crate::result::Result;
|
||||
use solana_ledger::{entry::Entry, shred::Shred};
|
||||
use solana_poh::poh_recorder::WorkingBankEntry;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::clock::Slot;
|
||||
use std::{
|
||||
|
@@ -494,7 +494,7 @@ impl BroadcastRun for StandardBroadcastRun {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use solana_gossip::cluster_info::{ClusterInfo, Node};
|
||||
use crate::cluster_info::{ClusterInfo, Node};
|
||||
use solana_ledger::genesis_utils::create_genesis_config;
|
||||
use solana_ledger::{
|
||||
blockstore::Blockstore, entry::create_ticks, get_tmp_ledger_path,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
pub use solana_ledger::blockstore_processor::CacheBlockMetaSender;
|
||||
pub use solana_ledger::blockstore_processor::CacheBlockTimeSender;
|
||||
use {
|
||||
crossbeam_channel::{Receiver, RecvTimeoutError},
|
||||
solana_ledger::blockstore::Blockstore,
|
||||
@@ -14,18 +14,18 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
pub type CacheBlockMetaReceiver = Receiver<Arc<Bank>>;
|
||||
pub type CacheBlockTimeReceiver = Receiver<Arc<Bank>>;
|
||||
|
||||
pub struct CacheBlockMetaService {
|
||||
pub struct CacheBlockTimeService {
|
||||
thread_hdl: JoinHandle<()>,
|
||||
}
|
||||
|
||||
const CACHE_BLOCK_TIME_WARNING_MS: u64 = 150;
|
||||
|
||||
impl CacheBlockMetaService {
|
||||
impl CacheBlockTimeService {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(
|
||||
cache_block_meta_receiver: CacheBlockMetaReceiver,
|
||||
cache_block_time_receiver: CacheBlockTimeReceiver,
|
||||
blockstore: Arc<Blockstore>,
|
||||
exit: &Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
@@ -36,19 +36,19 @@ impl CacheBlockMetaService {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let recv_result = cache_block_meta_receiver.recv_timeout(Duration::from_secs(1));
|
||||
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_meta_timer = Measure::start("cache_block_meta_timer");
|
||||
Self::cache_block_meta(bank, &blockstore);
|
||||
cache_block_meta_timer.stop();
|
||||
if cache_block_meta_timer.as_ms() > CACHE_BLOCK_TIME_WARNING_MS {
|
||||
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_meta operation took: {}ms",
|
||||
cache_block_meta_timer.as_ms()
|
||||
"cache_block_time operation took: {}ms",
|
||||
cache_block_time_timer.as_ms()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -59,13 +59,10 @@ impl CacheBlockMetaService {
|
||||
Self { thread_hdl }
|
||||
}
|
||||
|
||||
fn cache_block_meta(bank: Arc<Bank>, blockstore: &Arc<Blockstore>) {
|
||||
fn cache_block_time(bank: Arc<Bank>, blockstore: &Arc<Blockstore>) {
|
||||
if let Err(e) = blockstore.cache_block_time(bank.slot(), bank.clock().unix_timestamp) {
|
||||
error!("cache_block_time failed: slot {:?} {:?}", bank.slot(), e);
|
||||
}
|
||||
if let Err(e) = blockstore.cache_block_height(bank.slot(), bank.block_height()) {
|
||||
error!("cache_block_height failed: slot {:?} {:?}", bank.slot(), e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) -> thread::Result<()> {
|
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,11 @@
|
||||
use {
|
||||
crate::crds_gossip::CrdsGossip,
|
||||
solana_measure::measure::Measure,
|
||||
solana_sdk::pubkey::Pubkey,
|
||||
std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
RwLock,
|
||||
},
|
||||
time::Instant,
|
||||
use crate::crds_gossip::CrdsGossip;
|
||||
use solana_measure::measure::Measure;
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
RwLock,
|
||||
},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -118,24 +114,17 @@ pub(crate) struct GossipStats {
|
||||
pub(crate) trim_crds_table_failed: Counter,
|
||||
pub(crate) trim_crds_table_purged_values_count: Counter,
|
||||
pub(crate) tvu_peers: Counter,
|
||||
pub(crate) verify_gossip_packets_time: Counter,
|
||||
}
|
||||
|
||||
pub(crate) fn submit_gossip_stats(
|
||||
stats: &GossipStats,
|
||||
gossip: &RwLock<CrdsGossip>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
) {
|
||||
let (table_size, num_nodes, purged_values_size, failed_inserts_size) = {
|
||||
pub(crate) fn submit_gossip_stats(stats: &GossipStats, gossip: &RwLock<CrdsGossip>) {
|
||||
let (table_size, purged_values_size, failed_inserts_size) = {
|
||||
let gossip = gossip.read().unwrap();
|
||||
(
|
||||
gossip.crds.len(),
|
||||
gossip.crds.num_nodes(),
|
||||
gossip.crds.num_purged(),
|
||||
gossip.pull.purged_values.len(),
|
||||
gossip.pull.failed_inserts.len(),
|
||||
)
|
||||
};
|
||||
let num_nodes_staked = stakes.values().filter(|stake| **stake > 0).count();
|
||||
datapoint_info!(
|
||||
"cluster_info_stats",
|
||||
("entrypoint", stats.entrypoint.clear(), i64),
|
||||
@@ -153,8 +142,6 @@ pub(crate) fn submit_gossip_stats(
|
||||
("table_size", table_size as i64, i64),
|
||||
("purged_values_size", purged_values_size as i64, i64),
|
||||
("failed_inserts_size", failed_inserts_size as i64, i64),
|
||||
("num_nodes", num_nodes as i64, i64),
|
||||
("num_nodes_staked", num_nodes_staked as i64, i64),
|
||||
);
|
||||
datapoint_info!(
|
||||
"cluster_info_stats2",
|
||||
@@ -172,11 +159,6 @@ pub(crate) fn submit_gossip_stats(
|
||||
stats.process_gossip_packets_time.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"verify_gossip_packets_time",
|
||||
stats.verify_gossip_packets_time.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"handle_batch_ping_messages_time",
|
||||
stats.handle_batch_ping_messages_time.clear(),
|
@@ -1,7 +1,13 @@
|
||||
use crate::{
|
||||
cluster_info::{ClusterInfo, GOSSIP_SLEEP_MILLIS},
|
||||
crds::Cursor,
|
||||
crds_value::CrdsValueLabel,
|
||||
optimistic_confirmation_verifier::OptimisticConfirmationVerifier,
|
||||
optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender},
|
||||
poh_recorder::PohRecorder,
|
||||
replay_stage::DUPLICATE_THRESHOLD,
|
||||
result::{Error, Result},
|
||||
rpc_subscriptions::RpcSubscriptions,
|
||||
sigverify,
|
||||
verified_vote_packets::VerifiedVotePackets,
|
||||
vote_stake_tracker::VoteStakeTracker,
|
||||
@@ -11,19 +17,9 @@ use crossbeam_channel::{
|
||||
};
|
||||
use itertools::izip;
|
||||
use log::*;
|
||||
use solana_gossip::{
|
||||
cluster_info::{ClusterInfo, GOSSIP_SLEEP_MILLIS},
|
||||
crds::Cursor,
|
||||
crds_value::CrdsValueLabel,
|
||||
};
|
||||
use solana_ledger::blockstore::Blockstore;
|
||||
use solana_metrics::inc_new_counter_debug;
|
||||
use solana_perf::packet::{self, Packets};
|
||||
use solana_poh::poh_recorder::PohRecorder;
|
||||
use solana_rpc::{
|
||||
optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender},
|
||||
rpc_subscriptions::RpcSubscriptions,
|
||||
};
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
bank_forks::BankForks,
|
||||
@@ -33,7 +29,7 @@ use solana_runtime::{
|
||||
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT, DEFAULT_TICKS_PER_SLOT},
|
||||
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT},
|
||||
epoch_schedule::EpochSchedule,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
@@ -384,14 +380,9 @@ impl ClusterInfoVoteListener {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let would_be_leader = poh_recorder
|
||||
.lock()
|
||||
.unwrap()
|
||||
.would_be_leader(20 * DEFAULT_TICKS_PER_SLOT);
|
||||
if let Err(e) = verified_vote_packets.receive_and_process_vote_packets(
|
||||
&verified_vote_label_packets_receiver,
|
||||
&mut update_version,
|
||||
would_be_leader,
|
||||
) {
|
||||
match e {
|
||||
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Disconnected) => {
|
||||
@@ -833,8 +824,8 @@ impl ClusterInfoVoteListener {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank;
|
||||
use solana_perf::packet;
|
||||
use solana_rpc::optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank;
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
commitment::BlockCommitmentCache,
|
||||
|
@@ -3,9 +3,8 @@ use crate::{
|
||||
progress_map::ProgressMap,
|
||||
};
|
||||
use solana_sdk::{clock::Slot, hash::Hash};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
|
||||
pub(crate) type DuplicateSlotsTracker = BTreeSet<Slot>;
|
||||
pub(crate) type GossipDuplicateConfirmedSlots = BTreeMap<Slot, Hash>;
|
||||
type SlotStateHandler = fn(Slot, &Hash, Option<&Hash>, bool, bool) -> Vec<ResultingStateChange>;
|
||||
|
||||
@@ -201,12 +200,19 @@ fn get_cluster_duplicate_confirmed_hash<'a>(
|
||||
|
||||
fn apply_state_changes(
|
||||
slot: Slot,
|
||||
progress: &mut ProgressMap,
|
||||
fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||
descendants: &HashMap<Slot, HashSet<Slot>>,
|
||||
state_changes: Vec<ResultingStateChange>,
|
||||
) {
|
||||
for state_change in state_changes {
|
||||
match state_change {
|
||||
ResultingStateChange::MarkSlotDuplicate(bank_frozen_hash) => {
|
||||
progress.set_unconfirmed_duplicate_slot(
|
||||
slot,
|
||||
descendants.get(&slot).unwrap_or(&HashSet::default()),
|
||||
);
|
||||
fork_choice.mark_fork_invalid_candidate(&(slot, bank_frozen_hash));
|
||||
}
|
||||
ResultingStateChange::RepairDuplicateConfirmedVersion(
|
||||
@@ -217,20 +223,25 @@ fn apply_state_changes(
|
||||
repair_correct_version(slot, &cluster_duplicate_confirmed_hash);
|
||||
}
|
||||
ResultingStateChange::DuplicateConfirmedSlotMatchesCluster(bank_frozen_hash) => {
|
||||
progress.set_confirmed_duplicate_slot(
|
||||
slot,
|
||||
ancestors.get(&slot).unwrap_or(&HashSet::default()),
|
||||
descendants.get(&slot).unwrap_or(&HashSet::default()),
|
||||
);
|
||||
fork_choice.mark_fork_valid_candidate(&(slot, bank_frozen_hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn check_slot_agrees_with_cluster(
|
||||
slot: Slot,
|
||||
root: Slot,
|
||||
frozen_hash: Option<Hash>,
|
||||
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
||||
progress: &ProgressMap,
|
||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||
descendants: &HashMap<Slot, HashSet<Slot>>,
|
||||
progress: &mut ProgressMap,
|
||||
fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||
slot_state_update: SlotStateUpdate,
|
||||
) {
|
||||
@@ -247,15 +258,6 @@ pub(crate) fn check_slot_agrees_with_cluster(
|
||||
return;
|
||||
}
|
||||
|
||||
// Needs to happen before the frozen_hash.is_none() check below to account for duplicate
|
||||
// signals arriving before the bank is constructed in replay.
|
||||
if matches!(slot_state_update, SlotStateUpdate::Duplicate) {
|
||||
// If this slot has already been processed before, return
|
||||
if !duplicate_slots_tracker.insert(slot) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if frozen_hash.is_none() {
|
||||
// If the bank doesn't even exist in BankForks yet,
|
||||
// then there's nothing to do as replay of the slot
|
||||
@@ -266,18 +268,25 @@ pub(crate) fn check_slot_agrees_with_cluster(
|
||||
let frozen_hash = frozen_hash.unwrap();
|
||||
let gossip_duplicate_confirmed_hash = gossip_duplicate_confirmed_slots.get(&slot);
|
||||
|
||||
// If the bank hasn't been frozen yet, then we haven't duplicate confirmed a local version
|
||||
// this slot through replay yet.
|
||||
let is_local_replay_duplicate_confirmed = fork_choice
|
||||
.is_duplicate_confirmed(&(slot, frozen_hash))
|
||||
.unwrap_or(false);
|
||||
let is_local_replay_duplicate_confirmed = progress.is_duplicate_confirmed(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
|
||||
let cluster_duplicate_confirmed_hash = get_cluster_duplicate_confirmed_hash(
|
||||
slot,
|
||||
gossip_duplicate_confirmed_hash,
|
||||
&frozen_hash,
|
||||
is_local_replay_duplicate_confirmed,
|
||||
);
|
||||
let is_slot_duplicate = duplicate_slots_tracker.contains(&slot);
|
||||
let mut is_slot_duplicate =
|
||||
progress.is_unconfirmed_duplicate(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
|
||||
if matches!(slot_state_update, SlotStateUpdate::Duplicate) {
|
||||
if is_slot_duplicate {
|
||||
// Already processed duplicate signal for this slot, no need to continue
|
||||
return;
|
||||
} else {
|
||||
// Otherwise, mark the slot as duplicate so the appropriate state changes
|
||||
// will trigger
|
||||
is_slot_duplicate = true;
|
||||
}
|
||||
}
|
||||
let is_dead = progress.is_dead(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
|
||||
|
||||
info!(
|
||||
@@ -300,7 +309,14 @@ pub(crate) fn check_slot_agrees_with_cluster(
|
||||
is_slot_duplicate,
|
||||
is_dead,
|
||||
);
|
||||
apply_state_changes(slot, fork_choice, state_changes);
|
||||
apply_state_changes(
|
||||
slot,
|
||||
progress,
|
||||
fork_choice,
|
||||
ancestors,
|
||||
descendants,
|
||||
state_changes,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -308,16 +324,15 @@ mod test {
|
||||
use super::*;
|
||||
use crate::consensus::test::VoteSimulator;
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::RwLock,
|
||||
};
|
||||
use std::sync::RwLock;
|
||||
use trees::tr;
|
||||
|
||||
struct InitialState {
|
||||
heaviest_subtree_fork_choice: HeaviestSubtreeForkChoice,
|
||||
progress: ProgressMap,
|
||||
ancestors: HashMap<Slot, HashSet<Slot>>,
|
||||
descendants: HashMap<Slot, HashSet<Slot>>,
|
||||
slot: Slot,
|
||||
bank_forks: RwLock<BankForks>,
|
||||
}
|
||||
|
||||
@@ -326,6 +341,7 @@ mod test {
|
||||
let forks = tr(0) / (tr(1) / (tr(2) / tr(3)));
|
||||
let mut vote_simulator = VoteSimulator::new(1);
|
||||
vote_simulator.fill_bank_forks(forks, &HashMap::new());
|
||||
let ancestors = vote_simulator.bank_forks.read().unwrap().ancestors();
|
||||
|
||||
let descendants = vote_simulator
|
||||
.bank_forks
|
||||
@@ -337,7 +353,9 @@ mod test {
|
||||
InitialState {
|
||||
heaviest_subtree_fork_choice: vote_simulator.heaviest_subtree_fork_choice,
|
||||
progress: vote_simulator.progress,
|
||||
ancestors,
|
||||
descendants,
|
||||
slot: 0,
|
||||
bank_forks: vote_simulator.bank_forks,
|
||||
}
|
||||
}
|
||||
@@ -608,159 +626,75 @@ mod test {
|
||||
// Common state
|
||||
let InitialState {
|
||||
mut heaviest_subtree_fork_choice,
|
||||
mut progress,
|
||||
ancestors,
|
||||
descendants,
|
||||
slot,
|
||||
bank_forks,
|
||||
..
|
||||
} = setup();
|
||||
|
||||
// MarkSlotDuplicate should mark progress map and remove
|
||||
// the slot from fork choice
|
||||
let duplicate_slot = bank_forks.read().unwrap().root() + 1;
|
||||
let duplicate_slot_hash = bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(duplicate_slot)
|
||||
.unwrap()
|
||||
.hash();
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
apply_state_changes(
|
||||
duplicate_slot,
|
||||
slot,
|
||||
&mut progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
vec![ResultingStateChange::MarkSlotDuplicate(duplicate_slot_hash)],
|
||||
&ancestors,
|
||||
&descendants,
|
||||
vec![ResultingStateChange::MarkSlotDuplicate(slot_hash)],
|
||||
);
|
||||
assert!(!heaviest_subtree_fork_choice
|
||||
.is_candidate(&(duplicate_slot, duplicate_slot_hash))
|
||||
.is_candidate_slot(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
for child_slot in descendants
|
||||
.get(&duplicate_slot)
|
||||
.get(&slot)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.chain(std::iter::once(&duplicate_slot))
|
||||
.chain(std::iter::once(&slot))
|
||||
{
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(
|
||||
*child_slot,
|
||||
bank_forks.read().unwrap().get(*child_slot).unwrap().hash()
|
||||
))
|
||||
progress
|
||||
.latest_unconfirmed_duplicate_ancestor(*child_slot)
|
||||
.unwrap(),
|
||||
duplicate_slot
|
||||
slot
|
||||
);
|
||||
}
|
||||
|
||||
// DuplicateConfirmedSlotMatchesCluster should re-enable fork choice
|
||||
apply_state_changes(
|
||||
duplicate_slot,
|
||||
slot,
|
||||
&mut progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
vec![ResultingStateChange::DuplicateConfirmedSlotMatchesCluster(
|
||||
duplicate_slot_hash,
|
||||
slot_hash,
|
||||
)],
|
||||
);
|
||||
for child_slot in descendants
|
||||
.get(&duplicate_slot)
|
||||
.get(&slot)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.chain(std::iter::once(&duplicate_slot))
|
||||
.chain(std::iter::once(&slot))
|
||||
{
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(
|
||||
*child_slot,
|
||||
bank_forks.read().unwrap().get(*child_slot).unwrap().hash()
|
||||
))
|
||||
assert!(progress
|
||||
.latest_unconfirmed_duplicate_ancestor(*child_slot)
|
||||
.is_none());
|
||||
}
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_candidate(&(duplicate_slot, duplicate_slot_hash))
|
||||
.is_candidate_slot(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
fn run_test_state_duplicate_then_bank_frozen(initial_bank_hash: Option<Hash>) {
|
||||
// Common state
|
||||
let InitialState {
|
||||
mut heaviest_subtree_fork_choice,
|
||||
progress,
|
||||
bank_forks,
|
||||
..
|
||||
} = setup();
|
||||
|
||||
// Setup a duplicate slot state transition with the initial bank state of the duplicate slot
|
||||
// determined by `initial_bank_hash`, which can be:
|
||||
// 1) A default hash (unfrozen bank),
|
||||
// 2) None (a slot that hasn't even started replay yet).
|
||||
let root = 0;
|
||||
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||
let gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||
let duplicate_slot = 2;
|
||||
check_slot_agrees_with_cluster(
|
||||
duplicate_slot,
|
||||
root,
|
||||
initial_bank_hash,
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::Duplicate,
|
||||
);
|
||||
assert!(duplicate_slots_tracker.contains(&duplicate_slot));
|
||||
// Nothing should be applied yet to fork choice, since bank was not yet frozen
|
||||
for slot in 2..=3 {
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.is_none());
|
||||
}
|
||||
|
||||
// Now freeze the bank
|
||||
let frozen_duplicate_slot_hash = bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(duplicate_slot)
|
||||
.unwrap()
|
||||
.hash();
|
||||
check_slot_agrees_with_cluster(
|
||||
duplicate_slot,
|
||||
root,
|
||||
Some(frozen_duplicate_slot_hash),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::Frozen,
|
||||
);
|
||||
|
||||
// Progress map should have the correct updates, fork choice should mark duplicate
|
||||
// as unvotable
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_unconfirmed_duplicate(&(duplicate_slot, frozen_duplicate_slot_hash))
|
||||
.unwrap());
|
||||
|
||||
// The ancestor of the duplicate slot should be the best slot now
|
||||
let (duplicate_ancestor, duplicate_parent_hash) = {
|
||||
let r_bank_forks = bank_forks.read().unwrap();
|
||||
let parent_bank = r_bank_forks.get(duplicate_slot).unwrap().parent().unwrap();
|
||||
(parent_bank.slot(), parent_bank.hash())
|
||||
};
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(duplicate_ancestor, duplicate_parent_hash)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_unfrozen_bank_duplicate_then_bank_frozen() {
|
||||
run_test_state_duplicate_then_bank_frozen(Some(Hash::default()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_unreplayed_bank_duplicate_then_bank_frozen() {
|
||||
run_test_state_duplicate_then_bank_frozen(None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_ancestor_confirmed_descendant_duplicate() {
|
||||
// Common state
|
||||
let InitialState {
|
||||
mut heaviest_subtree_fork_choice,
|
||||
progress,
|
||||
mut progress,
|
||||
ancestors,
|
||||
descendants,
|
||||
bank_forks,
|
||||
..
|
||||
} = setup();
|
||||
@@ -771,7 +705,6 @@ mod test {
|
||||
(3, slot3_hash)
|
||||
);
|
||||
let root = 0;
|
||||
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||
|
||||
// Mark slot 2 as duplicate confirmed
|
||||
@@ -781,67 +714,36 @@ mod test {
|
||||
2,
|
||||
root,
|
||||
Some(slot2_hash),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&mut progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::DuplicateConfirmed,
|
||||
);
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_duplicate_confirmed(&(2, slot2_hash))
|
||||
.unwrap());
|
||||
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(3, slot3_hash)
|
||||
);
|
||||
for slot in 0..=2 {
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_duplicate_confirmed(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.is_none());
|
||||
}
|
||||
|
||||
// Mark 3 as duplicate, should not remove the duplicate confirmed slot 2 from
|
||||
// fork choice
|
||||
// Mark 3 as duplicate, should not remove slot 2 from fork choice
|
||||
check_slot_agrees_with_cluster(
|
||||
3,
|
||||
root,
|
||||
Some(slot3_hash),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&mut progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::Duplicate,
|
||||
);
|
||||
assert!(duplicate_slots_tracker.contains(&3));
|
||||
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(2, slot2_hash)
|
||||
);
|
||||
for slot in 0..=3 {
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
if slot <= 2 {
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_duplicate_confirmed(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.is_none());
|
||||
} else {
|
||||
assert!(!heaviest_subtree_fork_choice
|
||||
.is_duplicate_confirmed(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -849,7 +751,9 @@ mod test {
|
||||
// Common state
|
||||
let InitialState {
|
||||
mut heaviest_subtree_fork_choice,
|
||||
progress,
|
||||
mut progress,
|
||||
ancestors,
|
||||
descendants,
|
||||
bank_forks,
|
||||
..
|
||||
} = setup();
|
||||
@@ -860,30 +764,19 @@ mod test {
|
||||
(3, slot3_hash)
|
||||
);
|
||||
let root = 0;
|
||||
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||
|
||||
// Mark 2 as duplicate
|
||||
// Mark 2 as duplicate confirmed
|
||||
check_slot_agrees_with_cluster(
|
||||
2,
|
||||
root,
|
||||
Some(bank_forks.read().unwrap().get(2).unwrap().hash()),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&mut progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::Duplicate,
|
||||
);
|
||||
assert!(duplicate_slots_tracker.contains(&2));
|
||||
for slot in 2..=3 {
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.unwrap(),
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
let slot1_hash = bank_forks.read().unwrap().get(1).unwrap().hash();
|
||||
assert_eq!(
|
||||
@@ -897,93 +790,14 @@ mod test {
|
||||
3,
|
||||
root,
|
||||
Some(slot3_hash),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&mut progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::DuplicateConfirmed,
|
||||
);
|
||||
for slot in 0..=3 {
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_duplicate_confirmed(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.is_none());
|
||||
}
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(3, slot3_hash)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_descendant_confirmed_ancestor_duplicate() {
|
||||
// Common state
|
||||
let InitialState {
|
||||
mut heaviest_subtree_fork_choice,
|
||||
progress,
|
||||
bank_forks,
|
||||
..
|
||||
} = setup();
|
||||
|
||||
let slot3_hash = bank_forks.read().unwrap().get(3).unwrap().hash();
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(3, slot3_hash)
|
||||
);
|
||||
let root = 0;
|
||||
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||
|
||||
// Mark 3 as duplicate confirmed
|
||||
gossip_duplicate_confirmed_slots.insert(3, slot3_hash);
|
||||
check_slot_agrees_with_cluster(
|
||||
3,
|
||||
root,
|
||||
Some(slot3_hash),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::DuplicateConfirmed,
|
||||
);
|
||||
let verify_all_slots_duplicate_confirmed =
|
||||
|bank_forks: &RwLock<BankForks>,
|
||||
heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice| {
|
||||
for slot in 0..=3 {
|
||||
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.is_duplicate_confirmed(&(slot, slot_hash))
|
||||
.unwrap());
|
||||
assert!(heaviest_subtree_fork_choice
|
||||
.latest_invalid_ancestor(&(slot, slot_hash))
|
||||
.is_none());
|
||||
}
|
||||
};
|
||||
verify_all_slots_duplicate_confirmed(&bank_forks, &heaviest_subtree_fork_choice);
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(3, slot3_hash)
|
||||
);
|
||||
|
||||
// Mark ancestor 1 as duplicate, fork choice should be unaffected since
|
||||
// slot 1 was duplicate confirmed by the confirmation on its
|
||||
// descendant, 3.
|
||||
let slot1_hash = bank_forks.read().unwrap().get(1).unwrap().hash();
|
||||
check_slot_agrees_with_cluster(
|
||||
1,
|
||||
root,
|
||||
Some(slot1_hash),
|
||||
&mut duplicate_slots_tracker,
|
||||
&gossip_duplicate_confirmed_slots,
|
||||
&progress,
|
||||
&mut heaviest_subtree_fork_choice,
|
||||
SlotStateUpdate::Duplicate,
|
||||
);
|
||||
assert!(duplicate_slots_tracker.contains(&1));
|
||||
verify_all_slots_duplicate_confirmed(&bank_forks, &heaviest_subtree_fork_choice);
|
||||
assert_eq!(
|
||||
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||
(3, slot3_hash)
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use crate::serve_repair::RepairType;
|
||||
use itertools::Itertools;
|
||||
use solana_gossip::{
|
||||
use crate::{
|
||||
cluster_info::ClusterInfo, contact_info::ContactInfo, crds::Cursor, epoch_slots::EpochSlots,
|
||||
serve_repair::RepairType,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use solana_runtime::{bank_forks::BankForks, epoch_stakes::NodeIdToVoteAccounts};
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||
use std::{
|
||||
|
@@ -1,32 +1,28 @@
|
||||
use crate::cluster_slots::ClusterSlots;
|
||||
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
||||
use solana_gossip::cluster_info::ClusterInfo;
|
||||
use solana_ledger::blockstore::Blockstore;
|
||||
use crate::{cluster_info::ClusterInfo, cluster_slots::ClusterSlots};
|
||||
use solana_ledger::blockstore::{Blockstore, CompletedSlotsReceiver};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::RecvTimeoutError,
|
||||
{Arc, RwLock},
|
||||
},
|
||||
thread::{self, Builder, JoinHandle},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
pub type ClusterSlotsUpdateReceiver = Receiver<Vec<Slot>>;
|
||||
pub type ClusterSlotsUpdateSender = Sender<Vec<Slot>>;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct ClusterSlotsServiceTiming {
|
||||
pub lowest_slot_elapsed: u64,
|
||||
pub process_cluster_slots_updates_elapsed: u64,
|
||||
pub update_completed_slots_elapsed: u64,
|
||||
}
|
||||
|
||||
impl ClusterSlotsServiceTiming {
|
||||
fn update(&mut self, lowest_slot_elapsed: u64, process_cluster_slots_updates_elapsed: u64) {
|
||||
fn update(&mut self, lowest_slot_elapsed: u64, update_completed_slots_elapsed: u64) {
|
||||
self.lowest_slot_elapsed += lowest_slot_elapsed;
|
||||
self.process_cluster_slots_updates_elapsed += process_cluster_slots_updates_elapsed;
|
||||
self.update_completed_slots_elapsed += update_completed_slots_elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +36,12 @@ impl ClusterSlotsService {
|
||||
cluster_slots: Arc<ClusterSlots>,
|
||||
bank_forks: Arc<RwLock<BankForks>>,
|
||||
cluster_info: Arc<ClusterInfo>,
|
||||
cluster_slots_update_receiver: ClusterSlotsUpdateReceiver,
|
||||
completed_slots_receiver: CompletedSlotsReceiver,
|
||||
exit: Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
let id = cluster_info.id();
|
||||
Self::initialize_lowest_slot(id, &blockstore, &cluster_info);
|
||||
Self::initialize_epoch_slots(&bank_forks, &cluster_info);
|
||||
Self::initialize_epoch_slots(&blockstore, &cluster_info, &completed_slots_receiver);
|
||||
let t_cluster_slots_service = Builder::new()
|
||||
.name("solana-cluster-slots-service".to_string())
|
||||
.spawn(move || {
|
||||
@@ -54,7 +50,7 @@ impl ClusterSlotsService {
|
||||
cluster_slots,
|
||||
bank_forks,
|
||||
cluster_info,
|
||||
cluster_slots_update_receiver,
|
||||
completed_slots_receiver,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
@@ -74,7 +70,7 @@ impl ClusterSlotsService {
|
||||
cluster_slots: Arc<ClusterSlots>,
|
||||
bank_forks: Arc<RwLock<BankForks>>,
|
||||
cluster_info: Arc<ClusterInfo>,
|
||||
cluster_slots_update_receiver: ClusterSlotsUpdateReceiver,
|
||||
completed_slots_receiver: CompletedSlotsReceiver,
|
||||
exit: Arc<AtomicBool>,
|
||||
) {
|
||||
let mut cluster_slots_service_timing = ClusterSlotsServiceTiming::default();
|
||||
@@ -83,8 +79,7 @@ impl ClusterSlotsService {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let slots = match cluster_slots_update_receiver.recv_timeout(Duration::from_millis(200))
|
||||
{
|
||||
let slots = match completed_slots_receiver.recv_timeout(Duration::from_millis(200)) {
|
||||
Ok(slots) => Some(slots),
|
||||
Err(RecvTimeoutError::Timeout) => None,
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
@@ -98,21 +93,17 @@ impl ClusterSlotsService {
|
||||
let lowest_slot = blockstore.lowest_slot();
|
||||
Self::update_lowest_slot(&id, lowest_slot, &cluster_info);
|
||||
lowest_slot_elapsed.stop();
|
||||
let mut process_cluster_slots_updates_elapsed =
|
||||
Measure::start("process_cluster_slots_updates_elapsed");
|
||||
let mut update_completed_slots_elapsed =
|
||||
Measure::start("update_completed_slots_elapsed");
|
||||
if let Some(slots) = slots {
|
||||
Self::process_cluster_slots_updates(
|
||||
slots,
|
||||
&cluster_slots_update_receiver,
|
||||
&cluster_info,
|
||||
);
|
||||
Self::update_completed_slots(slots, &completed_slots_receiver, &cluster_info);
|
||||
}
|
||||
cluster_slots.update(new_root, &cluster_info, &bank_forks);
|
||||
process_cluster_slots_updates_elapsed.stop();
|
||||
update_completed_slots_elapsed.stop();
|
||||
|
||||
cluster_slots_service_timing.update(
|
||||
lowest_slot_elapsed.as_us(),
|
||||
process_cluster_slots_updates_elapsed.as_us(),
|
||||
update_completed_slots_elapsed.as_us(),
|
||||
);
|
||||
|
||||
if last_stats.elapsed().as_secs() > 2 {
|
||||
@@ -124,8 +115,8 @@ impl ClusterSlotsService {
|
||||
i64
|
||||
),
|
||||
(
|
||||
"process_cluster_slots_updates_elapsed",
|
||||
cluster_slots_service_timing.process_cluster_slots_updates_elapsed,
|
||||
"update_completed_slots_elapsed",
|
||||
cluster_slots_service_timing.update_completed_slots_elapsed,
|
||||
i64
|
||||
),
|
||||
);
|
||||
@@ -135,12 +126,12 @@ impl ClusterSlotsService {
|
||||
}
|
||||
}
|
||||
|
||||
fn process_cluster_slots_updates(
|
||||
fn update_completed_slots(
|
||||
mut slots: Vec<Slot>,
|
||||
cluster_slots_update_receiver: &ClusterSlotsUpdateReceiver,
|
||||
completed_slots_receiver: &CompletedSlotsReceiver,
|
||||
cluster_info: &ClusterInfo,
|
||||
) {
|
||||
while let Ok(mut more) = cluster_slots_update_receiver.try_recv() {
|
||||
while let Ok(mut more) = completed_slots_receiver.try_recv() {
|
||||
slots.append(&mut more);
|
||||
}
|
||||
#[allow(clippy::stable_sort_primitive)]
|
||||
@@ -163,16 +154,30 @@ impl ClusterSlotsService {
|
||||
cluster_info.push_lowest_slot(*id, lowest_slot);
|
||||
}
|
||||
|
||||
fn initialize_epoch_slots(bank_forks: &RwLock<BankForks>, cluster_info: &ClusterInfo) {
|
||||
// TODO: Should probably incorporate slots that were replayed on startup,
|
||||
// and maybe some that were frozen < snapshot root in case validators restart
|
||||
// from newer snapshots and lose history.
|
||||
let frozen_banks = bank_forks.read().unwrap().frozen_banks();
|
||||
let mut frozen_bank_slots: Vec<Slot> = frozen_banks.keys().cloned().collect();
|
||||
frozen_bank_slots.sort_unstable();
|
||||
fn initialize_epoch_slots(
|
||||
blockstore: &Blockstore,
|
||||
cluster_info: &ClusterInfo,
|
||||
completed_slots_receiver: &CompletedSlotsReceiver,
|
||||
) {
|
||||
let root = blockstore.last_root();
|
||||
let mut slots: Vec<_> = blockstore
|
||||
.live_slots_iterator(root)
|
||||
.filter_map(|(slot, slot_meta)| {
|
||||
if slot_meta.is_full() {
|
||||
Some(slot)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !frozen_bank_slots.is_empty() {
|
||||
cluster_info.push_epoch_slots(&frozen_bank_slots);
|
||||
while let Ok(mut more) = completed_slots_receiver.try_recv() {
|
||||
slots.append(&mut more);
|
||||
}
|
||||
slots.sort_unstable();
|
||||
slots.dedup();
|
||||
if !slots.is_empty() {
|
||||
cluster_info.push_epoch_slots(&slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +185,7 @@ impl ClusterSlotsService {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use solana_gossip::{cluster_info::Node, crds_value::CrdsValueLabel};
|
||||
use crate::{cluster_info::Node, crds_value::CrdsValueLabel};
|
||||
|
||||
#[test]
|
||||
pub fn test_update_lowest_slot() {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
use crate::consensus::Stake;
|
||||
use crate::{consensus::Stake, rpc_subscriptions::RpcSubscriptions};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_metrics::datapoint_info;
|
||||
use solana_rpc::rpc_subscriptions::RpcSubscriptions;
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
commitment::{BlockCommitment, BlockCommitmentCache, CommitmentSlots, VOTE_THRESHOLD_SIZE},
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use crate::{max_slots::MaxSlots, rpc_subscriptions::RpcSubscriptions};
|
||||
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
||||
use solana_ledger::blockstore::{Blockstore, CompletedDataSetInfo};
|
||||
use solana_ledger::entry::Entry;
|
||||
use solana_rpc::{max_slots::MaxSlots, rpc_subscriptions::RpcSubscriptions};
|
||||
use solana_sdk::signature::Signature;
|
||||
use std::{
|
||||
sync::{
|
||||
|
@@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice,
|
||||
latest_validator_votes_for_frozen_banks::LatestValidatorVotesForFrozenBanks,
|
||||
progress_map::{LockoutIntervals, ProgressMap},
|
||||
};
|
||||
@@ -92,8 +91,6 @@ pub type Stake = u64;
|
||||
pub type VotedStakes = HashMap<Slot, Stake>;
|
||||
pub type PubkeyVotes = Vec<(Pubkey, Slot)>;
|
||||
|
||||
// lint warning "bank_weight is never read"
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ComputedBankState {
|
||||
pub voted_stakes: VotedStakes,
|
||||
pub total_stake: Stake,
|
||||
@@ -267,7 +264,7 @@ impl Tower {
|
||||
};
|
||||
for vote in &vote_state.votes {
|
||||
lockout_intervals
|
||||
.entry(vote.last_locked_out_slot())
|
||||
.entry(vote.expiration_slot())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((vote.slot, key));
|
||||
}
|
||||
@@ -530,31 +527,31 @@ impl Tower {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_locked_out(&self, slot: Slot, ancestors: &HashSet<Slot>) -> bool {
|
||||
pub fn is_locked_out(&self, slot: Slot, ancestors: &HashMap<Slot, HashSet<Slot>>) -> bool {
|
||||
assert!(ancestors.contains_key(&slot));
|
||||
|
||||
if !self.is_recent(slot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if a slot is locked out by simulating adding a vote for that
|
||||
// slot to the current lockouts to pop any expired votes. If any of the
|
||||
// remaining voted slots are on a different fork from the checked slot,
|
||||
// it's still locked out.
|
||||
let mut lockouts = self.lockouts.clone();
|
||||
lockouts.process_slot_vote_unchecked(slot);
|
||||
for vote in &lockouts.votes {
|
||||
if slot != vote.slot && !ancestors.contains(&vote.slot) {
|
||||
if vote.slot == slot {
|
||||
continue;
|
||||
}
|
||||
if !ancestors[&slot].contains(&vote.slot) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(root_slot) = lockouts.root_slot {
|
||||
// This case should never happen because bank forks purges all
|
||||
// non-descendants of the root every time root is set
|
||||
if slot != root_slot {
|
||||
// This case should never happen because bank forks purges all
|
||||
// non-descendants of the root every time root is set
|
||||
assert!(
|
||||
ancestors.contains(&root_slot),
|
||||
ancestors[&slot].contains(&root_slot),
|
||||
"ancestors: {:?}, slot: {} root: {}",
|
||||
ancestors,
|
||||
ancestors[&slot],
|
||||
slot,
|
||||
root_slot
|
||||
);
|
||||
@@ -564,17 +561,6 @@ impl Tower {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_candidate_slot_descendant_of_last_vote(
|
||||
candidate_slot: Slot,
|
||||
last_voted_slot: Slot,
|
||||
ancestors: &HashMap<Slot, HashSet<u64>>,
|
||||
) -> Option<bool> {
|
||||
ancestors
|
||||
.get(&candidate_slot)
|
||||
.map(|candidate_slot_ancestors| candidate_slot_ancestors.contains(&last_voted_slot))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn make_check_switch_threshold_decision(
|
||||
&self,
|
||||
switch_slot: u64,
|
||||
@@ -583,11 +569,9 @@ impl Tower {
|
||||
progress: &ProgressMap,
|
||||
total_stake: u64,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||
latest_validator_votes_for_frozen_banks: &LatestValidatorVotesForFrozenBanks,
|
||||
heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice,
|
||||
) -> SwitchForkDecision {
|
||||
self.last_voted_slot_hash()
|
||||
.map(|(last_voted_slot, last_voted_hash)| {
|
||||
self.last_voted_slot()
|
||||
.map(|last_voted_slot| {
|
||||
let root = self.root();
|
||||
let empty_ancestors = HashSet::default();
|
||||
let empty_ancestors_due_to_minor_unsynced_ledger = || {
|
||||
@@ -676,7 +660,7 @@ impl Tower {
|
||||
if last_vote_ancestors.contains(&switch_slot) {
|
||||
if self.is_stray_last_vote() {
|
||||
return suspended_decision_due_to_major_unsynced_ledger();
|
||||
} else if let Some(latest_duplicate_ancestor) = heaviest_subtree_fork_choice.latest_invalid_ancestor(&(last_voted_slot, last_voted_hash)) {
|
||||
} else if let Some(latest_duplicate_ancestor) = progress.latest_unconfirmed_duplicate_ancestor(last_voted_slot) {
|
||||
// We're rolling back because one of the ancestors of the last vote was a duplicate. In this
|
||||
// case, it's acceptable if the switch candidate is one of ancestors of the previous vote,
|
||||
// just fail the switch check because there's no point in voting on an ancestor. ReplayStage
|
||||
@@ -718,9 +702,13 @@ impl Tower {
|
||||
// then use this bank as a representative for the fork.
|
||||
|| descendants.iter().any(|d| progress.get_fork_stats(*d).map(|stats| stats.computed).unwrap_or(false))
|
||||
|| *candidate_slot == last_voted_slot
|
||||
// Ignore if the `candidate_slot` is a descendant of the `last_voted_slot`, since we do not
|
||||
// want to count votes on the same fork.
|
||||
|| Self::is_candidate_slot_descendant_of_last_vote(*candidate_slot, last_voted_slot, ancestors).expect("exists in descendants map, so must exist in ancestors map")
|
||||
|| ancestors
|
||||
.get(&candidate_slot)
|
||||
.expect(
|
||||
"empty descendants implies this is a child, not parent of root, so must
|
||||
exist in the ancestors map",
|
||||
)
|
||||
.contains(&last_voted_slot)
|
||||
|| *candidate_slot <= root
|
||||
{
|
||||
continue;
|
||||
@@ -740,9 +728,8 @@ impl Tower {
|
||||
.unwrap()
|
||||
.fork_stats
|
||||
.lockout_intervals;
|
||||
// Find any locked out intervals for vote accounts in this bank with
|
||||
// `lockout_interval_end` >= `last_vote`, which implies they are locked out at
|
||||
// `last_vote` on another fork.
|
||||
// Find any locked out intervals in this bank with endpoint >= last_vote,
|
||||
// implies they are locked out at last_vote
|
||||
for (_lockout_interval_end, intervals_keyed_by_end) in lockout_intervals.range((Included(last_voted_slot), Unbounded)) {
|
||||
for (lockout_interval_start, vote_account_pubkey) in intervals_keyed_by_end {
|
||||
if locked_out_vote_accounts.contains(vote_account_pubkey) {
|
||||
@@ -765,66 +752,21 @@ impl Tower {
|
||||
.map(|(stake, _)| *stake)
|
||||
.unwrap_or(0);
|
||||
locked_out_stake += stake;
|
||||
if (locked_out_stake as f64 / total_stake as f64) > SWITCH_FORK_THRESHOLD {
|
||||
return SwitchForkDecision::SwitchProof(switch_proof);
|
||||
}
|
||||
locked_out_vote_accounts.insert(vote_account_pubkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the latest votes for potentially gossip votes that haven't landed yet
|
||||
for (vote_account_pubkey, (candidate_latest_frozen_vote, _candidate_latest_frozen_vote_hash)) in latest_validator_votes_for_frozen_banks.max_gossip_frozen_votes() {
|
||||
if locked_out_vote_accounts.contains(&vote_account_pubkey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if *candidate_latest_frozen_vote > last_voted_slot
|
||||
&&
|
||||
// Because `candidate_latest_frozen_vote` is the last vote made by some validator
|
||||
// in the cluster for a frozen bank `B` observed through gossip, we may have cleared
|
||||
// that frozen bank `B` because we `set_root(root)` for a `root` on a different fork,
|
||||
// like so:
|
||||
//
|
||||
// |----------X ------candidate_latest_frozen_vote (frozen)
|
||||
// old root
|
||||
// |----------new root ----last_voted_slot
|
||||
//
|
||||
// In most cases, because `last_voted_slot` must be a descendant of `root`, then
|
||||
// if `candidate_latest_frozen_vote` is not found in the ancestors/descendants map (recall these
|
||||
// directly reflect the state of BankForks), this implies that `B` was pruned from BankForks
|
||||
// because it was on a different fork than `last_voted_slot`, and thus this vote for `candidate_latest_frozen_vote`
|
||||
// should be safe to count towards the switching proof:
|
||||
//
|
||||
// However, there is also the possibility that `last_voted_slot` is a stray, in which
|
||||
// case we cannot make this conclusion as we do not know the ancestors/descendants
|
||||
// of strays. Hence we err on the side of caution here and ignore this vote. This
|
||||
// is ok because validators voting on different unrooted forks should eventually vote
|
||||
// on some descendant of the root, at which time they can be included in switching proofs.
|
||||
!Self::is_candidate_slot_descendant_of_last_vote(
|
||||
*candidate_latest_frozen_vote, last_voted_slot, ancestors)
|
||||
.unwrap_or(true) {
|
||||
let stake = epoch_vote_accounts
|
||||
.get(vote_account_pubkey)
|
||||
.map(|(stake, _)| *stake)
|
||||
.unwrap_or(0);
|
||||
locked_out_stake += stake;
|
||||
if (locked_out_stake as f64 / total_stake as f64) > SWITCH_FORK_THRESHOLD {
|
||||
return SwitchForkDecision::SwitchProof(switch_proof);
|
||||
}
|
||||
locked_out_vote_accounts.insert(vote_account_pubkey);
|
||||
}
|
||||
if (locked_out_stake as f64 / total_stake as f64) > SWITCH_FORK_THRESHOLD {
|
||||
SwitchForkDecision::SwitchProof(switch_proof)
|
||||
} else {
|
||||
SwitchForkDecision::FailedSwitchThreshold(locked_out_stake, total_stake)
|
||||
}
|
||||
|
||||
// We have not detected sufficient lockout past the last voted slot to generate
|
||||
// a switching proof
|
||||
SwitchForkDecision::FailedSwitchThreshold(locked_out_stake, total_stake)
|
||||
})
|
||||
.unwrap_or(SwitchForkDecision::SameFork)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn check_switch_threshold(
|
||||
&mut self,
|
||||
switch_slot: u64,
|
||||
@@ -833,8 +775,6 @@ impl Tower {
|
||||
progress: &ProgressMap,
|
||||
total_stake: u64,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||
latest_validator_votes_for_frozen_banks: &LatestValidatorVotesForFrozenBanks,
|
||||
heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice,
|
||||
) -> SwitchForkDecision {
|
||||
let decision = self.make_check_switch_threshold_decision(
|
||||
switch_slot,
|
||||
@@ -843,8 +783,6 @@ impl Tower {
|
||||
progress,
|
||||
total_stake,
|
||||
epoch_vote_accounts,
|
||||
latest_validator_votes_for_frozen_banks,
|
||||
heaviest_subtree_fork_choice,
|
||||
);
|
||||
let new_check = Some((switch_slot, decision.clone()));
|
||||
if new_check != self.last_switch_threshold_check {
|
||||
@@ -1364,11 +1302,11 @@ pub mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
cluster_info_vote_listener::VoteTracker,
|
||||
cluster_slot_state_verifier::{DuplicateSlotsTracker, GossipDuplicateConfirmedSlots},
|
||||
cluster_slot_state_verifier::GossipDuplicateConfirmedSlots,
|
||||
cluster_slots::ClusterSlots,
|
||||
fork_choice::{ForkChoice, SelectVoteAndResetForkResult},
|
||||
heaviest_subtree_fork_choice::SlotHashKey,
|
||||
progress_map::ForkProgress,
|
||||
fork_choice::SelectVoteAndResetForkResult,
|
||||
heaviest_subtree_fork_choice::{HeaviestSubtreeForkChoice, SlotHashKey},
|
||||
progress_map::{DuplicateStats, ForkProgress},
|
||||
replay_stage::{HeaviestForkFailures, ReplayStage},
|
||||
unfrozen_gossip_verified_vote_hashes::UnfrozenGossipVerifiedVoteHashes,
|
||||
};
|
||||
@@ -1382,7 +1320,7 @@ pub mod test {
|
||||
},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
|
||||
account::{Account, AccountSharedData, WritableAccount},
|
||||
clock::Slot,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
@@ -1445,9 +1383,9 @@ pub mod test {
|
||||
|
||||
while let Some(visit) = walk.get() {
|
||||
let slot = visit.node().data;
|
||||
self.progress
|
||||
.entry(slot)
|
||||
.or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0));
|
||||
self.progress.entry(slot).or_insert_with(|| {
|
||||
ForkProgress::new(Hash::default(), None, DuplicateStats::default(), None, 0, 0)
|
||||
});
|
||||
if self.bank_forks.read().unwrap().get(slot).is_some() {
|
||||
walk.forward();
|
||||
continue;
|
||||
@@ -1535,8 +1473,6 @@ pub mod test {
|
||||
&descendants,
|
||||
&self.progress,
|
||||
tower,
|
||||
&self.latest_validator_votes_for_frozen_banks,
|
||||
&self.heaviest_subtree_fork_choice,
|
||||
);
|
||||
|
||||
// Make sure this slot isn't locked out or failing threshold
|
||||
@@ -1561,7 +1497,6 @@ pub mod test {
|
||||
&AbsRequestSender::default(),
|
||||
None,
|
||||
&mut self.heaviest_subtree_fork_choice,
|
||||
&mut DuplicateSlotsTracker::default(),
|
||||
&mut GossipDuplicateConfirmedSlots::default(),
|
||||
&mut UnfrozenGossipVerifiedVoteHashes::default(),
|
||||
&mut true,
|
||||
@@ -1600,7 +1535,9 @@ pub mod test {
|
||||
) {
|
||||
self.progress
|
||||
.entry(slot)
|
||||
.or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0))
|
||||
.or_insert_with(|| {
|
||||
ForkProgress::new(Hash::default(), None, DuplicateStats::default(), None, 0, 0)
|
||||
})
|
||||
.fork_stats
|
||||
.lockout_intervals
|
||||
.entry(lockout_interval.1)
|
||||
@@ -1707,7 +1644,14 @@ pub mod test {
|
||||
let mut progress = ProgressMap::default();
|
||||
progress.insert(
|
||||
0,
|
||||
ForkProgress::new(bank0.last_blockhash(), None, None, 0, 0),
|
||||
ForkProgress::new(
|
||||
bank0.last_blockhash(),
|
||||
None,
|
||||
DuplicateStats::default(),
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
);
|
||||
let bank_forks = BankForks::new(bank0);
|
||||
let heaviest_subtree_fork_choice =
|
||||
@@ -1718,11 +1662,11 @@ pub mod test {
|
||||
fn gen_stakes(stake_votes: &[(u64, &[u64])]) -> Vec<(Pubkey, (u64, ArcVoteAccount))> {
|
||||
let mut stakes = vec![];
|
||||
for (lamports, votes) in stake_votes {
|
||||
let mut account = AccountSharedData::from(Account {
|
||||
let mut account = AccountSharedData {
|
||||
data: vec![0; VoteState::size_of()],
|
||||
lamports: *lamports,
|
||||
..Account::default()
|
||||
});
|
||||
..AccountSharedData::default()
|
||||
};
|
||||
let mut vote_state = VoteState::default();
|
||||
for slot in *votes {
|
||||
vote_state.process_slot_vote_unchecked(*slot);
|
||||
@@ -1842,8 +1786,7 @@ pub mod test {
|
||||
/ (tr(44)
|
||||
// Minor fork 2
|
||||
/ (tr(45) / (tr(46) / (tr(47) / (tr(48) / (tr(49) / (tr(50)))))))
|
||||
/ (tr(110)))
|
||||
/ tr(112))));
|
||||
/ (tr(110))))));
|
||||
|
||||
// Fill the BankForks according to the above fork structure
|
||||
vote_simulator.fill_bank_forks(forks, &HashMap::new());
|
||||
@@ -1866,46 +1809,21 @@ pub mod test {
|
||||
let mut tower = Tower::new_with_key(&vote_simulator.node_pubkeys[0]);
|
||||
|
||||
// Last vote is 47
|
||||
tower.record_vote(
|
||||
47,
|
||||
vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(47)
|
||||
.unwrap()
|
||||
.hash(),
|
||||
);
|
||||
tower.record_vote(47, Hash::default());
|
||||
|
||||
// Trying to switch to an ancestor of last vote should only not panic
|
||||
// if the current vote has a duplicate ancestor
|
||||
let ancestor_of_voted_slot = 43;
|
||||
let duplicate_ancestor1 = 44;
|
||||
let duplicate_ancestor2 = 45;
|
||||
vote_simulator
|
||||
.heaviest_subtree_fork_choice
|
||||
.mark_fork_invalid_candidate(&(
|
||||
duplicate_ancestor1,
|
||||
vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(duplicate_ancestor1)
|
||||
.unwrap()
|
||||
.hash(),
|
||||
));
|
||||
vote_simulator
|
||||
.heaviest_subtree_fork_choice
|
||||
.mark_fork_invalid_candidate(&(
|
||||
duplicate_ancestor2,
|
||||
vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(duplicate_ancestor2)
|
||||
.unwrap()
|
||||
.hash(),
|
||||
));
|
||||
vote_simulator.progress.set_unconfirmed_duplicate_slot(
|
||||
duplicate_ancestor1,
|
||||
&descendants.get(&duplicate_ancestor1).unwrap(),
|
||||
);
|
||||
vote_simulator.progress.set_unconfirmed_duplicate_slot(
|
||||
duplicate_ancestor2,
|
||||
&descendants.get(&duplicate_ancestor2).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
tower.check_switch_threshold(
|
||||
ancestor_of_voted_slot,
|
||||
@@ -1914,8 +1832,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchDuplicateRollback(duplicate_ancestor2)
|
||||
);
|
||||
@@ -1928,18 +1844,11 @@ pub mod test {
|
||||
confirm_ancestors.push(duplicate_ancestor2);
|
||||
}
|
||||
for (i, duplicate_ancestor) in confirm_ancestors.into_iter().enumerate() {
|
||||
vote_simulator
|
||||
.heaviest_subtree_fork_choice
|
||||
.mark_fork_valid_candidate(&(
|
||||
duplicate_ancestor,
|
||||
vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(duplicate_ancestor)
|
||||
.unwrap()
|
||||
.hash(),
|
||||
));
|
||||
vote_simulator.progress.set_confirmed_duplicate_slot(
|
||||
duplicate_ancestor,
|
||||
ancestors.get(&duplicate_ancestor).unwrap(),
|
||||
&descendants.get(&duplicate_ancestor).unwrap(),
|
||||
);
|
||||
let res = tower.check_switch_threshold(
|
||||
ancestor_of_voted_slot,
|
||||
&ancestors,
|
||||
@@ -1947,8 +1856,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
);
|
||||
if i == 0 {
|
||||
assert_eq!(
|
||||
@@ -1984,8 +1891,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SameFork
|
||||
);
|
||||
@@ -1999,8 +1904,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2016,8 +1919,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2033,8 +1934,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2050,8 +1949,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2069,8 +1966,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2086,8 +1981,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SwitchProof(Hash::default())
|
||||
);
|
||||
@@ -2104,8 +1997,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SwitchProof(Hash::default())
|
||||
);
|
||||
@@ -2131,115 +2022,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_threshold_use_gossip_votes() {
|
||||
let num_validators = 2;
|
||||
let (bank0, mut vote_simulator, total_stake) = setup_switch_test(2);
|
||||
let ancestors = vote_simulator.bank_forks.read().unwrap().ancestors();
|
||||
let descendants = vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.descendants()
|
||||
.clone();
|
||||
let mut tower = Tower::new_with_key(&vote_simulator.node_pubkeys[0]);
|
||||
let other_vote_account = vote_simulator.vote_pubkeys[1];
|
||||
|
||||
// Last vote is 47
|
||||
tower.record_vote(47, Hash::default());
|
||||
|
||||
// Trying to switch to another fork at 110 should fail
|
||||
assert_eq!(
|
||||
tower.check_switch_threshold(
|
||||
110,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, num_validators * 10000)
|
||||
);
|
||||
|
||||
// Adding a vote on the descendant shouldn't count toward the switch threshold
|
||||
vote_simulator.simulate_lockout_interval(50, (49, 100), &other_vote_account);
|
||||
assert_eq!(
|
||||
tower.check_switch_threshold(
|
||||
110,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
|
||||
// Adding a later vote from gossip that isn't on the same fork should count toward the
|
||||
// switch threshold
|
||||
vote_simulator
|
||||
.latest_validator_votes_for_frozen_banks
|
||||
.check_add_vote(
|
||||
other_vote_account,
|
||||
112,
|
||||
Some(
|
||||
vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(112)
|
||||
.unwrap()
|
||||
.hash(),
|
||||
),
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tower.check_switch_threshold(
|
||||
110,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SwitchProof(Hash::default())
|
||||
);
|
||||
|
||||
// If we now set a root that causes slot 112 to be purged from BankForks, then
|
||||
// the switch proof will now fail since that validator's vote can no longer be
|
||||
// included in the switching proof
|
||||
vote_simulator.set_root(44);
|
||||
let ancestors = vote_simulator.bank_forks.read().unwrap().ancestors();
|
||||
let descendants = vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.descendants()
|
||||
.clone();
|
||||
assert_eq!(
|
||||
tower.check_switch_threshold(
|
||||
110,
|
||||
&ancestors,
|
||||
&descendants,
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2521,14 +2303,16 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_is_locked_out_empty() {
|
||||
let tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors = HashSet::new();
|
||||
let ancestors = vec![(0, HashSet::new())].into_iter().collect();
|
||||
assert!(!tower.is_locked_out(0, &ancestors));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_locked_out_root_slot_child_pass() {
|
||||
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
|
||||
let ancestors = vec![(1, vec![0].into_iter().collect())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
tower.lockouts.root_slot = Some(0);
|
||||
assert!(!tower.is_locked_out(1, &ancestors));
|
||||
}
|
||||
@@ -2536,7 +2320,9 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_is_locked_out_root_slot_sibling_fail() {
|
||||
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
|
||||
let ancestors = vec![(2, vec![0].into_iter().collect())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
tower.lockouts.root_slot = Some(0);
|
||||
tower.record_vote(1, Hash::default());
|
||||
assert!(tower.is_locked_out(2, &ancestors));
|
||||
@@ -2567,7 +2353,9 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_is_locked_out_double_vote() {
|
||||
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
|
||||
let ancestors = vec![(1, vec![0].into_iter().collect()), (0, HashSet::new())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
tower.record_vote(0, Hash::default());
|
||||
tower.record_vote(1, Hash::default());
|
||||
assert!(tower.is_locked_out(0, &ancestors));
|
||||
@@ -2576,7 +2364,9 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_is_locked_out_child() {
|
||||
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
|
||||
let ancestors = vec![(1, vec![0].into_iter().collect())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
tower.record_vote(0, Hash::default());
|
||||
assert!(!tower.is_locked_out(1, &ancestors));
|
||||
}
|
||||
@@ -2584,7 +2374,13 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_is_locked_out_sibling() {
|
||||
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
|
||||
let ancestors = vec![
|
||||
(0, HashSet::new()),
|
||||
(1, vec![0].into_iter().collect()),
|
||||
(2, vec![0].into_iter().collect()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
tower.record_vote(0, Hash::default());
|
||||
tower.record_vote(1, Hash::default());
|
||||
assert!(tower.is_locked_out(2, &ancestors));
|
||||
@@ -2593,7 +2389,13 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_is_locked_out_last_vote_expired() {
|
||||
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
|
||||
let ancestors = vec![
|
||||
(0, HashSet::new()),
|
||||
(1, vec![0].into_iter().collect()),
|
||||
(4, vec![0].into_iter().collect()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
tower.record_vote(0, Hash::default());
|
||||
tower.record_vote(1, Hash::default());
|
||||
assert!(!tower.is_locked_out(4, &ancestors));
|
||||
@@ -2657,7 +2459,7 @@ pub mod test {
|
||||
});
|
||||
let set: HashSet<u64> = vec![0u64, 1u64].into_iter().collect();
|
||||
let ancestors: HashMap<u64, HashSet<u64>> = [(2u64, set)].iter().cloned().collect();
|
||||
Tower::update_ancestor_voted_stakes(&mut voted_stakes, 2, account.lamports(), &ancestors);
|
||||
Tower::update_ancestor_voted_stakes(&mut voted_stakes, 2, account.lamports, &ancestors);
|
||||
assert_eq!(voted_stakes[&0], 1);
|
||||
assert_eq!(voted_stakes[&1], 1);
|
||||
assert_eq!(voted_stakes[&2], 1);
|
||||
@@ -2918,8 +2720,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SameFork
|
||||
);
|
||||
@@ -2933,8 +2733,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -2949,8 +2747,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SwitchProof(Hash::default())
|
||||
);
|
||||
@@ -3020,8 +2816,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -3036,8 +2830,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
|
||||
);
|
||||
@@ -3052,8 +2844,6 @@ pub mod test {
|
||||
&vote_simulator.progress,
|
||||
total_stake,
|
||||
bank0.epoch_vote_accounts(0).unwrap(),
|
||||
&vote_simulator.latest_validator_votes_for_frozen_banks,
|
||||
&vote_simulator.heaviest_subtree_fork_choice,
|
||||
),
|
||||
SwitchForkDecision::SwitchProof(Hash::default())
|
||||
);
|
||||
|
@@ -1,17 +1,16 @@
|
||||
use {
|
||||
crate::crds_value::MAX_WALLCLOCK,
|
||||
solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
rpc_port,
|
||||
sanitize::{Sanitize, SanitizeError},
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
},
|
||||
std::net::{IpAddr, SocketAddr},
|
||||
};
|
||||
use crate::crds_value::MAX_WALLCLOCK;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
#[cfg(test)]
|
||||
use solana_sdk::rpc_port;
|
||||
use solana_sdk::sanitize::{Sanitize, SanitizeError};
|
||||
#[cfg(test)]
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
/// Structure representing a node on the network
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, AbiExample, Deserialize, Serialize)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, AbiExample)]
|
||||
pub struct ContactInfo {
|
||||
pub id: Pubkey,
|
||||
/// gossip address
|
||||
@@ -49,13 +48,34 @@ impl Sanitize for ContactInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for ContactInfo {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.id.cmp(&other.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for ContactInfo {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ContactInfo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ContactInfo {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! socketaddr {
|
||||
($ip:expr, $port:expr) => {
|
||||
std::net::SocketAddr::from((std::net::Ipv4Addr::from($ip), $port))
|
||||
};
|
||||
($str:expr) => {{
|
||||
$str.parse::<std::net::SocketAddr>().unwrap()
|
||||
let a: std::net::SocketAddr = $str.parse().unwrap();
|
||||
a
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
@@ -134,8 +154,8 @@ impl ContactInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Used in tests
|
||||
pub fn new_with_pubkey_socketaddr(pubkey: &Pubkey, bind_addr: &SocketAddr) -> Self {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new_with_pubkey_socketaddr(pubkey: &Pubkey, bind_addr: &SocketAddr) -> Self {
|
||||
fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr {
|
||||
let mut nxt_addr = *addr;
|
||||
nxt_addr.set_port(addr.port() + nxt);
|
||||
@@ -168,8 +188,8 @@ impl ContactInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Used in tests
|
||||
pub fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
|
||||
let keypair = Keypair::new();
|
||||
Self::new_with_pubkey_socketaddr(&keypair.pubkey(), bind_addr)
|
||||
}
|
@@ -19,35 +19,30 @@
|
||||
//! CrdsValue enums.
|
||||
//!
|
||||
//! Merge strategy is implemented in:
|
||||
//! fn overrides(value: &CrdsValue, other: &VersionedCrdsValue) -> bool
|
||||
//! impl PartialOrd for VersionedCrdsValue
|
||||
//!
|
||||
//! 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::{
|
||||
contact_info::ContactInfo,
|
||||
crds_shards::CrdsShards,
|
||||
crds_value::{CrdsData, CrdsValue, CrdsValueLabel, LowestSlot},
|
||||
},
|
||||
bincode::serialize,
|
||||
indexmap::{
|
||||
map::{rayon::ParValues, Entry, IndexMap},
|
||||
set::IndexSet,
|
||||
},
|
||||
rayon::{prelude::*, ThreadPool},
|
||||
solana_sdk::{
|
||||
hash::{hash, Hash},
|
||||
pubkey::Pubkey,
|
||||
},
|
||||
std::{
|
||||
cmp::Ordering,
|
||||
collections::{hash_map, BTreeMap, HashMap, VecDeque},
|
||||
ops::{Bound, Index, IndexMut},
|
||||
},
|
||||
use crate::contact_info::ContactInfo;
|
||||
use crate::crds_shards::CrdsShards;
|
||||
use crate::crds_value::{CrdsData, CrdsValue, CrdsValueLabel, LowestSlot};
|
||||
use bincode::serialize;
|
||||
use indexmap::map::{rayon::ParValues, Entry, IndexMap};
|
||||
use indexmap::set::IndexSet;
|
||||
use rayon::{prelude::*, ThreadPool};
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{hash_map, BTreeMap, HashMap},
|
||||
ops::{Bound, Index, IndexMut},
|
||||
};
|
||||
|
||||
const CRDS_SHARDS_BITS: u32 = 8;
|
||||
// Limit number of crds values associated with each unique pubkey. This
|
||||
// excludes crds values which by label design are limited per each pubkey.
|
||||
const MAX_CRDS_VALUES_PER_PUBKEY: usize = 32;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Crds {
|
||||
@@ -56,25 +51,26 @@ pub struct Crds {
|
||||
cursor: Cursor, // Next insert ordinal location.
|
||||
shards: CrdsShards,
|
||||
nodes: IndexSet<usize>, // Indices of nodes' ContactInfo.
|
||||
// Indices of Votes keyed by insert order.
|
||||
votes: BTreeMap<u64 /*insert order*/, usize /*index*/>,
|
||||
votes: IndexSet<usize>, // Indices of Vote crds values.
|
||||
// Indices of EpochSlots keyed by insert order.
|
||||
epoch_slots: BTreeMap<u64 /*insert order*/, usize /*index*/>,
|
||||
// Indices of all crds values associated with a node.
|
||||
records: HashMap<Pubkey, IndexSet<usize>>,
|
||||
// Indices of all entries keyed by insert order.
|
||||
entries: BTreeMap<u64 /*insert order*/, usize /*index*/>,
|
||||
// Hash of recently purged values.
|
||||
purged: VecDeque<(Hash, u64 /*timestamp*/)>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum CrdsError {
|
||||
InsertFailed,
|
||||
// Hash of the crds value which failed to insert should be recorded in
|
||||
// failed_inserts to be excluded from the next pull-request.
|
||||
InsertFailed(Hash),
|
||||
UnknownStakes,
|
||||
}
|
||||
|
||||
/// This structure stores some local metadata associated with the CrdsValue
|
||||
/// The implementation of PartialOrd ensures that the "highest" version is always picked to be
|
||||
/// stored in the Crds
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct VersionedCrdsValue {
|
||||
/// Ordinal index indicating insert order.
|
||||
@@ -120,11 +116,10 @@ impl Default for Crds {
|
||||
cursor: Cursor::default(),
|
||||
shards: CrdsShards::new(CRDS_SHARDS_BITS),
|
||||
nodes: IndexSet::default(),
|
||||
votes: BTreeMap::default(),
|
||||
votes: IndexSet::default(),
|
||||
epoch_slots: BTreeMap::default(),
|
||||
records: HashMap::default(),
|
||||
entries: BTreeMap::default(),
|
||||
purged: VecDeque::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,14 +128,6 @@ impl Default for Crds {
|
||||
// Both values should have the same key/label.
|
||||
fn overrides(value: &CrdsValue, other: &VersionedCrdsValue) -> bool {
|
||||
assert_eq!(value.label(), other.value.label(), "labels mismatch!");
|
||||
// Node instances are special cased so that if there are two running
|
||||
// instances of the same node, the more recent start is propagated through
|
||||
// gossip regardless of wallclocks.
|
||||
if let CrdsData::NodeInstance(value) = &value.data {
|
||||
if let Some(out) = value.overrides(&other.value) {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
match value.wallclock().cmp(&other.value.wallclock()) {
|
||||
Ordering::Less => false,
|
||||
Ordering::Greater => true,
|
||||
@@ -165,10 +152,14 @@ impl Crds {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, value: CrdsValue, now: u64) -> Result<(), CrdsError> {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
value: CrdsValue,
|
||||
local_timestamp: u64,
|
||||
) -> Result<Option<VersionedCrdsValue>, CrdsError> {
|
||||
let label = value.label();
|
||||
let pubkey = value.pubkey();
|
||||
let value = VersionedCrdsValue::new(value, self.cursor, now);
|
||||
let value = VersionedCrdsValue::new(value, self.cursor, local_timestamp);
|
||||
match self.table.entry(label) {
|
||||
Entry::Vacant(entry) => {
|
||||
let entry_index = entry.index();
|
||||
@@ -178,7 +169,7 @@ impl Crds {
|
||||
self.nodes.insert(entry_index);
|
||||
}
|
||||
CrdsData::Vote(_, _) => {
|
||||
self.votes.insert(value.ordinal, entry_index);
|
||||
self.votes.insert(entry_index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots.insert(value.ordinal, entry_index);
|
||||
@@ -189,22 +180,15 @@ impl Crds {
|
||||
self.records.entry(pubkey).or_default().insert(entry_index);
|
||||
self.cursor.consume(value.ordinal);
|
||||
entry.insert(value);
|
||||
Ok(())
|
||||
Ok(None)
|
||||
}
|
||||
Entry::Occupied(mut entry) if overrides(&value.value, entry.get()) => {
|
||||
let entry_index = entry.index();
|
||||
self.shards.remove(entry_index, entry.get());
|
||||
self.shards.insert(entry_index, &value);
|
||||
match value.value.data {
|
||||
CrdsData::Vote(_, _) => {
|
||||
self.votes.remove(&entry.get().ordinal);
|
||||
self.votes.insert(value.ordinal, entry_index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots.remove(&entry.get().ordinal);
|
||||
self.epoch_slots.insert(value.ordinal, entry_index);
|
||||
}
|
||||
_ => (),
|
||||
if let CrdsData::EpochSlots(_, _) = value.value.data {
|
||||
self.epoch_slots.remove(&entry.get().ordinal);
|
||||
self.epoch_slots.insert(value.ordinal, entry_index);
|
||||
}
|
||||
self.entries.remove(&entry.get().ordinal);
|
||||
self.entries.insert(value.ordinal, entry_index);
|
||||
@@ -212,24 +196,27 @@ impl Crds {
|
||||
// does not need to be updated.
|
||||
debug_assert_eq!(entry.get().value.pubkey(), pubkey);
|
||||
self.cursor.consume(value.ordinal);
|
||||
self.purged.push_back((entry.get().value_hash, now));
|
||||
entry.insert(value);
|
||||
Ok(())
|
||||
Ok(Some(entry.insert(value)))
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
_ => {
|
||||
trace!(
|
||||
"INSERT FAILED data: {} new.wallclock: {}",
|
||||
value.value.label(),
|
||||
value.value.wallclock(),
|
||||
);
|
||||
if entry.get().value_hash != value.value_hash {
|
||||
self.purged.push_back((value.value_hash, now));
|
||||
}
|
||||
Err(CrdsError::InsertFailed)
|
||||
Err(CrdsError::InsertFailed(value.value_hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&self, label: &CrdsValueLabel) -> Option<&CrdsValue> {
|
||||
self.table.get(label).map(|x| &x.value)
|
||||
}
|
||||
|
||||
pub fn lookup_versioned(&self, label: &CrdsValueLabel) -> Option<&VersionedCrdsValue> {
|
||||
self.table.get(label)
|
||||
}
|
||||
|
||||
pub fn get(&self, label: &CrdsValueLabel) -> Option<&VersionedCrdsValue> {
|
||||
self.table.get(label)
|
||||
}
|
||||
@@ -263,10 +250,15 @@ impl Crds {
|
||||
&'a self,
|
||||
cursor: &'a mut Cursor,
|
||||
) -> impl Iterator<Item = &'a VersionedCrdsValue> {
|
||||
let range = (Bound::Included(cursor.ordinal()), Bound::Unbounded);
|
||||
self.votes.range(range).map(move |(ordinal, index)| {
|
||||
cursor.consume(*ordinal);
|
||||
self.table.index(*index)
|
||||
let since = cursor.ordinal();
|
||||
self.votes.iter().filter_map(move |i| {
|
||||
let entry = self.table.index(*i);
|
||||
if entry.ordinal >= since {
|
||||
cursor.consume(entry.ordinal);
|
||||
Some(entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -326,24 +318,6 @@ impl Crds {
|
||||
self.table.par_values()
|
||||
}
|
||||
|
||||
pub(crate) fn num_purged(&self) -> usize {
|
||||
self.purged.len()
|
||||
}
|
||||
|
||||
pub(crate) fn purged(&self) -> impl IndexedParallelIterator<Item = Hash> + '_ {
|
||||
self.purged.par_iter().map(|(hash, _)| *hash)
|
||||
}
|
||||
|
||||
/// Drops purged value hashes with timestamp less than the given one.
|
||||
pub(crate) fn trim_purged(&mut self, timestamp: u64) {
|
||||
let count = self
|
||||
.purged
|
||||
.iter()
|
||||
.take_while(|(_, ts)| *ts < timestamp)
|
||||
.count();
|
||||
self.purged.drain(..count);
|
||||
}
|
||||
|
||||
/// Returns all crds values which the first 'mask_bits'
|
||||
/// of their hash value is equal to 'mask'.
|
||||
pub fn filter_bitmask(
|
||||
@@ -391,26 +365,50 @@ impl Crds {
|
||||
// returns crds labels of old values to be evicted.
|
||||
let evict = |pubkey, index: &IndexSet<usize>| {
|
||||
let timeout = timeouts.get(pubkey).copied().unwrap_or(default_timeout);
|
||||
// If the origin's contact-info hasn't expired yet then preserve
|
||||
// all associated values.
|
||||
let origin = CrdsValueLabel::ContactInfo(*pubkey);
|
||||
if let Some(origin) = self.table.get(&origin) {
|
||||
if now < origin.local_timestamp.saturating_add(timeout) {
|
||||
return vec![];
|
||||
let local_timestamp = {
|
||||
let origin = CrdsValueLabel::ContactInfo(*pubkey);
|
||||
match self.table.get(&origin) {
|
||||
Some(origin) => origin.local_timestamp,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
// Otherwise check each value's timestamp individually.
|
||||
index
|
||||
};
|
||||
let mut old_labels = Vec::new();
|
||||
// Buffer of crds values to be evicted based on their wallclock.
|
||||
let mut recent_unlimited_labels: Vec<(u64 /*wallclock*/, usize /*index*/)> = index
|
||||
.into_iter()
|
||||
.filter_map(|ix| {
|
||||
let (label, value) = self.table.get_index(*ix).unwrap();
|
||||
if value.local_timestamp.saturating_add(timeout) <= now {
|
||||
Some(label.clone())
|
||||
} else {
|
||||
let expiry_timestamp = value
|
||||
.local_timestamp
|
||||
.max(local_timestamp)
|
||||
.saturating_add(timeout);
|
||||
if expiry_timestamp <= now {
|
||||
old_labels.push(label.clone());
|
||||
None
|
||||
} else {
|
||||
match label.value_space() {
|
||||
Some(_) => None,
|
||||
None => Some((value.value.wallclock(), *ix)),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect();
|
||||
// Number of values to discard from the buffer:
|
||||
let nth = recent_unlimited_labels
|
||||
.len()
|
||||
.saturating_sub(MAX_CRDS_VALUES_PER_PUBKEY);
|
||||
// Partition on wallclock to discard the older ones.
|
||||
if nth > 0 && nth < recent_unlimited_labels.len() {
|
||||
recent_unlimited_labels.select_nth_unstable(nth);
|
||||
}
|
||||
old_labels.extend(
|
||||
recent_unlimited_labels
|
||||
.split_at(nth)
|
||||
.0
|
||||
.iter()
|
||||
.map(|(_ /*wallclock*/, ix)| self.table.get_index(*ix).unwrap().0.clone()),
|
||||
);
|
||||
old_labels
|
||||
};
|
||||
thread_pool.install(|| {
|
||||
self.records
|
||||
@@ -420,19 +418,15 @@ impl Crds {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: &CrdsValueLabel, now: u64) {
|
||||
let (index, _ /*label*/, value) = match self.table.swap_remove_full(key) {
|
||||
Some(entry) => entry,
|
||||
None => return,
|
||||
};
|
||||
self.purged.push_back((value.value_hash, now));
|
||||
pub fn remove(&mut self, key: &CrdsValueLabel) -> Option<VersionedCrdsValue> {
|
||||
let (index, _ /*label*/, value) = self.table.swap_remove_full(key)?;
|
||||
self.shards.remove(index, &value);
|
||||
match value.value.data {
|
||||
CrdsData::ContactInfo(_) => {
|
||||
self.nodes.swap_remove(&index);
|
||||
}
|
||||
CrdsData::Vote(_, _) => {
|
||||
self.votes.remove(&value.ordinal);
|
||||
self.votes.swap_remove(&index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots.remove(&value.ordinal);
|
||||
@@ -466,7 +460,8 @@ impl Crds {
|
||||
self.nodes.insert(index);
|
||||
}
|
||||
CrdsData::Vote(_, _) => {
|
||||
self.votes.insert(value.ordinal, index);
|
||||
self.votes.swap_remove(&size);
|
||||
self.votes.insert(index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots.insert(value.ordinal, index);
|
||||
@@ -479,6 +474,7 @@ impl Crds {
|
||||
records.swap_remove(&size);
|
||||
records.insert(index);
|
||||
}
|
||||
Some(value)
|
||||
}
|
||||
|
||||
/// Returns true if the number of unique pubkeys in the table exceeds the
|
||||
@@ -499,13 +495,12 @@ impl Crds {
|
||||
// e.g. trusted validators, self pubkey, ...
|
||||
keep: &[Pubkey],
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
now: u64,
|
||||
) -> Result</*num purged:*/ usize, CrdsError> {
|
||||
) -> Result<Vec<VersionedCrdsValue>, CrdsError> {
|
||||
if self.should_trim(cap) {
|
||||
let size = self.records.len().saturating_sub(cap);
|
||||
self.drop(size, keep, stakes, now)
|
||||
self.drop(size, keep, stakes)
|
||||
} else {
|
||||
Ok(0)
|
||||
Ok(Vec::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,8 +510,7 @@ impl Crds {
|
||||
size: usize,
|
||||
keep: &[Pubkey],
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
now: u64,
|
||||
) -> Result</*num purged:*/ usize, CrdsError> {
|
||||
) -> Result<Vec<VersionedCrdsValue>, CrdsError> {
|
||||
if stakes.is_empty() {
|
||||
return Err(CrdsError::UnknownStakes);
|
||||
}
|
||||
@@ -536,33 +530,27 @@ impl Crds {
|
||||
.flat_map(|k| &self.records[&k])
|
||||
.map(|k| self.table.get_index(*k).unwrap().0.clone())
|
||||
.collect();
|
||||
for key in &keys {
|
||||
self.remove(key, now);
|
||||
}
|
||||
Ok(keys.len())
|
||||
Ok(keys.iter().map(|k| self.remove(k).unwrap()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
crate::{
|
||||
contact_info::ContactInfo,
|
||||
crds_value::{new_rand_timestamp, NodeInstance},
|
||||
},
|
||||
rand::{thread_rng, Rng, SeedableRng},
|
||||
rand_chacha::ChaChaRng,
|
||||
rayon::ThreadPoolBuilder,
|
||||
solana_sdk::signature::{Keypair, Signer},
|
||||
std::{collections::HashSet, iter::repeat_with},
|
||||
use super::*;
|
||||
use crate::{
|
||||
contact_info::ContactInfo,
|
||||
crds_value::{new_rand_timestamp, NodeInstance},
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::{collections::HashSet, iter::repeat_with};
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let mut crds = Crds::default();
|
||||
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
||||
assert_eq!(crds.insert(val.clone(), 0), Ok(()));
|
||||
assert_eq!(crds.insert(val.clone(), 0).ok(), Some(None));
|
||||
assert_eq!(crds.table.len(), 1);
|
||||
assert!(crds.table.contains_key(&val.label()));
|
||||
assert_eq!(crds.table[&val.label()].local_timestamp, 0);
|
||||
@@ -571,9 +559,12 @@ mod test {
|
||||
fn test_update_old() {
|
||||
let mut crds = Crds::default();
|
||||
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
||||
assert_eq!(crds.insert(val.clone(), 0), Ok(()));
|
||||
assert_eq!(crds.insert(val.clone(), 1), Err(CrdsError::InsertFailed));
|
||||
assert!(crds.purged.is_empty());
|
||||
let value_hash = hash(&serialize(&val).unwrap());
|
||||
assert_eq!(crds.insert(val.clone(), 0), Ok(None));
|
||||
assert_eq!(
|
||||
crds.insert(val.clone(), 1),
|
||||
Err(CrdsError::InsertFailed(value_hash))
|
||||
);
|
||||
assert_eq!(crds.table[&val.label()].local_timestamp, 0);
|
||||
}
|
||||
#[test]
|
||||
@@ -583,14 +574,15 @@ mod test {
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
)));
|
||||
let value_hash = hash(&serialize(&original).unwrap());
|
||||
assert_matches!(crds.insert(original, 0), Ok(()));
|
||||
assert_matches!(crds.insert(original.clone(), 0), Ok(_));
|
||||
let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&Pubkey::default(),
|
||||
1,
|
||||
)));
|
||||
assert_eq!(crds.insert(val.clone(), 1), Ok(()));
|
||||
assert_eq!(*crds.purged.back().unwrap(), (value_hash, 1));
|
||||
assert_eq!(
|
||||
crds.insert(val.clone(), 1).unwrap().unwrap().value,
|
||||
original
|
||||
);
|
||||
assert_eq!(crds.table[&val.label()].local_timestamp, 1);
|
||||
}
|
||||
#[test]
|
||||
@@ -600,13 +592,12 @@ mod test {
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(val.clone(), 0), Ok(()));
|
||||
assert_eq!(crds.insert(val.clone(), 0), Ok(None));
|
||||
assert_eq!(crds.table[&val.label()].ordinal, 0);
|
||||
|
||||
let val2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
||||
let value_hash = hash(&serialize(&val2).unwrap());
|
||||
assert_eq!(val2.label().pubkey(), val.label().pubkey());
|
||||
assert_eq!(crds.insert(val2.clone(), 0), Ok(()));
|
||||
assert_matches!(crds.insert(val2.clone(), 0), Ok(Some(_)));
|
||||
|
||||
crds.update_record_timestamp(&val.label().pubkey(), 2);
|
||||
assert_eq!(crds.table[&val.label()].local_timestamp, 2);
|
||||
@@ -621,73 +612,16 @@ mod test {
|
||||
let mut ci = ContactInfo::default();
|
||||
ci.wallclock += 1;
|
||||
let val3 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci));
|
||||
assert_eq!(crds.insert(val3, 3), Ok(()));
|
||||
assert_eq!(*crds.purged.back().unwrap(), (value_hash, 3));
|
||||
assert_matches!(crds.insert(val3, 3), Ok(Some(_)));
|
||||
assert_eq!(crds.table[&val2.label()].local_timestamp, 3);
|
||||
assert_eq!(crds.table[&val2.label()].ordinal, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upsert_node_instance() {
|
||||
const SEED: [u8; 32] = [0x42; 32];
|
||||
let mut rng = ChaChaRng::from_seed(SEED);
|
||||
fn make_crds_value(node: NodeInstance) -> CrdsValue {
|
||||
CrdsValue::new_unsigned(CrdsData::NodeInstance(node))
|
||||
}
|
||||
let now = 1_620_838_767_000;
|
||||
let mut crds = Crds::default();
|
||||
let pubkey = Pubkey::new_unique();
|
||||
let node = NodeInstance::new(&mut rng, pubkey, now);
|
||||
let node = make_crds_value(node);
|
||||
assert_eq!(crds.insert(node, now), Ok(()));
|
||||
// A node-instance with a different key should insert fine even with
|
||||
// older timestamps.
|
||||
let other = NodeInstance::new(&mut rng, Pubkey::new_unique(), now - 1);
|
||||
let other = make_crds_value(other);
|
||||
assert_eq!(crds.insert(other, now), Ok(()));
|
||||
// A node-instance with older timestamp should fail to insert, even if
|
||||
// the wallclock is more recent.
|
||||
let other = NodeInstance::new(&mut rng, pubkey, now - 1);
|
||||
let other = other.with_wallclock(now + 1);
|
||||
let other = make_crds_value(other);
|
||||
let value_hash = hash(&serialize(&other).unwrap());
|
||||
assert_eq!(crds.insert(other, now), Err(CrdsError::InsertFailed));
|
||||
assert_eq!(*crds.purged.back().unwrap(), (value_hash, now));
|
||||
// A node instance with the same timestamp should insert only if the
|
||||
// random token is larger.
|
||||
let mut num_overrides = 0;
|
||||
for _ in 0..100 {
|
||||
let other = NodeInstance::new(&mut rng, pubkey, now);
|
||||
let other = make_crds_value(other);
|
||||
let value_hash = hash(&serialize(&other).unwrap());
|
||||
match crds.insert(other, now) {
|
||||
Ok(()) => num_overrides += 1,
|
||||
Err(CrdsError::InsertFailed) => {
|
||||
assert_eq!(*crds.purged.back().unwrap(), (value_hash, now))
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
assert_eq!(num_overrides, 5);
|
||||
// A node instance with larger timestamp should insert regardless of
|
||||
// its token value.
|
||||
for k in 1..10 {
|
||||
let other = NodeInstance::new(&mut rng, pubkey, now + k);
|
||||
let other = other.with_wallclock(now - 1);
|
||||
let other = make_crds_value(other);
|
||||
match crds.insert(other, now) {
|
||||
Ok(()) => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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(()));
|
||||
assert_eq!(crds.insert(val.clone(), 1), Ok(None));
|
||||
let mut set = HashMap::new();
|
||||
set.insert(Pubkey::default(), 0);
|
||||
assert!(crds.find_old_labels(&thread_pool, 0, &set).is_empty());
|
||||
@@ -710,7 +644,7 @@ mod test {
|
||||
let mut timeouts = HashMap::new();
|
||||
let val = CrdsValue::new_rand(&mut rng, None);
|
||||
timeouts.insert(Pubkey::default(), 3);
|
||||
assert_eq!(crds.insert(val.clone(), 0), Ok(()));
|
||||
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!(
|
||||
@@ -728,6 +662,40 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_old_records_unlimited() {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let mut rng = thread_rng();
|
||||
let now = 1_610_034_423_000;
|
||||
let pubkey = Pubkey::new_unique();
|
||||
let mut crds = Crds::default();
|
||||
let mut timeouts = HashMap::new();
|
||||
timeouts.insert(Pubkey::default(), 1);
|
||||
timeouts.insert(pubkey, 180);
|
||||
for _ in 0..1024 {
|
||||
let wallclock = now - rng.gen_range(0, 240);
|
||||
let val = NodeInstance::new(&mut rng, pubkey, wallclock);
|
||||
let val = CrdsData::NodeInstance(val);
|
||||
let val = CrdsValue::new_unsigned(val);
|
||||
assert_eq!(crds.insert(val, now), Ok(None));
|
||||
}
|
||||
let now = now + 1;
|
||||
let labels = crds.find_old_labels(&thread_pool, now, &timeouts);
|
||||
assert_eq!(crds.table.len() - labels.len(), MAX_CRDS_VALUES_PER_PUBKEY);
|
||||
let max_wallclock = labels
|
||||
.iter()
|
||||
.map(|label| crds.lookup(label).unwrap().wallclock())
|
||||
.max()
|
||||
.unwrap();
|
||||
assert!(max_wallclock > now - 180);
|
||||
let labels: HashSet<_> = labels.into_iter().collect();
|
||||
for (label, value) in crds.table.iter() {
|
||||
if !labels.contains(label) {
|
||||
assert!(max_wallclock <= value.value.wallclock());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_default() {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
@@ -740,7 +708,7 @@ mod test {
|
||||
crds.find_old_labels(&thread_pool, 2, &set),
|
||||
vec![val.label()]
|
||||
);
|
||||
crds.remove(&val.label(), /*now=*/ 0);
|
||||
crds.remove(&val.label());
|
||||
assert!(crds.find_old_labels(&thread_pool, 2, &set).is_empty());
|
||||
}
|
||||
#[test]
|
||||
@@ -748,7 +716,7 @@ mod test {
|
||||
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(()));
|
||||
assert_eq!(crds.insert(val.clone(), 1), Ok(None));
|
||||
let mut set = HashMap::new();
|
||||
//now < timestamp
|
||||
set.insert(Pubkey::default(), 0);
|
||||
@@ -786,19 +754,27 @@ mod test {
|
||||
let keypairs: Vec<_> = std::iter::repeat_with(Keypair::new).take(256).collect();
|
||||
let mut rng = thread_rng();
|
||||
let mut num_inserts = 0;
|
||||
let mut num_overrides = 0;
|
||||
for _ in 0..4096 {
|
||||
let keypair = &keypairs[rng.gen_range(0, keypairs.len())];
|
||||
let value = CrdsValue::new_rand(&mut rng, Some(keypair));
|
||||
let local_timestamp = new_rand_timestamp(&mut rng);
|
||||
if let Ok(()) = crds.insert(value, local_timestamp) {
|
||||
num_inserts += 1;
|
||||
check_crds_shards(&crds);
|
||||
match crds.insert(value, local_timestamp) {
|
||||
Ok(None) => {
|
||||
num_inserts += 1;
|
||||
check_crds_shards(&crds);
|
||||
}
|
||||
Ok(Some(_)) => {
|
||||
num_inserts += 1;
|
||||
num_overrides += 1;
|
||||
check_crds_shards(&crds);
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
assert_eq!(num_inserts, crds.cursor.0 as usize);
|
||||
assert!(num_inserts > 700);
|
||||
assert!(crds.num_purged() > 500);
|
||||
assert_eq!(crds.num_purged() + crds.table.len(), 4096);
|
||||
assert!(num_overrides > 500);
|
||||
assert!(crds.table.len() > 200);
|
||||
assert!(num_inserts > crds.table.len());
|
||||
check_crds_shards(&crds);
|
||||
@@ -806,7 +782,7 @@ mod test {
|
||||
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, /*now=*/ 0);
|
||||
crds.remove(&key);
|
||||
check_crds_shards(&crds);
|
||||
}
|
||||
}
|
||||
@@ -940,12 +916,20 @@ mod test {
|
||||
let keypairs: Vec<_> = repeat_with(Keypair::new).take(128).collect();
|
||||
let mut crds = Crds::default();
|
||||
let mut num_inserts = 0;
|
||||
let mut num_overrides = 0;
|
||||
for k in 0..4096 {
|
||||
let keypair = &keypairs[rng.gen_range(0, keypairs.len())];
|
||||
let value = CrdsValue::new_rand(&mut rng, Some(keypair));
|
||||
let local_timestamp = new_rand_timestamp(&mut rng);
|
||||
if let Ok(()) = crds.insert(value, local_timestamp) {
|
||||
num_inserts += 1;
|
||||
match crds.insert(value, local_timestamp) {
|
||||
Ok(None) => {
|
||||
num_inserts += 1;
|
||||
}
|
||||
Ok(Some(_)) => {
|
||||
num_inserts += 1;
|
||||
num_overrides += 1;
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
if k % 16 == 0 {
|
||||
check_crds_value_indices(&mut rng, &crds);
|
||||
@@ -953,9 +937,8 @@ mod test {
|
||||
}
|
||||
assert_eq!(num_inserts, crds.cursor.0 as usize);
|
||||
assert!(num_inserts > 700);
|
||||
assert!(crds.num_purged() > 500);
|
||||
assert!(num_overrides > 500);
|
||||
assert!(crds.table.len() > 200);
|
||||
assert_eq!(crds.num_purged() + crds.table.len(), 4096);
|
||||
assert!(num_inserts > crds.table.len());
|
||||
let (num_nodes, num_votes, num_epoch_slots) = check_crds_value_indices(&mut rng, &crds);
|
||||
assert!(num_nodes * 3 < crds.table.len());
|
||||
@@ -970,7 +953,7 @@ mod test {
|
||||
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, /*now=*/ 0);
|
||||
crds.remove(&key);
|
||||
if crds.table.len() % 16 == 0 {
|
||||
check_crds_value_indices(&mut rng, &crds);
|
||||
}
|
||||
@@ -1009,7 +992,7 @@ mod test {
|
||||
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, /*now=*/ 0);
|
||||
crds.remove(&key);
|
||||
if crds.table.len() % 64 == 0 {
|
||||
check_crds_records(&crds);
|
||||
}
|
||||
@@ -1018,7 +1001,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::needless_collect)]
|
||||
fn test_drop() {
|
||||
fn num_unique_pubkeys<'a, I>(values: I) -> usize
|
||||
where
|
||||
@@ -1047,15 +1029,7 @@ mod test {
|
||||
let num_pubkeys = num_unique_pubkeys(crds.table.values());
|
||||
assert!(!crds.should_trim(num_pubkeys));
|
||||
assert!(crds.should_trim(num_pubkeys * 5 / 6));
|
||||
let values: Vec<_> = crds.table.values().cloned().collect();
|
||||
crds.drop(16, &[], &stakes, /*now=*/ 0).unwrap();
|
||||
let purged: Vec<_> = {
|
||||
let purged: HashSet<_> = crds.purged.iter().map(|(hash, _)| hash).copied().collect();
|
||||
values
|
||||
.into_iter()
|
||||
.filter(|v| purged.contains(&v.value_hash))
|
||||
.collect()
|
||||
};
|
||||
let purged = crds.drop(16, &[], &stakes).unwrap();
|
||||
assert_eq!(purged.len() + crds.table.len(), num_values);
|
||||
assert_eq!(num_unique_pubkeys(&purged), 16);
|
||||
assert_eq!(num_unique_pubkeys(crds.table.values()), num_pubkeys - 16);
|
||||
@@ -1092,7 +1066,7 @@ mod test {
|
||||
crds.find_old_labels(&thread_pool, 2, &set),
|
||||
vec![val.label()]
|
||||
);
|
||||
crds.remove(&val.label(), /*now=*/ 0);
|
||||
crds.remove(&val.label());
|
||||
assert!(crds.find_old_labels(&thread_pool, 2, &set).is_empty());
|
||||
}
|
||||
|
@@ -3,34 +3,33 @@
|
||||
//! designed to run with a simulator or over a UDP network connection with messages up to a
|
||||
//! packet::PACKET_DATA_SIZE size.
|
||||
|
||||
use {
|
||||
crate::{
|
||||
cluster_info::Ping,
|
||||
contact_info::ContactInfo,
|
||||
crds::Crds,
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_gossip_pull::{CrdsFilter, CrdsGossipPull, ProcessPullStats},
|
||||
crds_gossip_push::{CrdsGossipPush, CRDS_GOSSIP_NUM_ACTIVE},
|
||||
crds_value::{CrdsData, CrdsValue},
|
||||
duplicate_shred::{self, DuplicateShredIndex, LeaderScheduleFn, MAX_DUPLICATE_SHREDS},
|
||||
ping_pong::PingCache,
|
||||
},
|
||||
itertools::Itertools,
|
||||
rayon::ThreadPool,
|
||||
solana_ledger::shred::Shred,
|
||||
solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
},
|
||||
std::{
|
||||
collections::{HashMap, HashSet},
|
||||
net::SocketAddr,
|
||||
sync::Mutex,
|
||||
time::Duration,
|
||||
},
|
||||
use crate::{
|
||||
cluster_info::Ping,
|
||||
contact_info::ContactInfo,
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_gossip_pull::{CrdsFilter, CrdsGossipPull, ProcessPullStats},
|
||||
crds_gossip_push::{CrdsGossipPush, CRDS_GOSSIP_NUM_ACTIVE},
|
||||
crds_value::{CrdsData, CrdsValue, CrdsValueLabel},
|
||||
duplicate_shred::{self, DuplicateShredIndex, LeaderScheduleFn, MAX_DUPLICATE_SHREDS},
|
||||
ping_pong::PingCache,
|
||||
};
|
||||
use rayon::ThreadPool;
|
||||
use solana_ledger::shred::Shred;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
net::SocketAddr,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
///The min size for bloom filters
|
||||
pub const CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS: usize = 500;
|
||||
|
||||
pub struct CrdsGossip {
|
||||
pub crds: Crds,
|
||||
@@ -61,44 +60,41 @@ impl CrdsGossip {
|
||||
}
|
||||
|
||||
/// process a push message to the network
|
||||
/// Returns origins' pubkeys of upserted values.
|
||||
pub fn process_push_message(
|
||||
&mut self,
|
||||
from: &Pubkey,
|
||||
values: Vec<CrdsValue>,
|
||||
now: u64,
|
||||
) -> Vec<Pubkey> {
|
||||
) -> Vec<VersionedCrdsValue> {
|
||||
values
|
||||
.into_iter()
|
||||
.flat_map(|val| {
|
||||
let origin = val.pubkey();
|
||||
self.push
|
||||
.filter_map(|val| {
|
||||
let old = self
|
||||
.push
|
||||
.process_push_message(&mut self.crds, from, val, now)
|
||||
.ok()?;
|
||||
Some(origin)
|
||||
self.pull.record_old_hash(old.as_ref()?.value_hash, now);
|
||||
old
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// remove redundant paths in the network
|
||||
pub fn prune_received_cache<I>(
|
||||
pub fn prune_received_cache(
|
||||
&mut self,
|
||||
origins: I, // Unique pubkeys of crds values' owners.
|
||||
labels: Vec<CrdsValueLabel>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
) -> HashMap</*gossip peer:*/ Pubkey, /*origins:*/ Vec<Pubkey>>
|
||||
where
|
||||
I: IntoIterator<Item = Pubkey>,
|
||||
{
|
||||
let self_pubkey = self.id;
|
||||
origins
|
||||
.into_iter()
|
||||
.flat_map(|origin| {
|
||||
self.push
|
||||
.prune_received_cache(&self_pubkey, &origin, stakes)
|
||||
.into_iter()
|
||||
.zip(std::iter::repeat(origin))
|
||||
})
|
||||
.into_group_map()
|
||||
) -> HashMap<Pubkey, HashSet<Pubkey>> {
|
||||
let id = &self.id;
|
||||
let push = &mut self.push;
|
||||
let mut prune_map: HashMap<Pubkey, HashSet<_>> = HashMap::new();
|
||||
for origin in labels.iter().map(|k| k.pubkey()) {
|
||||
let peers = push.prune_received_cache(id, &origin, stakes);
|
||||
for from in peers {
|
||||
prune_map.entry(from).or_default().insert(origin);
|
||||
}
|
||||
}
|
||||
prune_map
|
||||
}
|
||||
|
||||
pub fn new_push_messages(
|
||||
@@ -300,12 +296,16 @@ impl CrdsGossip {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn make_timeouts_test(&self) -> HashMap<Pubkey, u64> {
|
||||
self.make_timeouts(&HashMap::new(), self.pull.crds_timeout)
|
||||
}
|
||||
|
||||
pub fn make_timeouts(
|
||||
&self,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
epoch_duration: Duration,
|
||||
epoch_ms: u64,
|
||||
) -> HashMap<Pubkey, u64> {
|
||||
self.pull.make_timeouts(self.id, stakes, epoch_duration)
|
||||
self.pull.make_timeouts(&self.id, stakes, epoch_ms)
|
||||
}
|
||||
|
||||
pub fn purge(
|
||||
@@ -321,14 +321,17 @@ impl CrdsGossip {
|
||||
}
|
||||
if now > self.pull.crds_timeout {
|
||||
//sanity check
|
||||
let min = self.pull.crds_timeout;
|
||||
assert_eq!(timeouts[&self.id], std::u64::MAX);
|
||||
assert!(timeouts.contains_key(&Pubkey::default()));
|
||||
assert_eq!(timeouts[&Pubkey::default()], min);
|
||||
rv = self
|
||||
.pull
|
||||
.purge_active(thread_pool, &mut self.crds, now, &timeouts);
|
||||
}
|
||||
self.crds
|
||||
.trim_purged(now.saturating_sub(5 * self.pull.crds_timeout));
|
||||
if now > 5 * self.pull.crds_timeout {
|
||||
let min = now - 5 * self.pull.crds_timeout;
|
||||
self.pull.purge_purged(min);
|
||||
}
|
||||
self.pull.purge_failed_inserts(now);
|
||||
rv
|
||||
}
|
||||
@@ -363,11 +366,11 @@ pub fn get_weight(max_weight: f32, time_since_last_selected: u32, stake: f32) ->
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
crate::{contact_info::ContactInfo, crds_value::CrdsData},
|
||||
solana_sdk::{hash::hash, timing::timestamp},
|
||||
};
|
||||
use super::*;
|
||||
use crate::contact_info::ContactInfo;
|
||||
use crate::crds_value::CrdsData;
|
||||
use solana_sdk::hash::hash;
|
||||
use solana_sdk::timing::timestamp;
|
||||
|
||||
#[test]
|
||||
fn test_prune_errors() {
|
@@ -9,37 +9,33 @@
|
||||
//! with random hash functions. So each subsequent request will have a different distribution
|
||||
//! of false positives.
|
||||
|
||||
use {
|
||||
crate::{
|
||||
cluster_info::{Ping, CRDS_UNIQUE_PUBKEY_CAPACITY},
|
||||
contact_info::ContactInfo,
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_gossip::{get_stake, get_weight},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_value::CrdsValue,
|
||||
ping_pong::PingCache,
|
||||
},
|
||||
itertools::Itertools,
|
||||
lru::LruCache,
|
||||
rand::{
|
||||
distributions::{Distribution, WeightedIndex},
|
||||
Rng,
|
||||
},
|
||||
rayon::{prelude::*, ThreadPool},
|
||||
solana_runtime::bloom::{AtomicBloom, Bloom},
|
||||
solana_sdk::{
|
||||
hash::{hash, Hash},
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
},
|
||||
std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
convert::TryInto,
|
||||
iter::repeat_with,
|
||||
net::SocketAddr,
|
||||
sync::Mutex,
|
||||
time::{Duration, Instant},
|
||||
},
|
||||
use crate::{
|
||||
cluster_info::{Ping, CRDS_UNIQUE_PUBKEY_CAPACITY},
|
||||
contact_info::ContactInfo,
|
||||
crds::{Crds, CrdsError},
|
||||
crds_gossip::{get_stake, get_weight, CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_value::CrdsValue,
|
||||
ping_pong::PingCache,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use lru::LruCache;
|
||||
use rand::distributions::{Distribution, WeightedIndex};
|
||||
use rand::Rng;
|
||||
use rayon::{prelude::*, ThreadPool};
|
||||
use solana_runtime::bloom::{AtomicBloom, Bloom};
|
||||
use solana_sdk::{
|
||||
hash::{hash, Hash},
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
convert::TryInto,
|
||||
net::SocketAddr,
|
||||
sync::Mutex,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
pub const CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS: u64 = 15000;
|
||||
@@ -145,10 +141,11 @@ impl CrdsFilterSet {
|
||||
let max_bits = (max_bytes * 8) as f64;
|
||||
let max_items = CrdsFilter::max_items(max_bits, FALSE_RATE, KEYS);
|
||||
let mask_bits = CrdsFilter::mask_bits(num_items as f64, max_items as f64);
|
||||
let filters =
|
||||
repeat_with(|| Bloom::random(max_items as usize, FALSE_RATE, max_bits as usize).into())
|
||||
.take(1 << mask_bits)
|
||||
.collect();
|
||||
let filters = std::iter::repeat_with(|| {
|
||||
Bloom::random(max_items as usize, FALSE_RATE, max_bits as usize).into()
|
||||
})
|
||||
.take(1 << mask_bits)
|
||||
.collect();
|
||||
Self { filters, mask_bits }
|
||||
}
|
||||
|
||||
@@ -186,6 +183,8 @@ pub struct ProcessPullStats {
|
||||
pub struct CrdsGossipPull {
|
||||
/// timestamp of last request
|
||||
pub(crate) pull_request_time: LruCache<Pubkey, u64>,
|
||||
/// hash and insert time
|
||||
pub purged_values: VecDeque<(Hash, u64)>,
|
||||
// Hash value and record time (ms) of the pull responses which failed to be
|
||||
// inserted in crds table; Preserved to stop the sender to send back the
|
||||
// same outdated payload again by adding them to the filter for the next
|
||||
@@ -199,6 +198,7 @@ pub struct CrdsGossipPull {
|
||||
impl Default for CrdsGossipPull {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
purged_values: VecDeque::new(),
|
||||
pull_request_time: LruCache::new(CRDS_UNIQUE_PUBKEY_CAPACITY),
|
||||
failed_inserts: VecDeque::new(),
|
||||
crds_timeout: CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
|
||||
@@ -242,7 +242,7 @@ impl CrdsGossipPull {
|
||||
let num_samples = peers.len() * 2;
|
||||
let index = WeightedIndex::new(weights).unwrap();
|
||||
let sample_peer = move || peers[index.sample(&mut rng)];
|
||||
repeat_with(sample_peer).take(num_samples)
|
||||
std::iter::repeat_with(sample_peer).take(num_samples)
|
||||
};
|
||||
let peer = {
|
||||
let mut rng = rand::thread_rng();
|
||||
@@ -322,14 +322,21 @@ impl CrdsGossipPull {
|
||||
self.pull_request_time.put(from, now);
|
||||
}
|
||||
|
||||
/// Store an old hash in the purged values set
|
||||
pub fn record_old_hash(&mut self, hash: Hash, timestamp: u64) {
|
||||
self.purged_values.push_back((hash, timestamp))
|
||||
}
|
||||
|
||||
/// process a pull request
|
||||
pub fn process_pull_requests<I>(&mut self, crds: &mut Crds, callers: I, now: u64)
|
||||
where
|
||||
I: IntoIterator<Item = CrdsValue>,
|
||||
{
|
||||
for caller in callers {
|
||||
let key = caller.pubkey();
|
||||
let _ = crds.insert(caller, now);
|
||||
let key = caller.label().pubkey();
|
||||
if let Ok(Some(val)) = crds.insert(caller, now) {
|
||||
self.purged_values.push_back((val.value_hash, now));
|
||||
}
|
||||
crds.update_record_timestamp(&key, now);
|
||||
}
|
||||
}
|
||||
@@ -361,38 +368,37 @@ impl CrdsGossipPull {
|
||||
) -> (Vec<CrdsValue>, Vec<CrdsValue>, Vec<Hash>) {
|
||||
let mut active_values = vec![];
|
||||
let mut expired_values = vec![];
|
||||
let mut failed_inserts = vec![];
|
||||
let mut maybe_push = |response, values: &mut Vec<CrdsValue>| {
|
||||
if crds.upserts(&response) {
|
||||
values.push(response);
|
||||
} else {
|
||||
let response = bincode::serialize(&response).unwrap();
|
||||
failed_inserts.push(hash(&response));
|
||||
}
|
||||
};
|
||||
let default_timeout = timeouts
|
||||
.get(&Pubkey::default())
|
||||
.copied()
|
||||
.unwrap_or(self.msg_timeout);
|
||||
let upsert = |response: CrdsValue| {
|
||||
for response in responses {
|
||||
let owner = response.label().pubkey();
|
||||
// Check if the crds value is older than the msg_timeout
|
||||
let timeout = timeouts.get(&owner).copied().unwrap_or(default_timeout);
|
||||
// Before discarding this value, check if a ContactInfo for the
|
||||
// owner exists in the table. If it doesn't, that implies that this
|
||||
// value can be discarded
|
||||
if !crds.upserts(&response) {
|
||||
Some(response)
|
||||
} else if now <= response.wallclock().saturating_add(timeout) {
|
||||
active_values.push(response);
|
||||
None
|
||||
if now <= response.wallclock().saturating_add(timeout) {
|
||||
maybe_push(response, &mut active_values);
|
||||
} else if crds.get_contact_info(owner).is_some() {
|
||||
// Silently insert this old value without bumping record
|
||||
// timestamps
|
||||
expired_values.push(response);
|
||||
None
|
||||
maybe_push(response, &mut expired_values);
|
||||
} else {
|
||||
stats.timeout_count += 1;
|
||||
stats.failed_timeout += 1;
|
||||
Some(response)
|
||||
}
|
||||
};
|
||||
let failed_inserts = responses
|
||||
.into_iter()
|
||||
.filter_map(upsert)
|
||||
.map(|resp| hash(&bincode::serialize(&resp).unwrap()))
|
||||
.collect();
|
||||
}
|
||||
(active_values, expired_values, failed_inserts)
|
||||
}
|
||||
|
||||
@@ -403,20 +409,32 @@ impl CrdsGossipPull {
|
||||
from: &Pubkey,
|
||||
responses: Vec<CrdsValue>,
|
||||
responses_expired_timeout: Vec<CrdsValue>,
|
||||
failed_inserts: Vec<Hash>,
|
||||
mut failed_inserts: Vec<Hash>,
|
||||
now: u64,
|
||||
stats: &mut ProcessPullStats,
|
||||
) {
|
||||
let mut owners = HashSet::new();
|
||||
for response in responses_expired_timeout {
|
||||
let _ = crds.insert(response, now);
|
||||
match crds.insert(response, now) {
|
||||
Ok(None) => (),
|
||||
Ok(Some(old)) => self.purged_values.push_back((old.value_hash, now)),
|
||||
Err(CrdsError::InsertFailed(value_hash)) => failed_inserts.push(value_hash),
|
||||
Err(CrdsError::UnknownStakes) => (),
|
||||
}
|
||||
}
|
||||
for response in responses {
|
||||
let owner = response.pubkey();
|
||||
if let Ok(()) = crds.insert(response, now) {
|
||||
stats.success += 1;
|
||||
self.num_pulls += 1;
|
||||
owners.insert(owner);
|
||||
match crds.insert(response, now) {
|
||||
Err(CrdsError::InsertFailed(value_hash)) => failed_inserts.push(value_hash),
|
||||
Err(CrdsError::UnknownStakes) => (),
|
||||
Ok(old) => {
|
||||
stats.success += 1;
|
||||
self.num_pulls += 1;
|
||||
owners.insert(owner);
|
||||
if let Some(val) = old {
|
||||
self.purged_values.push_back((val.value_hash, now))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
owners.insert(*from);
|
||||
@@ -450,18 +468,21 @@ impl CrdsGossipPull {
|
||||
bloom_size: usize,
|
||||
) -> Vec<CrdsFilter> {
|
||||
const PAR_MIN_LENGTH: usize = 512;
|
||||
#[cfg(debug_assertions)]
|
||||
const MIN_NUM_BLOOM_ITEMS: usize = 512;
|
||||
#[cfg(not(debug_assertions))]
|
||||
const MIN_NUM_BLOOM_ITEMS: usize = 65_536;
|
||||
let num_items = crds.len() + crds.num_purged() + self.failed_inserts.len();
|
||||
let num_items = MIN_NUM_BLOOM_ITEMS.max(num_items);
|
||||
let filters = CrdsFilterSet::new(num_items, bloom_size);
|
||||
let num = cmp::max(
|
||||
CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS,
|
||||
crds.len() + self.purged_values.len() + self.failed_inserts.len(),
|
||||
);
|
||||
let filters = CrdsFilterSet::new(num, bloom_size);
|
||||
thread_pool.install(|| {
|
||||
crds.par_values()
|
||||
.with_min_len(PAR_MIN_LENGTH)
|
||||
.map(|v| v.value_hash)
|
||||
.chain(crds.purged().with_min_len(PAR_MIN_LENGTH))
|
||||
.chain(
|
||||
self.purged_values
|
||||
.par_iter()
|
||||
.with_min_len(PAR_MIN_LENGTH)
|
||||
.map(|(v, _)| *v),
|
||||
)
|
||||
.chain(
|
||||
self.failed_inserts
|
||||
.par_iter()
|
||||
@@ -484,8 +505,8 @@ impl CrdsGossipPull {
|
||||
let msg_timeout = CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
|
||||
let jitter = rand::thread_rng().gen_range(0, msg_timeout / 4);
|
||||
//skip filters from callers that are too old
|
||||
let caller_wallclock_window =
|
||||
now.saturating_sub(msg_timeout)..now.saturating_add(msg_timeout);
|
||||
let future = now.saturating_add(msg_timeout);
|
||||
let past = now.saturating_sub(msg_timeout);
|
||||
let mut dropped_requests = 0;
|
||||
let mut total_skipped = 0;
|
||||
let ret: Vec<_> = filters
|
||||
@@ -495,28 +516,25 @@ impl CrdsGossipPull {
|
||||
return None;
|
||||
}
|
||||
let caller_wallclock = caller.wallclock();
|
||||
if !caller_wallclock_window.contains(&caller_wallclock) {
|
||||
if caller_wallclock >= future || caller_wallclock < past {
|
||||
dropped_requests += 1;
|
||||
return Some(vec![]);
|
||||
}
|
||||
let caller_pubkey = caller.pubkey();
|
||||
let caller_wallclock = caller_wallclock.checked_add(jitter).unwrap_or(0);
|
||||
let pred = |entry: &&VersionedCrdsValue| {
|
||||
debug_assert!(filter.test_mask(&entry.value_hash));
|
||||
// Skip values that are too new.
|
||||
if entry.value.wallclock() > caller_wallclock {
|
||||
total_skipped += 1;
|
||||
false
|
||||
} else {
|
||||
!filter.filter_contains(&entry.value_hash)
|
||||
&& (entry.value.pubkey() != caller_pubkey
|
||||
|| entry.value.should_force_push(&caller_pubkey))
|
||||
}
|
||||
};
|
||||
let out: Vec<_> = crds
|
||||
.filter_bitmask(filter.mask, filter.mask_bits)
|
||||
.filter(pred)
|
||||
.map(|entry| entry.value.clone())
|
||||
.filter_map(|item| {
|
||||
debug_assert!(filter.test_mask(&item.value_hash));
|
||||
//skip values that are too new
|
||||
if item.value.wallclock() > caller_wallclock {
|
||||
total_skipped += 1;
|
||||
None
|
||||
} else if filter.filter_contains(&item.value_hash) {
|
||||
None
|
||||
} else {
|
||||
Some(item.value.clone())
|
||||
}
|
||||
})
|
||||
.take(output_size_limit)
|
||||
.collect();
|
||||
output_size_limit -= out.len();
|
||||
@@ -531,31 +549,30 @@ impl CrdsGossipPull {
|
||||
inc_new_counter_info!("gossip_filter_crds_values-dropped_values", total_skipped);
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) fn make_timeouts(
|
||||
pub fn make_timeouts_def(
|
||||
&self,
|
||||
self_pubkey: Pubkey,
|
||||
self_id: &Pubkey,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
epoch_duration: Duration,
|
||||
epoch_ms: u64,
|
||||
min_ts: u64,
|
||||
) -> HashMap<Pubkey, u64> {
|
||||
let extended_timeout = self.crds_timeout.max(epoch_duration.as_millis() as u64);
|
||||
let default_timeout = if stakes.values().all(|stake| *stake == 0) {
|
||||
extended_timeout
|
||||
} else {
|
||||
self.crds_timeout
|
||||
};
|
||||
stakes
|
||||
.iter()
|
||||
.filter(|(_, stake)| **stake > 0)
|
||||
.map(|(pubkey, _)| (*pubkey, extended_timeout))
|
||||
.chain(vec![
|
||||
(Pubkey::default(), default_timeout),
|
||||
(self_pubkey, u64::MAX),
|
||||
])
|
||||
.collect()
|
||||
let mut timeouts: HashMap<Pubkey, u64> = stakes.keys().map(|s| (*s, epoch_ms)).collect();
|
||||
timeouts.insert(*self_id, std::u64::MAX);
|
||||
timeouts.insert(Pubkey::default(), min_ts);
|
||||
timeouts
|
||||
}
|
||||
|
||||
pub fn make_timeouts(
|
||||
&self,
|
||||
self_id: &Pubkey,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
epoch_ms: u64,
|
||||
) -> HashMap<Pubkey, u64> {
|
||||
self.make_timeouts_def(self_id, stakes, epoch_ms, self.crds_timeout)
|
||||
}
|
||||
|
||||
/// Purge values from the crds that are older then `active_timeout`
|
||||
/// The value_hash of an active item is put into self.purged_values queue
|
||||
pub fn purge_active(
|
||||
&mut self,
|
||||
thread_pool: &ThreadPool,
|
||||
@@ -563,11 +580,25 @@ impl CrdsGossipPull {
|
||||
now: u64,
|
||||
timeouts: &HashMap<Pubkey, u64>,
|
||||
) -> usize {
|
||||
let labels = crds.find_old_labels(thread_pool, now, timeouts);
|
||||
for label in &labels {
|
||||
crds.remove(label, now);
|
||||
}
|
||||
labels.len()
|
||||
let num_purged_values = self.purged_values.len();
|
||||
self.purged_values.extend(
|
||||
crds.find_old_labels(thread_pool, now, timeouts)
|
||||
.into_iter()
|
||||
.filter_map(|label| {
|
||||
let val = crds.remove(&label)?;
|
||||
Some((val.value_hash, now))
|
||||
}),
|
||||
);
|
||||
self.purged_values.len() - num_purged_values
|
||||
}
|
||||
/// Purge values from the `self.purged_values` queue that are older then purge_timeout
|
||||
pub fn purge_purged(&mut self, min_ts: u64) {
|
||||
let cnt = self
|
||||
.purged_values
|
||||
.iter()
|
||||
.take_while(|v| v.1 < min_ts)
|
||||
.count();
|
||||
self.purged_values.drain(..cnt);
|
||||
}
|
||||
|
||||
/// For legacy tests
|
||||
@@ -607,35 +638,28 @@ impl CrdsGossipPull {
|
||||
}
|
||||
Self {
|
||||
pull_request_time,
|
||||
purged_values: self.purged_values.clone(),
|
||||
failed_inserts: self.failed_inserts.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use {
|
||||
super::*,
|
||||
crate::{
|
||||
cluster_info::MAX_BLOOM_SIZE,
|
||||
contact_info::ContactInfo,
|
||||
crds_value::{CrdsData, Vote},
|
||||
},
|
||||
itertools::Itertools,
|
||||
rand::{seq::SliceRandom, thread_rng},
|
||||
rayon::ThreadPoolBuilder,
|
||||
solana_perf::test_tx::test_tx,
|
||||
solana_sdk::{
|
||||
hash::{hash, HASH_BYTES},
|
||||
packet::PACKET_DATA_SIZE,
|
||||
timing::timestamp,
|
||||
},
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::cluster_info::MAX_BLOOM_SIZE;
|
||||
use crate::contact_info::ContactInfo;
|
||||
use crate::crds_value::{CrdsData, Vote};
|
||||
use itertools::Itertools;
|
||||
use rand::thread_rng;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_sdk::{
|
||||
hash::{hash, HASH_BYTES},
|
||||
packet::PACKET_DATA_SIZE,
|
||||
timing::timestamp,
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) const MIN_NUM_BLOOM_FILTERS: usize = 1;
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub(crate) const MIN_NUM_BLOOM_FILTERS: usize = 64;
|
||||
use std::{iter::repeat_with, time::Duration};
|
||||
|
||||
#[test]
|
||||
fn test_hash_as_u64() {
|
||||
@@ -826,7 +850,7 @@ pub(crate) mod tests {
|
||||
let mut rng = thread_rng();
|
||||
let crds_filter_set =
|
||||
CrdsFilterSet::new(/*num_items=*/ 9672788, /*max_bytes=*/ 8196);
|
||||
let hash_values: Vec<_> = repeat_with(|| solana_sdk::hash::new_rand(&mut rng))
|
||||
let hash_values: Vec<_> = std::iter::repeat_with(|| solana_sdk::hash::new_rand(&mut rng))
|
||||
.take(1024)
|
||||
.collect();
|
||||
for hash_value in &hash_values {
|
||||
@@ -873,23 +897,37 @@ pub(crate) mod tests {
|
||||
fn test_build_crds_filter() {
|
||||
let mut rng = thread_rng();
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let crds_gossip_pull = CrdsGossipPull::default();
|
||||
let mut crds_gossip_pull = CrdsGossipPull::default();
|
||||
let mut crds = Crds::default();
|
||||
let keypairs: Vec<_> = repeat_with(Keypair::new).take(10_000).collect();
|
||||
for _ in 0..10_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..40_000 {
|
||||
let keypair = keypairs.choose(&mut rng).unwrap();
|
||||
let value = CrdsValue::new_rand(&mut rng, Some(keypair));
|
||||
if crds.insert(value, rng.gen()).is_ok() {
|
||||
for _ in 0..20_000 {
|
||||
if crds
|
||||
.insert(CrdsValue::new_rand(&mut rng, None), rng.gen())
|
||||
.is_ok()
|
||||
{
|
||||
num_inserts += 1;
|
||||
}
|
||||
}
|
||||
assert!(num_inserts > 30_000, "num inserts: {}", num_inserts);
|
||||
assert_eq!(num_inserts, 20_000);
|
||||
let filters = crds_gossip_pull.build_crds_filters(&thread_pool, &crds, MAX_BLOOM_SIZE);
|
||||
assert_eq!(filters.len(), MIN_NUM_BLOOM_FILTERS.max(32));
|
||||
let purged: Vec<_> = thread_pool.install(|| crds.purged().collect());
|
||||
let hash_values: Vec<_> = crds.values().map(|v| v.value_hash).chain(purged).collect();
|
||||
assert_eq!(hash_values.len(), 40_000);
|
||||
assert_eq!(filters.len(), 32);
|
||||
let hash_values: Vec<_> = crds
|
||||
.values()
|
||||
.map(|v| v.value_hash)
|
||||
.chain(
|
||||
crds_gossip_pull
|
||||
.purged_values
|
||||
.iter()
|
||||
.map(|(value_hash, _)| value_hash)
|
||||
.cloned(),
|
||||
)
|
||||
.collect();
|
||||
assert_eq!(hash_values.len(), 10_000 + 20_000);
|
||||
let mut false_positives = 0;
|
||||
for hash_value in hash_values {
|
||||
let mut num_hits = 0;
|
||||
@@ -904,7 +942,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
assert_eq!(num_hits, 1);
|
||||
}
|
||||
assert!(false_positives < 150_000, "fp: {}", false_positives);
|
||||
assert!(false_positives < 50_000, "fp: {}", false_positives);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1010,28 +1048,22 @@ pub(crate) mod tests {
|
||||
let now = now + 1_000;
|
||||
let mut pings = Vec::new();
|
||||
let ping_cache = Mutex::new(ping_cache);
|
||||
let old = old.contact_info().unwrap();
|
||||
let count = repeat_with(|| {
|
||||
let (peer, _filters) = node
|
||||
.new_pull_request(
|
||||
&thread_pool,
|
||||
&crds,
|
||||
&node_keypair,
|
||||
0, // self_shred_version
|
||||
now,
|
||||
None, // gossip_validators
|
||||
&HashMap::new(), // stakes
|
||||
PACKET_DATA_SIZE, // bloom_size
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
)
|
||||
.unwrap();
|
||||
peer
|
||||
})
|
||||
.take(100)
|
||||
.filter(|peer| peer != old)
|
||||
.count();
|
||||
assert!(count < 2, "count of peer != old: {}", count);
|
||||
for _ in 0..10 {
|
||||
let req = node.new_pull_request(
|
||||
&thread_pool,
|
||||
&crds,
|
||||
&node_keypair,
|
||||
0,
|
||||
now,
|
||||
None,
|
||||
&HashMap::new(),
|
||||
PACKET_DATA_SIZE,
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
);
|
||||
let (peer, _) = req.unwrap();
|
||||
assert_eq!(peer, *old.contact_info().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1132,29 +1164,24 @@ pub(crate) mod tests {
|
||||
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
||||
);
|
||||
assert_eq!(rsp[0].len(), 0);
|
||||
assert_eq!(filters.len(), MIN_NUM_BLOOM_FILTERS);
|
||||
filters.extend({
|
||||
// Should return new value since caller is new.
|
||||
let now = CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS + 1;
|
||||
let caller = ContactInfo::new_localhost(&Pubkey::new_unique(), now);
|
||||
let caller = CrdsValue::new_unsigned(CrdsData::ContactInfo(caller));
|
||||
filters
|
||||
.iter()
|
||||
.map(|(_, filter)| (caller.clone(), filter.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
assert_eq!(filters.len(), 1);
|
||||
filters.push(filters[0].clone());
|
||||
//should return new value since caller is new
|
||||
filters[1].0 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS + 1,
|
||||
)));
|
||||
|
||||
let rsp = dest.generate_pull_responses(
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
||||
);
|
||||
assert_eq!(rsp.len(), 2 * MIN_NUM_BLOOM_FILTERS);
|
||||
// There should be only one non-empty response in the 2nd half.
|
||||
// Orders are also preserved.
|
||||
assert!(rsp.iter().take(MIN_NUM_BLOOM_FILTERS).all(|r| r.is_empty()));
|
||||
assert_eq!(rsp.iter().filter(|r| r.is_empty()).count(), rsp.len() - 1);
|
||||
assert_eq!(rsp.iter().find(|r| r.len() == 1).unwrap().len(), 1);
|
||||
assert_eq!(rsp.len(), 2);
|
||||
assert_eq!(rsp[0].len(), 0);
|
||||
assert_eq!(rsp[1].len(), 1); // Orders are also preserved.
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1207,8 +1234,14 @@ pub(crate) mod tests {
|
||||
1,
|
||||
);
|
||||
assert!(rsp.iter().all(|rsp| rsp.is_empty()));
|
||||
assert!(dest_crds.get(&caller.label()).is_some());
|
||||
assert_eq!(dest_crds.get(&caller.label()).unwrap().local_timestamp, 1);
|
||||
assert!(dest_crds.lookup(&caller.label()).is_some());
|
||||
assert_eq!(
|
||||
dest_crds
|
||||
.lookup_versioned(&caller.label())
|
||||
.unwrap()
|
||||
.local_timestamp,
|
||||
1
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_process_pull_request_response() {
|
||||
@@ -1247,7 +1280,13 @@ pub(crate) mod tests {
|
||||
assert_eq!(same_key.label(), new.label());
|
||||
assert!(same_key.wallclock() < new.wallclock());
|
||||
node_crds.insert(same_key.clone(), 0).unwrap();
|
||||
assert_eq!(node_crds.get(&same_key.label()).unwrap().local_timestamp, 0);
|
||||
assert_eq!(
|
||||
node_crds
|
||||
.lookup_versioned(&same_key.label())
|
||||
.unwrap()
|
||||
.local_timestamp,
|
||||
0
|
||||
);
|
||||
let mut done = false;
|
||||
let mut pings = Vec::new();
|
||||
let ping_cache = Mutex::new(ping_cache);
|
||||
@@ -1267,7 +1306,7 @@ pub(crate) mod tests {
|
||||
);
|
||||
let (_, filters) = req.unwrap();
|
||||
let filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
||||
let rsp = dest.generate_pull_responses(
|
||||
let mut rsp = dest.generate_pull_responses(
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
@@ -1287,20 +1326,32 @@ pub(crate) mod tests {
|
||||
if rsp.is_empty() {
|
||||
continue;
|
||||
}
|
||||
assert_eq!(rsp.len(), MIN_NUM_BLOOM_FILTERS);
|
||||
assert_eq!(rsp.len(), 1);
|
||||
let failed = node
|
||||
.process_pull_response(
|
||||
&mut node_crds,
|
||||
&node_pubkey,
|
||||
&node.make_timeouts(node_pubkey, &HashMap::new(), Duration::default()),
|
||||
rsp.into_iter().flatten().collect(),
|
||||
&node.make_timeouts_def(&node_pubkey, &HashMap::new(), 0, 1),
|
||||
rsp.pop().unwrap(),
|
||||
1,
|
||||
)
|
||||
.0;
|
||||
assert_eq!(failed, 0);
|
||||
assert_eq!(node_crds.get(&new.label()).unwrap().local_timestamp, 1);
|
||||
assert_eq!(
|
||||
node_crds
|
||||
.lookup_versioned(&new.label())
|
||||
.unwrap()
|
||||
.local_timestamp,
|
||||
1
|
||||
);
|
||||
// verify that the whole record was updated for dest since this is a response from dest
|
||||
assert_eq!(node_crds.get(&same_key.label()).unwrap().local_timestamp, 1);
|
||||
assert_eq!(
|
||||
node_crds
|
||||
.lookup_versioned(&same_key.label())
|
||||
.unwrap()
|
||||
.local_timestamp,
|
||||
1
|
||||
);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
@@ -1323,24 +1374,20 @@ pub(crate) mod tests {
|
||||
0,
|
||||
)));
|
||||
node_crds.insert(old.clone(), 0).unwrap();
|
||||
let value_hash = node_crds.get(&old.label()).unwrap().value_hash;
|
||||
let value_hash = node_crds.lookup_versioned(&old.label()).unwrap().value_hash;
|
||||
|
||||
//verify self is valid
|
||||
assert_eq!(
|
||||
node_crds.get(&node_label).unwrap().value.label(),
|
||||
node_label
|
||||
);
|
||||
assert_eq!(node_crds.lookup(&node_label).unwrap().label(), node_label);
|
||||
|
||||
// purge
|
||||
let timeouts = node.make_timeouts(node_pubkey, &HashMap::new(), Duration::default());
|
||||
node.purge_active(&thread_pool, &mut node_crds, node.crds_timeout, &timeouts);
|
||||
let timeouts = node.make_timeouts_def(&node_pubkey, &HashMap::new(), 0, 1);
|
||||
node.purge_active(&thread_pool, &mut node_crds, 2, &timeouts);
|
||||
|
||||
//verify self is still valid after purge
|
||||
assert_eq!(
|
||||
node_crds.get(&node_label).unwrap().value.label(),
|
||||
node_label
|
||||
);
|
||||
assert_eq!(node_crds.get(&old.label()), None);
|
||||
assert_eq!(node_crds.num_purged(), 1);
|
||||
assert_eq!(node_crds.lookup(&node_label).unwrap().label(), node_label);
|
||||
|
||||
assert_eq!(node_crds.lookup_versioned(&old.label()), None);
|
||||
assert_eq!(node.purged_values.len(), 1);
|
||||
for _ in 0..30 {
|
||||
// there is a chance of a false positive with bloom filters
|
||||
// assert that purged value is still in the set
|
||||
@@ -1350,8 +1397,8 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
// purge the value
|
||||
node_crds.trim_purged(node.crds_timeout + 1);
|
||||
assert_eq!(node_crds.num_purged(), 0);
|
||||
node.purge_purged(3);
|
||||
assert_eq!(node.purged_values.len(), 0);
|
||||
}
|
||||
#[test]
|
||||
#[allow(clippy::float_cmp)]
|
||||
@@ -1423,13 +1470,11 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
fn run_test_mask(mask_bits: u32) {
|
||||
assert_eq!(
|
||||
(0..2u64.pow(mask_bits))
|
||||
.map(|seed| CrdsFilter::compute_mask(seed, mask_bits))
|
||||
.dedup()
|
||||
.count(),
|
||||
2u64.pow(mask_bits) as usize
|
||||
)
|
||||
let masks: Vec<_> = (0..2u64.pow(mask_bits))
|
||||
.map(|seed| CrdsFilter::compute_mask(seed, mask_bits))
|
||||
.dedup()
|
||||
.collect();
|
||||
assert_eq!(masks.len(), 2u64.pow(mask_bits) as usize)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1471,7 +1516,7 @@ pub(crate) mod tests {
|
||||
node.msg_timeout + 100,
|
||||
)
|
||||
.0,
|
||||
4
|
||||
2
|
||||
);
|
||||
|
||||
let mut node_crds = Crds::default();
|
||||
@@ -1516,7 +1561,7 @@ pub(crate) mod tests {
|
||||
node.msg_timeout + 2,
|
||||
)
|
||||
.0,
|
||||
2
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
@@ -8,27 +8,26 @@
|
||||
//! the local nodes wallclock window they are dropped silently.
|
||||
//! 2. The prune set is stored in a Bloom filter.
|
||||
|
||||
use {
|
||||
crate::{
|
||||
cluster_info::CRDS_UNIQUE_PUBKEY_CAPACITY,
|
||||
contact_info::ContactInfo,
|
||||
crds::{Crds, Cursor},
|
||||
crds_gossip::{get_stake, get_weight},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_value::CrdsValue,
|
||||
weighted_shuffle::weighted_shuffle,
|
||||
},
|
||||
bincode::serialized_size,
|
||||
indexmap::map::IndexMap,
|
||||
lru::LruCache,
|
||||
rand::{seq::SliceRandom, Rng},
|
||||
solana_runtime::bloom::{AtomicBloom, Bloom},
|
||||
solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, timing::timestamp},
|
||||
std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
ops::RangeBounds,
|
||||
},
|
||||
use crate::{
|
||||
cluster_info::CRDS_UNIQUE_PUBKEY_CAPACITY,
|
||||
contact_info::ContactInfo,
|
||||
crds::{Crds, Cursor, VersionedCrdsValue},
|
||||
crds_gossip::{get_stake, get_weight, CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_value::CrdsValue,
|
||||
weighted_shuffle::weighted_shuffle,
|
||||
};
|
||||
use bincode::serialized_size;
|
||||
use indexmap::map::IndexMap;
|
||||
use itertools::Itertools;
|
||||
use lru::LruCache;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use solana_runtime::bloom::{AtomicBloom, Bloom};
|
||||
use solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, timing::timestamp};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
ops::RangeBounds,
|
||||
};
|
||||
|
||||
pub const CRDS_GOSSIP_NUM_ACTIVE: usize = 30;
|
||||
@@ -54,10 +53,7 @@ pub struct CrdsGossipPush {
|
||||
/// bool indicates it has been pruned.
|
||||
/// This cache represents a lagging view of which validators
|
||||
/// currently have this node in their `active_set`
|
||||
received_cache: HashMap<
|
||||
Pubkey, // origin/owner
|
||||
HashMap</*gossip peer:*/ Pubkey, (/*pruned:*/ bool, /*timestamp:*/ u64)>,
|
||||
>,
|
||||
received_cache: HashMap<Pubkey, HashMap<Pubkey, (bool, u64)>>,
|
||||
last_pushed_to: LruCache<Pubkey, u64>,
|
||||
pub num_active: usize,
|
||||
pub push_fanout: usize,
|
||||
@@ -106,58 +102,67 @@ impl CrdsGossipPush {
|
||||
) -> Vec<Pubkey> {
|
||||
let origin_stake = stakes.get(origin).unwrap_or(&0);
|
||||
let self_stake = stakes.get(self_pubkey).unwrap_or(&0);
|
||||
let peers = match self.received_cache.get_mut(origin) {
|
||||
None => return Vec::default(),
|
||||
Some(peers) => peers,
|
||||
};
|
||||
let cache = self.received_cache.get(origin);
|
||||
if cache.is_none() {
|
||||
return Vec::new();
|
||||
}
|
||||
let peers = cache.unwrap();
|
||||
|
||||
let peer_stake_total: u64 = peers
|
||||
.iter()
|
||||
.filter(|(_, (pruned, _))| !pruned)
|
||||
.filter_map(|(peer, _)| stakes.get(peer))
|
||||
.filter(|v| !(v.1).0)
|
||||
.map(|v| stakes.get(v.0).unwrap_or(&0))
|
||||
.sum();
|
||||
let prune_stake_threshold = Self::prune_stake_threshold(*self_stake, *origin_stake);
|
||||
if peer_stake_total < prune_stake_threshold {
|
||||
return Vec::new();
|
||||
}
|
||||
let shuffled_staked_peers = {
|
||||
let peers: Vec<_> = peers
|
||||
.iter()
|
||||
.filter(|(_, (pruned, _))| !pruned)
|
||||
.filter_map(|(peer, _)| Some((*peer, *stakes.get(peer)?)))
|
||||
.filter(|(_, stake)| *stake > 0)
|
||||
.collect();
|
||||
let mut seed = [0; 32];
|
||||
rand::thread_rng().fill(&mut seed[..]);
|
||||
let weights: Vec<_> = peers.iter().map(|(_, stake)| *stake).collect();
|
||||
weighted_shuffle(&weights, seed)
|
||||
.into_iter()
|
||||
.map(move |i| peers[i])
|
||||
};
|
||||
|
||||
let staked_peers: Vec<(Pubkey, u64)> = peers
|
||||
.iter()
|
||||
.filter(|v| !(v.1).0)
|
||||
.filter_map(|p| stakes.get(p.0).map(|s| (*p.0, *s)))
|
||||
.filter(|(_, s)| *s > 0)
|
||||
.collect();
|
||||
|
||||
let mut seed = [0; 32];
|
||||
rand::thread_rng().fill(&mut seed[..]);
|
||||
let shuffle = weighted_shuffle(
|
||||
&staked_peers.iter().map(|(_, stake)| *stake).collect_vec(),
|
||||
seed,
|
||||
);
|
||||
|
||||
let mut keep = HashSet::new();
|
||||
let mut peer_stake_sum = 0;
|
||||
keep.insert(*origin);
|
||||
for (peer, stake) in shuffled_staked_peers {
|
||||
if peer == *origin {
|
||||
for next in shuffle {
|
||||
let (next_peer, next_stake) = staked_peers[next];
|
||||
if next_peer == *origin {
|
||||
continue;
|
||||
}
|
||||
keep.insert(peer);
|
||||
peer_stake_sum += stake;
|
||||
keep.insert(next_peer);
|
||||
peer_stake_sum += next_stake;
|
||||
if peer_stake_sum >= prune_stake_threshold
|
||||
&& keep.len() >= CRDS_GOSSIP_PRUNE_MIN_INGRESS_NODES
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (peer, (pruned, _)) in peers.iter_mut() {
|
||||
if !*pruned && !keep.contains(peer) {
|
||||
*pruned = true;
|
||||
}
|
||||
}
|
||||
peers
|
||||
|
||||
let pruned_peers: Vec<Pubkey> = peers
|
||||
.keys()
|
||||
.filter(|peer| !keep.contains(peer))
|
||||
.copied()
|
||||
.collect()
|
||||
.filter(|p| !keep.contains(p))
|
||||
.cloned()
|
||||
.collect();
|
||||
pruned_peers.iter().for_each(|p| {
|
||||
self.received_cache
|
||||
.get_mut(origin)
|
||||
.unwrap()
|
||||
.get_mut(p)
|
||||
.unwrap()
|
||||
.0 = true;
|
||||
});
|
||||
pruned_peers
|
||||
}
|
||||
|
||||
fn wallclock_window(&self, now: u64) -> impl RangeBounds<u64> {
|
||||
@@ -171,7 +176,7 @@ impl CrdsGossipPush {
|
||||
from: &Pubkey,
|
||||
value: CrdsValue,
|
||||
now: u64,
|
||||
) -> Result<(), CrdsGossipError> {
|
||||
) -> Result<Option<VersionedCrdsValue>, CrdsGossipError> {
|
||||
self.num_total += 1;
|
||||
if !self.wallclock_window(now).contains(&value.wallclock()) {
|
||||
return Err(CrdsGossipError::PushMessageTimeout);
|
||||
@@ -265,48 +270,46 @@ impl CrdsGossipPush {
|
||||
network_size: usize,
|
||||
ratio: usize,
|
||||
) {
|
||||
const BLOOM_FALSE_RATE: f64 = 0.1;
|
||||
const BLOOM_MAX_BITS: usize = 1024 * 8 * 4;
|
||||
#[cfg(debug_assertions)]
|
||||
const MIN_NUM_BLOOM_ITEMS: usize = 512;
|
||||
#[cfg(not(debug_assertions))]
|
||||
const MIN_NUM_BLOOM_ITEMS: usize = CRDS_UNIQUE_PUBKEY_CAPACITY;
|
||||
let mut rng = rand::thread_rng();
|
||||
let need = Self::compute_need(self.num_active, self.active_set.len(), ratio);
|
||||
let mut new_items = HashMap::new();
|
||||
let (weights, peers): (Vec<_>, Vec<_>) = self
|
||||
.push_options(
|
||||
crds,
|
||||
&self_id,
|
||||
self_shred_version,
|
||||
stakes,
|
||||
gossip_validators,
|
||||
)
|
||||
.into_iter()
|
||||
.unzip();
|
||||
if peers.is_empty() {
|
||||
|
||||
let options: Vec<_> = self.push_options(
|
||||
crds,
|
||||
&self_id,
|
||||
self_shred_version,
|
||||
stakes,
|
||||
gossip_validators,
|
||||
);
|
||||
if options.is_empty() {
|
||||
return;
|
||||
}
|
||||
let num_bloom_items = MIN_NUM_BLOOM_ITEMS.max(network_size);
|
||||
let shuffle = {
|
||||
let mut seed = [0; 32];
|
||||
rng.fill(&mut seed[..]);
|
||||
weighted_shuffle(&weights, seed).into_iter()
|
||||
};
|
||||
for peer in shuffle.map(|i| peers[i].id) {
|
||||
if new_items.len() >= need {
|
||||
break;
|
||||
|
||||
let mut seed = [0; 32];
|
||||
rng.fill(&mut seed[..]);
|
||||
let mut shuffle = weighted_shuffle(
|
||||
&options.iter().map(|weighted| weighted.0).collect_vec(),
|
||||
seed,
|
||||
)
|
||||
.into_iter();
|
||||
|
||||
while new_items.len() < need {
|
||||
match shuffle.next() {
|
||||
Some(index) => {
|
||||
let item = options[index].1;
|
||||
if self.active_set.get(&item.id).is_some() {
|
||||
continue;
|
||||
}
|
||||
if new_items.get(&item.id).is_some() {
|
||||
continue;
|
||||
}
|
||||
let size = cmp::max(CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS, network_size);
|
||||
let bloom: AtomicBloom<_> = Bloom::random(size, 0.1, 1024 * 8 * 4).into();
|
||||
bloom.add(&item.id);
|
||||
new_items.insert(item.id, bloom);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
if self.active_set.contains_key(&peer) || new_items.contains_key(&peer) {
|
||||
continue;
|
||||
}
|
||||
let bloom = AtomicBloom::from(Bloom::random(
|
||||
num_bloom_items,
|
||||
BLOOM_FALSE_RATE,
|
||||
BLOOM_MAX_BITS,
|
||||
));
|
||||
bloom.add(&peer);
|
||||
new_items.insert(peer, bloom);
|
||||
}
|
||||
let mut keys: Vec<Pubkey> = self.active_set.keys().cloned().collect();
|
||||
keys.shuffle(&mut rng);
|
||||
@@ -397,10 +400,9 @@ impl CrdsGossipPush {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
crate::{contact_info::ContactInfo, crds_value::CrdsData},
|
||||
};
|
||||
use super::*;
|
||||
use crate::contact_info::ContactInfo;
|
||||
use crate::crds_value::CrdsData;
|
||||
|
||||
#[test]
|
||||
fn test_prune() {
|
||||
@@ -460,9 +462,9 @@ mod test {
|
||||
// push a new message
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value.clone(), 0),
|
||||
Ok(())
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(crds.get(&label).unwrap().value, value);
|
||||
assert_eq!(crds.lookup(&label), Some(&value));
|
||||
|
||||
// push it again
|
||||
assert_matches!(
|
||||
@@ -481,7 +483,7 @@ mod test {
|
||||
// push a new message
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value, 0),
|
||||
Ok(())
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
// push an old version
|
||||
@@ -525,16 +527,19 @@ mod test {
|
||||
|
||||
// push a new message
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value_old, 0),
|
||||
Ok(())
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value_old.clone(), 0),
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
// push an old version
|
||||
ci.wallclock = 1;
|
||||
let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci));
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value, 0),
|
||||
Ok(())
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value, 0)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.value,
|
||||
value_old
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -555,7 +560,7 @@ mod test {
|
||||
0,
|
||||
)));
|
||||
|
||||
assert_eq!(crds.insert(value1.clone(), now), Ok(()));
|
||||
assert_eq!(crds.insert(value1.clone(), now), Ok(None));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
|
||||
assert!(push.active_set.get(&value1.label().pubkey()).is_some());
|
||||
@@ -564,7 +569,7 @@ mod test {
|
||||
0,
|
||||
)));
|
||||
assert!(push.active_set.get(&value2.label().pubkey()).is_none());
|
||||
assert_eq!(crds.insert(value2.clone(), now), Ok(()));
|
||||
assert_eq!(crds.insert(value2.clone(), now), Ok(None));
|
||||
for _ in 0..30 {
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
if push.active_set.get(&value2.label().pubkey()).is_some() {
|
||||
@@ -577,7 +582,7 @@ mod test {
|
||||
let value2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(
|
||||
ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0),
|
||||
));
|
||||
assert_eq!(crds.insert(value2.clone(), now), Ok(()));
|
||||
assert_eq!(crds.insert(value2.clone(), now), Ok(None));
|
||||
}
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
assert_eq!(push.active_set.len(), push.num_active);
|
||||
@@ -735,7 +740,7 @@ mod test {
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer.clone(), now), Ok(()));
|
||||
assert_eq!(crds.insert(peer.clone(), now), Ok(None));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
|
||||
let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
@@ -746,7 +751,7 @@ mod test {
|
||||
expected.insert(peer.label().pubkey(), vec![new_msg.clone()]);
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), new_msg, 0),
|
||||
Ok(())
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(push.active_set.len(), 1);
|
||||
assert_eq!(push.new_push_messages(&crds, 0), expected);
|
||||
@@ -754,32 +759,36 @@ mod test {
|
||||
#[test]
|
||||
fn test_personalized_push_messages() {
|
||||
let now = timestamp();
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut crds = Crds::default();
|
||||
let mut push = CrdsGossipPush::default();
|
||||
let peers: Vec<_> = vec![0, 0, now]
|
||||
.into_iter()
|
||||
.map(|wallclock| {
|
||||
let mut peer = ContactInfo::new_rand(&mut rng, /*pubkey=*/ None);
|
||||
peer.wallclock = wallclock;
|
||||
CrdsValue::new_unsigned(CrdsData::ContactInfo(peer))
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(crds.insert(peers[0].clone(), now), Ok(()));
|
||||
assert_eq!(crds.insert(peers[1].clone(), now), Ok(()));
|
||||
let peer_1 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer_1.clone(), now), Ok(None));
|
||||
let peer_2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer_2.clone(), now), Ok(None));
|
||||
let peer_3 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
now,
|
||||
)));
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), peers[2].clone(), now),
|
||||
Ok(())
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), peer_3.clone(), now),
|
||||
Ok(None)
|
||||
);
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
|
||||
// push 3's contact info to 1 and 2 and 3
|
||||
let expected: HashMap<_, _> = vec![
|
||||
(peers[0].pubkey(), vec![peers[2].clone()]),
|
||||
(peers[1].pubkey(), vec![peers[2].clone()]),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&peer_3.pubkey(),
|
||||
0,
|
||||
)));
|
||||
let mut expected = HashMap::new();
|
||||
expected.insert(peer_1.pubkey(), vec![new_msg.clone()]);
|
||||
expected.insert(peer_2.pubkey(), vec![new_msg]);
|
||||
assert_eq!(push.active_set.len(), 3);
|
||||
assert_eq!(push.new_push_messages(&crds, now), expected);
|
||||
}
|
||||
@@ -792,7 +801,7 @@ mod test {
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer.clone(), 0), Ok(()));
|
||||
assert_eq!(crds.insert(peer.clone(), 0), Ok(None));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
|
||||
let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
@@ -802,7 +811,7 @@ mod test {
|
||||
let expected = HashMap::new();
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), new_msg.clone(), 0),
|
||||
Ok(())
|
||||
Ok(None)
|
||||
);
|
||||
push.process_prune_msg(
|
||||
&self_id,
|
||||
@@ -819,7 +828,7 @@ mod test {
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer, 0), Ok(()));
|
||||
assert_eq!(crds.insert(peer, 0), Ok(None));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
|
||||
let mut ci = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
||||
@@ -828,7 +837,7 @@ mod test {
|
||||
let expected = HashMap::new();
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), new_msg, 1),
|
||||
Ok(())
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(push.new_push_messages(&crds, 0), expected);
|
||||
}
|
||||
@@ -844,9 +853,9 @@ mod test {
|
||||
// push a new message
|
||||
assert_eq!(
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), value.clone(), 0),
|
||||
Ok(())
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(crds.get(&label).unwrap().value, value);
|
||||
assert_eq!(crds.lookup(&label), Some(&value));
|
||||
|
||||
// push it again
|
||||
assert_matches!(
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user