Compare commits
1 Commits
v1.10.5
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
ffb9efa89d |
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script is used to upload the full buildkite pipeline. The steps defined
|
||||
# in the buildkite UI should simply be:
|
||||
#
|
||||
# steps:
|
||||
# - command: ".buildkite/pipeline-upload.sh"
|
||||
#
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")"/..
|
||||
source ci/_
|
||||
sudo chmod 0777 ci/buildkite-pipeline-in-disk.sh
|
||||
|
||||
_ ci/buildkite-pipeline-in-disk.sh pipeline.yml
|
||||
echo +++ pipeline
|
||||
cat pipeline.yml
|
||||
|
||||
_ buildkite-agent pipeline upload pipeline.yml
|
@@ -12,14 +12,7 @@ export PS4="++"
|
||||
# Restore target/ from the previous CI build on this machine
|
||||
#
|
||||
eval "$(ci/channel-info.sh)"
|
||||
eval "$(ci/sbf-tools-info.sh)"
|
||||
source "ci/rust-version.sh"
|
||||
HOST_RUST_VERSION="$rust_stable"
|
||||
pattern='^[0-9]+\.[0-9]+\.[0-9]+$'
|
||||
if [[ ${HOST_RUST_VERSION} =~ ${pattern} ]]; then
|
||||
HOST_RUST_VERSION="${rust_stable%.*}"
|
||||
fi
|
||||
export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"-"$SBF_TOOLS_VERSION"-"$HOST_RUST_VERSION"
|
||||
export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
|
||||
(
|
||||
set -x
|
||||
MAX_CACHE_SIZE=18 # gigabytes
|
||||
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,9 +1,5 @@
|
||||
#### Problem
|
||||
|
||||
|
||||
|
||||
#### Summary of Changes
|
||||
|
||||
|
||||
|
||||
Fixes #
|
||||
|
66
.github/workflows/client-targets.yml
vendored
66
.github/workflows/client-targets.yml
vendored
@@ -1,66 +0,0 @@
|
||||
name: client_targets
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "client/**"
|
||||
- "sdk/**"
|
||||
- ".github/workflows/client-targets.yml"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
check_compilation:
|
||||
name: Client compilation
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64-apple-ios, x86_64-apple-ios, aarch64-apple-darwin, x86_64-apple-darwin, aarch64-linux-android, armv7-linux-androideabi, i686-linux-android, x86_64-linux-android]
|
||||
include:
|
||||
- target: aarch64-apple-ios
|
||||
platform: ios
|
||||
os: macos-latest
|
||||
- target: x86_64-apple-ios
|
||||
platform: ios
|
||||
os: macos-latest
|
||||
- target: aarch64-apple-darwin
|
||||
platform: ios
|
||||
os: macos-latest
|
||||
- target: x86_64-apple-darwin
|
||||
platform: ios
|
||||
os: macos-latest
|
||||
- target: aarch64-linux-android
|
||||
platform: android
|
||||
os: ubuntu-latest
|
||||
- target: armv7-linux-androideabi
|
||||
platform: android
|
||||
os: ubuntu-latest
|
||||
- target: i686-linux-android
|
||||
platform: android
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-linux-android
|
||||
platform: android
|
||||
os: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: ${{ matrix.target }}
|
||||
- name: Install cargo-ndk
|
||||
if: ${{ matrix.platform == 'android' }}
|
||||
run: cargo install cargo-ndk
|
||||
- uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.platform == 'android' }}
|
||||
with:
|
||||
command: ndk
|
||||
args: --target ${{ matrix.target }} build -p solana-client
|
||||
- uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.platform == 'ios' }}
|
||||
with:
|
||||
command: build
|
||||
args: -p solana-client --target ${{ matrix.target }}
|
33
.github/workflows/explorer_preview.yml
vendored
33
.github/workflows/explorer_preview.yml
vendored
@@ -17,42 +17,35 @@ jobs:
|
||||
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }} #Optional
|
||||
vercel-org-id: ${{ secrets.ORG_ID}} #Required
|
||||
vercel-project-id: ${{ secrets.PROJECT_ID}} #Required
|
||||
vercel-project-id: ${{ secrets.PROJECT_ID}} #Required
|
||||
working-directory: ./explorer
|
||||
scope: ${{ secrets.TEAM_ID }}
|
||||
|
||||
- name: vercel url
|
||||
run : |
|
||||
touch vercelfile.txt
|
||||
vercel --token ${{secrets.VERCEL_TOKEN}} ls explorer --scope team_8A2WD7p4uR7tmKX9M68loHXI > vercelfile.txt
|
||||
vercel --token ${{secrets.VERCEL_TOKEN}} ls solana > vercelfile.txt
|
||||
echo "first line"
|
||||
touch vercelfile1.txt
|
||||
head -n 2 vercelfile.txt > vercelfile1.txt
|
||||
touch vercelfile2.txt
|
||||
tail -n 1 vercelfile1.txt > vercelfile2.txt
|
||||
filtered_url7=$(cut -f7 -d" " vercelfile2.txt)
|
||||
echo "filtered_url7 is: $filtered_url7"
|
||||
filtered_url=$(cut -f8 -d" " vercelfile2.txt)
|
||||
touch .env.preview1
|
||||
echo "$filtered_url7" > .env.preview1
|
||||
#filtered_url=$(cat vercelfile2.txt )
|
||||
#echo "$filtered_url" >> .env.preview1
|
||||
echo "$filtered_url" > .env.preview1
|
||||
echo "filtered_url is: $filtered_url"
|
||||
|
||||
- name: Fetching Vercel Preview Deployment Link
|
||||
|
||||
- name: Run tests
|
||||
uses: mathiasvr/command-output@v1
|
||||
id: test1
|
||||
id: tests2
|
||||
with:
|
||||
run: |
|
||||
echo "$(cat .env.preview1)"
|
||||
- name: Fetching PR commit URL
|
||||
uses: mathiasvr/command-output@v1
|
||||
id: test2
|
||||
with:
|
||||
run: |
|
||||
HEAD_SHA=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/solana-labs/solana/pulls | jq .[0] | jq -r .head.sha)
|
||||
USER_NAME=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/solana-labs/solana/pulls | jq .[0] | jq -r .head.user.login)
|
||||
echo "github.com/$USER_NAME/solana/commit/$HEAD_SHA"
|
||||
|
||||
- name: Slack Notification4
|
||||
- name: Slack Notification1
|
||||
uses: rtCamp/action-slack-notify@master
|
||||
env:
|
||||
SLACK_MESSAGE: ' Vercel Link: ${{ steps.test1.outputs.stdout }} PR Commit: ${{steps.test2.outputs.stdout}}'
|
||||
SLACK_TITLE: Vercel "Explorer" Preview Deployment Link , PR Commit
|
||||
SLACK_MESSAGE: ${{ steps.tests2.outputs.stdout }}
|
||||
SLACK_TITLE: Vercel "Explorer" Preview Deployment Link
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
|
6
.github/workflows/explorer_production.yml
vendored
6
.github/workflows/explorer_production.yml
vendored
@@ -30,8 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: explorer
|
||||
|
||||
working-directory: explorer
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -39,8 +38,9 @@ jobs:
|
||||
- uses: amondnet/vercel-action@v20
|
||||
with:
|
||||
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
|
||||
github-token: ${{ secrets.PAT }} #Optional
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }} #Optional
|
||||
vercel-args: '--prod' #for production
|
||||
vercel-org-id: ${{ secrets.ORG_ID}} #Required
|
||||
vercel-project-id: ${{ secrets.PROJECT_ID}} #Required
|
||||
working-directory: ./explorer
|
||||
scope: ${{ secrets.TEAM_ID }}
|
||||
|
@@ -93,7 +93,6 @@ pull_request_rules:
|
||||
- author=mergify[bot]
|
||||
- head~=^mergify/bp/
|
||||
- "#status-failure=0"
|
||||
- "-merged"
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
@@ -114,10 +113,3 @@ pull_request_rules:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.9
|
||||
|
||||
commands_restrictions:
|
||||
# The author of copied PRs is the Mergify user.
|
||||
# Restrict `copy` access to Core Contributors
|
||||
copy:
|
||||
conditions:
|
||||
- author=@core-contributors
|
||||
|
19
.travis.yml
19
.travis.yml
@@ -78,24 +78,7 @@ jobs:
|
||||
# - sudo apt-get install libssl-dev libudev-dev
|
||||
|
||||
# docs pull request
|
||||
# - name: "explorer"
|
||||
# if: type = pull_request AND branch = master
|
||||
|
||||
# language: node_js
|
||||
# node_js:
|
||||
# - "lts/*"
|
||||
|
||||
# cache:
|
||||
# directories:
|
||||
# - ~/.npm
|
||||
|
||||
# before_install:
|
||||
# - .travis/affects.sh explorer/ .travis || travis_terminate 0
|
||||
# - cd explorer
|
||||
|
||||
# script:
|
||||
# - npm run build
|
||||
# - npm run format
|
||||
|
||||
- name: "docs"
|
||||
if: type IN (push, pull_request) OR tag IS present
|
||||
language: node_js
|
||||
|
2221
Cargo.lock
generated
2221
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@@ -1,48 +1,48 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"account-decoder",
|
||||
"accounts-bench",
|
||||
"accountsdb-plugin-interface",
|
||||
"accountsdb-plugin-manager",
|
||||
"accounts-cluster-bench",
|
||||
"bench-streamer",
|
||||
"bench-tps",
|
||||
"accounts-bench",
|
||||
"banking-bench",
|
||||
"banks-client",
|
||||
"banks-interface",
|
||||
"banks-server",
|
||||
"bench-streamer",
|
||||
"bench-tps",
|
||||
"bloom",
|
||||
"bucket_map",
|
||||
"bloom",
|
||||
"clap-utils",
|
||||
"cli",
|
||||
"cli-config",
|
||||
"cli-output",
|
||||
"client",
|
||||
"client-test",
|
||||
"core",
|
||||
"dos",
|
||||
"download-utils",
|
||||
"entry",
|
||||
"faucet",
|
||||
"frozen-abi",
|
||||
"perf",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis-utils",
|
||||
"geyser-plugin-interface",
|
||||
"geyser-plugin-manager",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
"ledger",
|
||||
"ledger-tool",
|
||||
"local-cluster",
|
||||
"log-analyzer",
|
||||
"logger",
|
||||
"measure",
|
||||
"log-analyzer",
|
||||
"merkle-root-bench",
|
||||
"merkle-tree",
|
||||
"storage-bigtable",
|
||||
"storage-proto",
|
||||
"streamer",
|
||||
"measure",
|
||||
"metrics",
|
||||
"net-shaper",
|
||||
"net-utils",
|
||||
"notifier",
|
||||
"perf",
|
||||
"poh",
|
||||
"poh-bench",
|
||||
"program-test",
|
||||
@@ -52,17 +52,11 @@ members = [
|
||||
"programs/bpf_loader/gen-syscall-list",
|
||||
"programs/compute-budget",
|
||||
"programs/config",
|
||||
"programs/ed25519-tests",
|
||||
"programs/stake",
|
||||
"programs/vote",
|
||||
"programs/zk-token-proof",
|
||||
"rayon-threadlimit",
|
||||
"rbpf-cli",
|
||||
"remote-wallet",
|
||||
"replica-lib",
|
||||
"replica-node",
|
||||
"rpc",
|
||||
"rpc-test",
|
||||
"runtime",
|
||||
"runtime/store-tool",
|
||||
"sdk",
|
||||
@@ -70,19 +64,24 @@ members = [
|
||||
"sdk/cargo-test-bpf",
|
||||
"send-transaction-service",
|
||||
"stake-accounts",
|
||||
"storage-bigtable",
|
||||
"storage-proto",
|
||||
"streamer",
|
||||
"sys-tuner",
|
||||
"test-validator",
|
||||
"tokens",
|
||||
"transaction-dos",
|
||||
"transaction-status",
|
||||
"account-decoder",
|
||||
"upload-perf",
|
||||
"validator",
|
||||
"net-utils",
|
||||
"version",
|
||||
"cli",
|
||||
"rayon-threadlimit",
|
||||
"watchtower",
|
||||
"replica-node",
|
||||
"replica-lib",
|
||||
"test-validator",
|
||||
"rpc-test",
|
||||
"client-test",
|
||||
"zk-token-sdk",
|
||||
"programs/zk-token-proof",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright 2022 Solana Foundation.
|
||||
Copyright 2020 Solana Foundation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,21 +10,21 @@ license = "Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
Inflector = "0.11.4"
|
||||
base64 = "0.13.0"
|
||||
base64 = "0.12.3"
|
||||
bincode = "1.3.3"
|
||||
bs58 = "0.4.0"
|
||||
bv = "0.11.1"
|
||||
Inflector = "0.11.4"
|
||||
lazy_static = "1.4.0"
|
||||
serde = "1.0.136"
|
||||
serde = "1.0.134"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.79"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.5" }
|
||||
serde_json = "1.0.78"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
|
||||
spl-token = { version = "=3.2.0", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
zstd = "0.11.1"
|
||||
zstd = "0.9.2"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -33,7 +33,7 @@ pub type StringDecimals = String;
|
||||
pub const MAX_BASE58_BYTES: usize = 128;
|
||||
|
||||
/// A duplicate representation of an Account for pretty JSON serialization
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UiAccount {
|
||||
pub lamports: u64,
|
||||
|
@@ -5,7 +5,7 @@ use {
|
||||
parse_nonce::parse_nonce,
|
||||
parse_stake::parse_stake,
|
||||
parse_sysvar::parse_sysvar,
|
||||
parse_token::{parse_token, spl_token_ids},
|
||||
parse_token::{parse_token, spl_token_id},
|
||||
parse_vote::parse_vote,
|
||||
},
|
||||
inflector::Inflector,
|
||||
@@ -21,6 +21,7 @@ lazy_static! {
|
||||
static ref STAKE_PROGRAM_ID: Pubkey = 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();
|
||||
static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id();
|
||||
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
|
||||
let mut m = HashMap::new();
|
||||
@@ -30,9 +31,7 @@ lazy_static! {
|
||||
);
|
||||
m.insert(*CONFIG_PROGRAM_ID, ParsableAccount::Config);
|
||||
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
|
||||
for spl_token_id in spl_token_ids() {
|
||||
m.insert(spl_token_id, ParsableAccount::SplToken);
|
||||
}
|
||||
m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::SplToken);
|
||||
m.insert(*STAKE_PROGRAM_ID, ParsableAccount::Stake);
|
||||
m.insert(*SYSVAR_PROGRAM_ID, ParsableAccount::Sysvar);
|
||||
m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
|
||||
|
@@ -15,31 +15,16 @@ use {
|
||||
|
||||
// A helper function to convert spl_token::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
fn spl_token_id() -> Pubkey {
|
||||
pub fn spl_token_id() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token::id().to_bytes())
|
||||
}
|
||||
|
||||
// Returns all known SPL Token program ids
|
||||
pub fn spl_token_ids() -> Vec<Pubkey> {
|
||||
vec![spl_token_id()]
|
||||
}
|
||||
|
||||
// Check if the provided program id as a known SPL Token program id
|
||||
pub fn is_known_spl_token_id(program_id: &Pubkey) -> bool {
|
||||
*program_id == spl_token_id()
|
||||
}
|
||||
|
||||
// A helper function to convert spl_token::native_mint::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_native_mint() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token::native_mint::id().to_bytes())
|
||||
}
|
||||
|
||||
// The program id of the `spl_token_native_mint` account
|
||||
pub fn spl_token_native_mint_program_id() -> Pubkey {
|
||||
spl_token_id()
|
||||
}
|
||||
|
||||
// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
|
||||
SplTokenPubkey::new_from_array(pubkey.to_bytes())
|
||||
|
@@ -2,21 +2,21 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
log = "0.4.14"
|
||||
rayon = "1.5.1"
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
clap = "2.33.1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-accounts-cluster-bench"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,25 +13,25 @@ clap = "2.33.1"
|
||||
log = "0.4.14"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.1"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.5" }
|
||||
solana-client = { path = "../client", version = "=1.10.5" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.5" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
|
||||
solana-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.0" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.0" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
spl-token = { version = "=3.2.0", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "=1.10.5" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.10.5" }
|
||||
solana-test-validator = { path = "../test-validator", version = "=1.10.5" }
|
||||
solana-core = { path = "../core", version = "=1.10.0" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.10.0" }
|
||||
solana-test-validator = { path = "../test-validator", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -673,7 +673,7 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_accounts_cluster_bench() {
|
||||
solana_logger::setup();
|
||||
let validator_config = ValidatorConfig::default_for_test();
|
||||
let validator_config = ValidatorConfig::default();
|
||||
let num_nodes = 1;
|
||||
let mut config = ClusterConfig {
|
||||
cluster_lamports: 10_000_000,
|
||||
|
@@ -1,19 +1,19 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-geyser-plugin-interface"
|
||||
description = "The Solana Geyser plugin interface."
|
||||
version = "1.10.5"
|
||||
name = "solana-accountsdb-plugin-interface"
|
||||
description = "The Solana AccountsDb plugin interface."
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-geyser-plugin-interface"
|
||||
documentation = "https://docs.rs/solana-accountsdb-plugin-interface"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.11"
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
thiserror = "1.0.30"
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
20
accountsdb-plugin-interface/README.md
Normal file
20
accountsdb-plugin-interface/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
<p align="center">
|
||||
<a href="https://solana.com">
|
||||
<img alt="Solana" src="https://i.imgur.com/IKyzQ6T.png" width="250" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# Solana AccountsDb Plugin Interface
|
||||
|
||||
This crate enables an AccountsDb plugin to be plugged into the Solana Validator runtime to take actions
|
||||
at the time of each account update; for example, saving the account state to an external database. The plugin must implement the `AccountsDbPlugin` trait. Please see the detail of the `accountsdb_plugin_interface.rs` for the interface definition.
|
||||
|
||||
The plugin should produce a `cdylib` dynamic library, which must expose a `C` function `_create_plugin()` that
|
||||
instantiates the implementation of the interface.
|
||||
|
||||
The `solana-accountsdb-plugin-postgres` crate provides an example of how to create a plugin which saves the accounts data into an
|
||||
external PostgreSQL databases.
|
||||
|
||||
More information about Solana is available in the [Solana documentation](https://docs.solana.com/).
|
||||
|
||||
Still have questions? Ask us on [Discord](https://discordapp.com/invite/pquxPsq)
|
@@ -1,5 +1,5 @@
|
||||
/// The interface for Geyser plugins. A plugin must implement
|
||||
/// the GeyserPlugin trait to work with the runtime.
|
||||
/// The interface for AccountsDb plugins. A plugin must implement
|
||||
/// the AccountsDbPlugin trait to work with the runtime.
|
||||
/// In addition, the dynamic library must export a "C" function _create_plugin which
|
||||
/// creates the implementation of the plugin.
|
||||
use {
|
||||
@@ -9,7 +9,9 @@ use {
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
impl Eq for ReplicaAccountInfo<'_> {}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
/// Information about an account being updated
|
||||
pub struct ReplicaAccountInfo<'a> {
|
||||
/// The Pubkey for the account
|
||||
@@ -85,7 +87,7 @@ pub enum ReplicaBlockInfoVersions<'a> {
|
||||
|
||||
/// Errors returned by plugin calls
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GeyserPluginError {
|
||||
pub enum AccountsDbPluginError {
|
||||
/// Error opening the configuration file; for example, when the file
|
||||
/// is not found or when the validator process has no permission to read it.
|
||||
#[error("Error opening config file. Error detail: ({0}).")]
|
||||
@@ -110,7 +112,7 @@ pub enum GeyserPluginError {
|
||||
}
|
||||
|
||||
/// The current status of a slot
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SlotStatus {
|
||||
/// The highest slot of the heaviest fork processed by the node. Ledger state at this slot is
|
||||
/// not derived from a confirmed or finalized block, but if multiple forks are present, is from
|
||||
@@ -134,12 +136,12 @@ impl SlotStatus {
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, GeyserPluginError>;
|
||||
pub type Result<T> = std::result::Result<T, AccountsDbPluginError>;
|
||||
|
||||
/// Defines a Geyser plugin, to stream data from the runtime.
|
||||
/// Geyser plugins must describe desired behavior for load and unload,
|
||||
/// Defines an AccountsDb plugin, to stream data from the runtime.
|
||||
/// AccountsDb plugins must describe desired behavior for load and unload,
|
||||
/// as well as how they will handle streamed data.
|
||||
pub trait GeyserPlugin: Any + Send + Sync + std::fmt::Debug {
|
||||
pub trait AccountsDbPlugin: Any + Send + Sync + std::fmt::Debug {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// The callback called when a plugin is loaded by the system,
|
1
accountsdb-plugin-interface/src/lib.rs
Normal file
1
accountsdb-plugin-interface/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod accountsdb_plugin_interface;
|
29
accountsdb-plugin-manager/Cargo.toml
Normal file
29
accountsdb-plugin-manager/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-accountsdb-plugin-manager"
|
||||
description = "The Solana AccountsDb plugin manager."
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-validator"
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
crossbeam-channel = "0.5"
|
||||
json5 = "0.4.1"
|
||||
libloading = "0.7.3"
|
||||
log = "0.4.11"
|
||||
serde_json = "1.0.78"
|
||||
solana-accountsdb-plugin-interface = { path = "../accountsdb-plugin-interface", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.0" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
thiserror = "1.0.30"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
@@ -1,8 +1,8 @@
|
||||
/// Module responsible for notifying plugins of account updates
|
||||
use {
|
||||
crate::geyser_plugin_manager::GeyserPluginManager,
|
||||
crate::accountsdb_plugin_manager::AccountsDbPluginManager,
|
||||
log::*,
|
||||
solana_geyser_plugin_interface::geyser_plugin_interface::{
|
||||
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::{
|
||||
ReplicaAccountInfo, ReplicaAccountInfoVersions,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
@@ -19,7 +19,7 @@ use {
|
||||
};
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AccountsUpdateNotifierImpl {
|
||||
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
|
||||
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
|
||||
}
|
||||
|
||||
impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
|
||||
@@ -30,14 +30,14 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
|
||||
}
|
||||
|
||||
fn notify_account_restore_from_snapshot(&self, slot: Slot, account: &StoredAccountMeta) {
|
||||
let mut measure_all = Measure::start("geyser-plugin-notify-account-restore-all");
|
||||
let mut measure_copy = Measure::start("geyser-plugin-copy-stored-account-info");
|
||||
let mut measure_all = Measure::start("accountsdb-plugin-notify-account-restore-all");
|
||||
let mut measure_copy = Measure::start("accountsdb-plugin-copy-stored-account-info");
|
||||
|
||||
let account = self.accountinfo_from_stored_account_meta(account);
|
||||
measure_copy.stop();
|
||||
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-copy-stored-account-info-us",
|
||||
"accountsdb-plugin-copy-stored-account-info-us",
|
||||
measure_copy.as_us() as usize,
|
||||
100000,
|
||||
100000
|
||||
@@ -49,7 +49,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
|
||||
measure_all.stop();
|
||||
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-notify-account-restore-all-us",
|
||||
"accountsdb-plugin-notify-account-restore-all-us",
|
||||
measure_all.as_us() as usize,
|
||||
100000,
|
||||
100000
|
||||
@@ -63,7 +63,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
|
||||
}
|
||||
|
||||
for plugin in plugin_manager.plugins.iter_mut() {
|
||||
let mut measure = Measure::start("geyser-plugin-end-of-restore-from-snapshot");
|
||||
let mut measure = Measure::start("accountsdb-plugin-end-of-restore-from-snapshot");
|
||||
match plugin.notify_end_of_startup() {
|
||||
Err(err) => {
|
||||
error!(
|
||||
@@ -81,7 +81,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
|
||||
}
|
||||
measure.stop();
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-end-of-restore-from-snapshot",
|
||||
"accountsdb-plugin-end-of-restore-from-snapshot",
|
||||
measure.as_us() as usize
|
||||
);
|
||||
}
|
||||
@@ -89,7 +89,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
|
||||
}
|
||||
|
||||
impl AccountsUpdateNotifierImpl {
|
||||
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
|
||||
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
|
||||
AccountsUpdateNotifierImpl { plugin_manager }
|
||||
}
|
||||
|
||||
@@ -130,14 +130,14 @@ impl AccountsUpdateNotifierImpl {
|
||||
slot: Slot,
|
||||
is_startup: bool,
|
||||
) {
|
||||
let mut measure2 = Measure::start("geyser-plugin-notify_plugins_of_account_update");
|
||||
let mut measure2 = Measure::start("accountsdb-plugin-notify_plugins_of_account_update");
|
||||
let mut plugin_manager = self.plugin_manager.write().unwrap();
|
||||
|
||||
if plugin_manager.plugins.is_empty() {
|
||||
return;
|
||||
}
|
||||
for plugin in plugin_manager.plugins.iter_mut() {
|
||||
let mut measure = Measure::start("geyser-plugin-update-account");
|
||||
let mut measure = Measure::start("accountsdb-plugin-update-account");
|
||||
match plugin.update_account(
|
||||
ReplicaAccountInfoVersions::V0_0_1(&account),
|
||||
slot,
|
||||
@@ -163,7 +163,7 @@ impl AccountsUpdateNotifierImpl {
|
||||
}
|
||||
measure.stop();
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-update-account-us",
|
||||
"accountsdb-plugin-update-account-us",
|
||||
measure.as_us() as usize,
|
||||
100000,
|
||||
100000
|
||||
@@ -171,7 +171,7 @@ impl AccountsUpdateNotifierImpl {
|
||||
}
|
||||
measure2.stop();
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-notify_plugins_of_account_update-us",
|
||||
"accountsdb-plugin-notify_plugins_of_account_update-us",
|
||||
measure2.as_us() as usize,
|
||||
100000,
|
||||
100000
|
@@ -1,20 +1,20 @@
|
||||
/// Managing the Geyser plugins
|
||||
/// Managing the AccountsDb plugins
|
||||
use {
|
||||
libloading::{Library, Symbol},
|
||||
log::*,
|
||||
solana_geyser_plugin_interface::geyser_plugin_interface::GeyserPlugin,
|
||||
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::AccountsDbPlugin,
|
||||
std::error::Error,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct GeyserPluginManager {
|
||||
pub plugins: Vec<Box<dyn GeyserPlugin>>,
|
||||
pub struct AccountsDbPluginManager {
|
||||
pub plugins: Vec<Box<dyn AccountsDbPlugin>>,
|
||||
libs: Vec<Library>,
|
||||
}
|
||||
|
||||
impl GeyserPluginManager {
|
||||
impl AccountsDbPluginManager {
|
||||
pub fn new() -> Self {
|
||||
GeyserPluginManager {
|
||||
AccountsDbPluginManager {
|
||||
plugins: Vec::default(),
|
||||
libs: Vec::default(),
|
||||
}
|
||||
@@ -29,7 +29,7 @@ impl GeyserPluginManager {
|
||||
libpath: &str,
|
||||
config_file: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
type PluginConstructor = unsafe fn() -> *mut dyn GeyserPlugin;
|
||||
type PluginConstructor = unsafe fn() -> *mut dyn AccountsDbPlugin;
|
||||
let lib = Library::new(libpath)?;
|
||||
let constructor: Symbol<PluginConstructor> = lib.get(b"_create_plugin")?;
|
||||
let plugin_raw = constructor();
|
@@ -1,10 +1,11 @@
|
||||
use {
|
||||
crate::{
|
||||
accounts_update_notifier::AccountsUpdateNotifierImpl,
|
||||
accountsdb_plugin_manager::AccountsDbPluginManager,
|
||||
block_metadata_notifier::BlockMetadataNotifierImpl,
|
||||
block_metadata_notifier_interface::BlockMetadataNotifierLock,
|
||||
geyser_plugin_manager::GeyserPluginManager, slot_status_notifier::SlotStatusNotifierImpl,
|
||||
slot_status_observer::SlotStatusObserver, transaction_notifier::TransactionNotifierImpl,
|
||||
slot_status_notifier::SlotStatusNotifierImpl, slot_status_observer::SlotStatusObserver,
|
||||
transaction_notifier::TransactionNotifierImpl,
|
||||
},
|
||||
crossbeam_channel::Receiver,
|
||||
log::*,
|
||||
@@ -24,7 +25,7 @@ use {
|
||||
};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GeyserPluginServiceError {
|
||||
pub enum AccountsdbPluginServiceError {
|
||||
#[error("Cannot open the the plugin config file")]
|
||||
CannotOpenConfigFile(String),
|
||||
|
||||
@@ -44,41 +45,41 @@ pub enum GeyserPluginServiceError {
|
||||
PluginLoadError(String),
|
||||
}
|
||||
|
||||
/// The service managing the Geyser plugin workflow.
|
||||
pub struct GeyserPluginService {
|
||||
/// The service managing the AccountsDb plugin workflow.
|
||||
pub struct AccountsDbPluginService {
|
||||
slot_status_observer: Option<SlotStatusObserver>,
|
||||
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
|
||||
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
|
||||
accounts_update_notifier: Option<AccountsUpdateNotifier>,
|
||||
transaction_notifier: Option<TransactionNotifierLock>,
|
||||
block_metadata_notifier: Option<BlockMetadataNotifierLock>,
|
||||
}
|
||||
|
||||
impl GeyserPluginService {
|
||||
/// Creates and returns the GeyserPluginService.
|
||||
impl AccountsDbPluginService {
|
||||
/// Creates and returns the AccountsDbPluginService.
|
||||
/// # Arguments
|
||||
/// * `confirmed_bank_receiver` - The receiver for confirmed bank notification
|
||||
/// * `geyser_plugin_config_file` - The config file path for the plugin. The
|
||||
/// * `accountsdb_plugin_config_file` - The config file path for the plugin. The
|
||||
/// config file controls the plugin responsible
|
||||
/// for transporting the data to external data stores. It is defined in JSON format.
|
||||
/// The `libpath` field should be pointed to the full path of the dynamic shared library
|
||||
/// (.so file) to be loaded. The shared library must implement the `GeyserPlugin`
|
||||
/// (.so file) to be loaded. The shared library must implement the `AccountsDbPlugin`
|
||||
/// trait. And the shared library shall export a `C` function `_create_plugin` which
|
||||
/// shall create the implementation of `GeyserPlugin` and returns to the caller.
|
||||
/// shall create the implementation of `AccountsDbPlugin` and returns to the caller.
|
||||
/// The rest of the JSON fields' definition is up to to the concrete plugin implementation
|
||||
/// It is usually used to configure the connection information for the external data store.
|
||||
|
||||
pub fn new(
|
||||
confirmed_bank_receiver: Receiver<BankNotification>,
|
||||
geyser_plugin_config_files: &[PathBuf],
|
||||
) -> Result<Self, GeyserPluginServiceError> {
|
||||
accountsdb_plugin_config_files: &[PathBuf],
|
||||
) -> Result<Self, AccountsdbPluginServiceError> {
|
||||
info!(
|
||||
"Starting GeyserPluginService from config files: {:?}",
|
||||
geyser_plugin_config_files
|
||||
"Starting AccountsDbPluginService from config files: {:?}",
|
||||
accountsdb_plugin_config_files
|
||||
);
|
||||
let mut plugin_manager = GeyserPluginManager::new();
|
||||
let mut plugin_manager = AccountsDbPluginManager::new();
|
||||
|
||||
for geyser_plugin_config_file in geyser_plugin_config_files {
|
||||
Self::load_plugin(&mut plugin_manager, geyser_plugin_config_file)?;
|
||||
for accountsdb_plugin_config_file in accountsdb_plugin_config_files {
|
||||
Self::load_plugin(&mut plugin_manager, accountsdb_plugin_config_file)?;
|
||||
}
|
||||
let account_data_notifications_enabled =
|
||||
plugin_manager.account_data_notifications_enabled();
|
||||
@@ -122,8 +123,8 @@ impl GeyserPluginService {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
info!("Started GeyserPluginService");
|
||||
Ok(GeyserPluginService {
|
||||
info!("Started AccountsDbPluginService");
|
||||
Ok(AccountsDbPluginService {
|
||||
slot_status_observer,
|
||||
plugin_manager,
|
||||
accounts_update_notifier,
|
||||
@@ -133,64 +134,55 @@ impl GeyserPluginService {
|
||||
}
|
||||
|
||||
fn load_plugin(
|
||||
plugin_manager: &mut GeyserPluginManager,
|
||||
geyser_plugin_config_file: &Path,
|
||||
) -> Result<(), GeyserPluginServiceError> {
|
||||
let mut file = match File::open(geyser_plugin_config_file) {
|
||||
plugin_manager: &mut AccountsDbPluginManager,
|
||||
accountsdb_plugin_config_file: &Path,
|
||||
) -> Result<(), AccountsdbPluginServiceError> {
|
||||
let mut file = match File::open(accountsdb_plugin_config_file) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
return Err(GeyserPluginServiceError::CannotOpenConfigFile(format!(
|
||||
return Err(AccountsdbPluginServiceError::CannotOpenConfigFile(format!(
|
||||
"Failed to open the plugin config file {:?}, error: {:?}",
|
||||
geyser_plugin_config_file, err
|
||||
accountsdb_plugin_config_file, err
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let mut contents = String::new();
|
||||
if let Err(err) = file.read_to_string(&mut contents) {
|
||||
return Err(GeyserPluginServiceError::CannotReadConfigFile(format!(
|
||||
return Err(AccountsdbPluginServiceError::CannotReadConfigFile(format!(
|
||||
"Failed to read the plugin config file {:?}, error: {:?}",
|
||||
geyser_plugin_config_file, err
|
||||
accountsdb_plugin_config_file, err
|
||||
)));
|
||||
}
|
||||
|
||||
let result: serde_json::Value = match json5::from_str(&contents) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Err(GeyserPluginServiceError::InvalidConfigFileFormat(format!(
|
||||
"The config file {:?} is not in a valid Json5 format, error: {:?}",
|
||||
geyser_plugin_config_file, err
|
||||
)));
|
||||
return Err(AccountsdbPluginServiceError::InvalidConfigFileFormat(
|
||||
format!(
|
||||
"The config file {:?} is not in a valid Json5 format, error: {:?}",
|
||||
accountsdb_plugin_config_file, err
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let libpath = result["libpath"]
|
||||
.as_str()
|
||||
.ok_or(GeyserPluginServiceError::LibPathNotSet)?;
|
||||
let mut libpath = PathBuf::from(libpath);
|
||||
if libpath.is_relative() {
|
||||
let config_dir = geyser_plugin_config_file.parent().ok_or_else(|| {
|
||||
GeyserPluginServiceError::CannotOpenConfigFile(format!(
|
||||
"Failed to resolve parent of {:?}",
|
||||
geyser_plugin_config_file,
|
||||
))
|
||||
})?;
|
||||
libpath = config_dir.join(libpath);
|
||||
}
|
||||
|
||||
let config_file = geyser_plugin_config_file
|
||||
.ok_or(AccountsdbPluginServiceError::LibPathNotSet)?;
|
||||
let config_file = accountsdb_plugin_config_file
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.ok_or(GeyserPluginServiceError::InvalidPluginPath)?;
|
||||
.ok_or(AccountsdbPluginServiceError::InvalidPluginPath)?;
|
||||
|
||||
unsafe {
|
||||
let result = plugin_manager.load_plugin(libpath.to_str().unwrap(), config_file);
|
||||
let result = plugin_manager.load_plugin(libpath, config_file);
|
||||
if let Err(err) = result {
|
||||
let msg = format!(
|
||||
"Failed to load the plugin library: {:?}, error: {:?}",
|
||||
libpath, err
|
||||
);
|
||||
return Err(GeyserPluginServiceError::PluginLoadError(msg));
|
||||
return Err(AccountsdbPluginServiceError::PluginLoadError(msg));
|
||||
}
|
||||
}
|
||||
Ok(())
|
@@ -1,10 +1,10 @@
|
||||
use {
|
||||
crate::{
|
||||
accountsdb_plugin_manager::AccountsDbPluginManager,
|
||||
block_metadata_notifier_interface::BlockMetadataNotifier,
|
||||
geyser_plugin_manager::GeyserPluginManager,
|
||||
},
|
||||
log::*,
|
||||
solana_geyser_plugin_interface::geyser_plugin_interface::{
|
||||
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::{
|
||||
ReplicaBlockInfo, ReplicaBlockInfoVersions,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
@@ -16,7 +16,7 @@ use {
|
||||
};
|
||||
|
||||
pub(crate) struct BlockMetadataNotifierImpl {
|
||||
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
|
||||
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
|
||||
}
|
||||
|
||||
impl BlockMetadataNotifier for BlockMetadataNotifierImpl {
|
||||
@@ -36,7 +36,7 @@ impl BlockMetadataNotifier for BlockMetadataNotifierImpl {
|
||||
let rewards = Self::build_rewards(rewards);
|
||||
|
||||
for plugin in plugin_manager.plugins.iter_mut() {
|
||||
let mut measure = Measure::start("geyser-plugin-update-slot");
|
||||
let mut measure = Measure::start("accountsdb-plugin-update-slot");
|
||||
let block_info =
|
||||
Self::build_replica_block_info(slot, blockhash, &rewards, block_time, block_height);
|
||||
let block_info = ReplicaBlockInfoVersions::V0_0_1(&block_info);
|
||||
@@ -59,7 +59,7 @@ impl BlockMetadataNotifier for BlockMetadataNotifierImpl {
|
||||
}
|
||||
measure.stop();
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-update-block-metadata-us",
|
||||
"accountsdb-plugin-update-block-metadata-us",
|
||||
measure.as_us() as usize,
|
||||
1000,
|
||||
1000
|
||||
@@ -99,7 +99,7 @@ impl BlockMetadataNotifierImpl {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
|
||||
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
|
||||
Self { plugin_manager }
|
||||
}
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
pub mod accounts_update_notifier;
|
||||
pub mod accountsdb_plugin_manager;
|
||||
pub mod accountsdb_plugin_service;
|
||||
pub mod block_metadata_notifier;
|
||||
pub mod block_metadata_notifier_interface;
|
||||
pub mod geyser_plugin_manager;
|
||||
pub mod geyser_plugin_service;
|
||||
pub mod slot_status_notifier;
|
||||
pub mod slot_status_observer;
|
||||
pub mod transaction_notifier;
|
@@ -1,7 +1,7 @@
|
||||
use {
|
||||
crate::geyser_plugin_manager::GeyserPluginManager,
|
||||
crate::accountsdb_plugin_manager::AccountsDbPluginManager,
|
||||
log::*,
|
||||
solana_geyser_plugin_interface::geyser_plugin_interface::SlotStatus,
|
||||
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::SlotStatus,
|
||||
solana_measure::measure::Measure,
|
||||
solana_metrics::*,
|
||||
solana_sdk::clock::Slot,
|
||||
@@ -22,7 +22,7 @@ pub trait SlotStatusNotifierInterface {
|
||||
pub type SlotStatusNotifier = Arc<RwLock<dyn SlotStatusNotifierInterface + Sync + Send>>;
|
||||
|
||||
pub struct SlotStatusNotifierImpl {
|
||||
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
|
||||
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
|
||||
}
|
||||
|
||||
impl SlotStatusNotifierInterface for SlotStatusNotifierImpl {
|
||||
@@ -40,7 +40,7 @@ impl SlotStatusNotifierInterface for SlotStatusNotifierImpl {
|
||||
}
|
||||
|
||||
impl SlotStatusNotifierImpl {
|
||||
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
|
||||
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
|
||||
Self { plugin_manager }
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ impl SlotStatusNotifierImpl {
|
||||
}
|
||||
|
||||
for plugin in plugin_manager.plugins.iter_mut() {
|
||||
let mut measure = Measure::start("geyser-plugin-update-slot");
|
||||
match plugin.update_slot_status(slot, parent, slot_status) {
|
||||
let mut measure = Measure::start("accountsdb-plugin-update-slot");
|
||||
match plugin.update_slot_status(slot, parent, slot_status.clone()) {
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to update slot status at slot {}, error: {} to plugin {}",
|
||||
@@ -71,7 +71,7 @@ impl SlotStatusNotifierImpl {
|
||||
}
|
||||
measure.stop();
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-update-slot-us",
|
||||
"accountsdb-plugin-update-slot-us",
|
||||
measure.as_us() as usize,
|
||||
1000,
|
||||
1000
|
@@ -1,8 +1,8 @@
|
||||
/// Module responsible for notifying plugins of transactions
|
||||
use {
|
||||
crate::geyser_plugin_manager::GeyserPluginManager,
|
||||
crate::accountsdb_plugin_manager::AccountsDbPluginManager,
|
||||
log::*,
|
||||
solana_geyser_plugin_interface::geyser_plugin_interface::{
|
||||
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::{
|
||||
ReplicaTransactionInfo, ReplicaTransactionInfoVersions,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
@@ -16,9 +16,9 @@ use {
|
||||
/// This implementation of TransactionNotifier is passed to the rpc's TransactionStatusService
|
||||
/// at the validator startup. TransactionStatusService invokes the notify_transaction method
|
||||
/// for new transactions. The implementation in turn invokes the notify_transaction of each
|
||||
/// plugin enabled with transaction notification managed by the GeyserPluginManager.
|
||||
/// plugin enabled with transaction notification managed by the AccountsDbPluginManager.
|
||||
pub(crate) struct TransactionNotifierImpl {
|
||||
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
|
||||
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
|
||||
}
|
||||
|
||||
impl TransactionNotifier for TransactionNotifierImpl {
|
||||
@@ -29,7 +29,7 @@ impl TransactionNotifier for TransactionNotifierImpl {
|
||||
transaction_status_meta: &TransactionStatusMeta,
|
||||
transaction: &SanitizedTransaction,
|
||||
) {
|
||||
let mut measure = Measure::start("geyser-plugin-notify_plugins_of_transaction_info");
|
||||
let mut measure = Measure::start("accountsdb-plugin-notify_plugins_of_transaction_info");
|
||||
let transaction_log_info =
|
||||
Self::build_replica_transaction_info(signature, transaction_status_meta, transaction);
|
||||
|
||||
@@ -64,7 +64,7 @@ impl TransactionNotifier for TransactionNotifierImpl {
|
||||
}
|
||||
measure.stop();
|
||||
inc_new_counter_debug!(
|
||||
"geyser-plugin-notify_plugins_of_transaction_info-us",
|
||||
"accountsdb-plugin-notify_plugins_of_transaction_info-us",
|
||||
measure.as_us() as usize,
|
||||
10000,
|
||||
10000
|
||||
@@ -73,7 +73,7 @@ impl TransactionNotifier for TransactionNotifierImpl {
|
||||
}
|
||||
|
||||
impl TransactionNotifierImpl {
|
||||
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
|
||||
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
|
||||
Self { plugin_manager }
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -14,17 +14,17 @@ crossbeam-channel = "0.5"
|
||||
log = "0.4.14"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.1"
|
||||
solana-core = { path = "../core", version = "=1.10.5" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.5" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-perf = { path = "../perf", version = "=1.10.5" }
|
||||
solana-poh = { path = "../poh", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
solana-core = { path = "../core", version = "=1.10.0" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.0" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.10.0" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-perf = { path = "../perf", version = "=1.10.0" }
|
||||
solana-poh = { path = "../poh", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -11,7 +11,6 @@ use {
|
||||
blockstore::Blockstore,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
get_tmp_ledger_path,
|
||||
leader_schedule_cache::LeaderScheduleCache,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
solana_perf::packet::to_packet_batches,
|
||||
@@ -175,11 +174,6 @@ fn main() {
|
||||
let mut bank_forks = BankForks::new(bank0);
|
||||
let mut bank = bank_forks.working_bank();
|
||||
|
||||
// set cost tracker limits to MAX so it will not filter out TXs
|
||||
bank.write_cost_tracker()
|
||||
.unwrap()
|
||||
.set_limits(std::u64::MAX, std::u64::MAX, std::u64::MAX);
|
||||
|
||||
info!("threads: {} txs: {}", num_threads, total_num_transactions);
|
||||
|
||||
let same_payer = matches.is_present("same_payer");
|
||||
@@ -224,13 +218,8 @@ fn main() {
|
||||
let blockstore = Arc::new(
|
||||
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
||||
);
|
||||
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
||||
let (exit, poh_recorder, poh_service, signal_receiver) = create_test_recorder(
|
||||
&bank,
|
||||
&blockstore,
|
||||
None,
|
||||
Some(leader_schedule_cache.clone()),
|
||||
);
|
||||
let (exit, poh_recorder, poh_service, signal_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
Node::new_localhost().info,
|
||||
Arc::new(Keypair::new()),
|
||||
@@ -340,17 +329,9 @@ fn main() {
|
||||
bank = bank_forks.working_bank();
|
||||
insert_time.stop();
|
||||
|
||||
// set cost tracker limits to MAX so it will not filter out TXs
|
||||
bank.write_cost_tracker().unwrap().set_limits(
|
||||
std::u64::MAX,
|
||||
std::u64::MAX,
|
||||
std::u64::MAX,
|
||||
);
|
||||
|
||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||
assert!(poh_recorder.lock().unwrap().bank().is_some());
|
||||
if bank.slot() > 32 {
|
||||
leader_schedule_cache.set_root(&bank);
|
||||
bank_forks.set_root(root, &AbsRequestSender::default(), None);
|
||||
root += 1;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-client"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,19 +10,19 @@ documentation = "https://docs.rs/solana-banks-client"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
borsh = "0.9.3"
|
||||
borsh = "0.9.1"
|
||||
futures = "0.3"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.10.5" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.10.0" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
tarpc = { version = "0.27.2", features = ["full"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.10.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -5,10 +5,8 @@
|
||||
//! but they are undocumented, may change over time, and are generally more
|
||||
//! cumbersome to use.
|
||||
|
||||
pub use {
|
||||
crate::error::BanksClientError,
|
||||
solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus},
|
||||
};
|
||||
pub use crate::error::BanksClientError;
|
||||
pub use solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus};
|
||||
use {
|
||||
borsh::BorshDeserialize,
|
||||
futures::{future::join_all, Future, FutureExt, TryFutureExt},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-interface"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,8 +10,8 @@ documentation = "https://docs.rs/solana-banks-interface"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
serde = { version = "1.0.134", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
tarpc = { version = "0.27.2", features = ["full"] }
|
||||
|
||||
[lib]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-server"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -13,10 +13,10 @@ edition = "2021"
|
||||
bincode = "1.3.3"
|
||||
crossbeam-channel = "0.5"
|
||||
futures = "0.3"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.5" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.0" }
|
||||
tarpc = { version = "0.27.2", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
@@ -24,7 +24,7 @@ use {
|
||||
transaction::{self, SanitizedTransaction, Transaction},
|
||||
},
|
||||
solana_send_transaction_service::{
|
||||
send_transaction_service::{SendTransactionService, TransactionInfo, DEFAULT_TPU_USE_QUIC},
|
||||
send_transaction_service::{SendTransactionService, TransactionInfo},
|
||||
tpu_info::NullTpuInfo,
|
||||
},
|
||||
std::{
|
||||
@@ -399,7 +399,6 @@ pub async fn start_tcp_server(
|
||||
receiver,
|
||||
5_000,
|
||||
0,
|
||||
DEFAULT_TPU_USE_QUIC,
|
||||
);
|
||||
|
||||
let server = BanksServer::new(
|
||||
|
@@ -2,18 +2,18 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.5"
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
clap = "2.33.1"
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use {
|
||||
clap::{crate_description, crate_name, value_t, App, Arg},
|
||||
clap::{crate_description, crate_name, App, Arg},
|
||||
crossbeam_channel::unbounded,
|
||||
solana_streamer::{
|
||||
packet::{Packet, PacketBatch, PacketBatchRecycler, PACKET_DATA_SIZE},
|
||||
@@ -67,22 +67,13 @@ fn main() -> Result<()> {
|
||||
.takes_value(true)
|
||||
.help("Use NUM receive sockets"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("num-producers")
|
||||
.long("num-producers")
|
||||
.value_name("NUM")
|
||||
.takes_value(true)
|
||||
.help("Use this many producer threads."),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
if let Some(n) = matches.value_of("num-recv-sockets") {
|
||||
num_sockets = max(num_sockets, n.to_string().parse().expect("integer"));
|
||||
}
|
||||
|
||||
let num_producers = value_t!(matches, "num_producers", u64).unwrap_or(4);
|
||||
|
||||
let port = 0;
|
||||
let mut port = 0;
|
||||
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||
let mut addr = SocketAddr::new(ip_addr, 0);
|
||||
|
||||
@@ -91,16 +82,13 @@ fn main() -> Result<()> {
|
||||
let mut read_channels = Vec::new();
|
||||
let mut read_threads = Vec::new();
|
||||
let recycler = PacketBatchRecycler::default();
|
||||
let (_port, read_sockets) = solana_net_utils::multi_bind_in_range(
|
||||
ip_addr,
|
||||
(port, port + num_sockets as u16),
|
||||
num_sockets,
|
||||
)
|
||||
.unwrap();
|
||||
for read in read_sockets {
|
||||
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();
|
||||
|
||||
addr = read.local_addr().unwrap();
|
||||
port = addr.port();
|
||||
|
||||
let (s_reader, r_reader) = unbounded();
|
||||
read_channels.push(r_reader);
|
||||
read_threads.push(receiver(
|
||||
@@ -114,10 +102,9 @@ fn main() -> Result<()> {
|
||||
));
|
||||
}
|
||||
|
||||
let producer_threads: Vec<_> = (0..num_producers)
|
||||
.into_iter()
|
||||
.map(|_| producer(&addr, exit.clone()))
|
||||
.collect();
|
||||
let t_producer1 = producer(&addr, exit.clone());
|
||||
let t_producer2 = producer(&addr, exit.clone());
|
||||
let t_producer3 = producer(&addr, exit.clone());
|
||||
|
||||
let rvs = Arc::new(AtomicUsize::new(0));
|
||||
let sink_threads: Vec<_> = read_channels
|
||||
@@ -137,9 +124,9 @@ fn main() -> Result<()> {
|
||||
for t_reader in read_threads {
|
||||
t_reader.join()?;
|
||||
}
|
||||
for t_producer in producer_threads {
|
||||
t_producer.join()?;
|
||||
}
|
||||
t_producer1.join()?;
|
||||
t_producer2.join()?;
|
||||
t_producer3.join()?;
|
||||
for t_sink in sink_threads {
|
||||
t_sink.join()?;
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,25 +13,25 @@ clap = "2.33.1"
|
||||
crossbeam-channel = "0.5"
|
||||
log = "0.4.14"
|
||||
rayon = "1.5.1"
|
||||
serde_json = "1.0.79"
|
||||
serde_json = "1.0.78"
|
||||
serde_yaml = "0.8.23"
|
||||
solana-client = { path = "../client", version = "=1.10.5" }
|
||||
solana-core = { path = "../core", version = "=1.10.5" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.5" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.10.5" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
solana-core = { path = "../core", version = "=1.10.0" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.10.0" }
|
||||
solana-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.0" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.0" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.6.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.10.5" }
|
||||
serial_test = "0.5.1"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -110,7 +110,7 @@ fn generate_chunked_transfers(
|
||||
shared_txs: &SharedTransactions,
|
||||
shared_tx_active_thread_count: Arc<AtomicIsize>,
|
||||
source_keypair_chunks: Vec<Vec<&Keypair>>,
|
||||
dest_keypair_chunks: &mut [VecDeque<&Keypair>],
|
||||
dest_keypair_chunks: &mut Vec<VecDeque<&Keypair>>,
|
||||
threads: usize,
|
||||
duration: Duration,
|
||||
sustained: bool,
|
||||
@@ -475,7 +475,6 @@ fn do_tx_transfers<T: Client>(
|
||||
let tx_len = txs0.len();
|
||||
let transfer_start = Instant::now();
|
||||
let mut old_transactions = false;
|
||||
let mut transactions = Vec::<_>::new();
|
||||
for tx in txs0 {
|
||||
let now = timestamp();
|
||||
// Transactions that are too old will be rejected by the cluster Don't bother
|
||||
@@ -484,13 +483,10 @@ fn do_tx_transfers<T: Client>(
|
||||
old_transactions = true;
|
||||
continue;
|
||||
}
|
||||
transactions.push(tx.0);
|
||||
client
|
||||
.async_send_transaction(tx.0)
|
||||
.expect("async_send_transaction in do_tx_transfers");
|
||||
}
|
||||
|
||||
if let Err(error) = client.async_send_batch(transactions) {
|
||||
warn!("send_batch_sync in do_tx_transfers failed: {}", error);
|
||||
}
|
||||
|
||||
if old_transactions {
|
||||
let mut shared_txs_wl = shared_txs.write().expect("write lock in do_tx_transfers");
|
||||
shared_txs_wl.clear();
|
||||
|
@@ -21,7 +21,7 @@ pub const NUM_SIGNATURES_FOR_TXS: u64 = 100_000 * 60 * 60 * 24 * 7;
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup_with_default("solana=info");
|
||||
solana_metrics::set_panic_hook("bench-tps", /*version:*/ None);
|
||||
solana_metrics::set_panic_hook("bench-tps");
|
||||
|
||||
let matches = cli::build_args(solana_version::version!()).get_matches();
|
||||
let cli_config = cli::extract_args(&matches);
|
||||
|
@@ -29,7 +29,7 @@ fn test_bench_tps_local_cluster(config: Config) {
|
||||
node_stakes: vec![999_990; NUM_NODES],
|
||||
cluster_lamports: 200_000_000,
|
||||
validator_configs: make_identical_validator_configs(
|
||||
&ValidatorConfig::default_for_test(),
|
||||
&ValidatorConfig::default(),
|
||||
NUM_NODES,
|
||||
),
|
||||
native_instruction_processors,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-bloom"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana bloom filter"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,14 +12,14 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bv = { version = "0.11.1", features = ["serde"] }
|
||||
fnv = "1.0.7"
|
||||
log = "0.4.14"
|
||||
rand = "0.7.0"
|
||||
serde = { version = "1.0.134", features = ["rc"] }
|
||||
rayon = "1.5.1"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.103"
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.5" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.0" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
log = "0.4.14"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-bucket-map"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "solana-bucket-map"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-bucket-map"
|
||||
@@ -11,18 +11,18 @@ license = "Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
memmap2 = "0.5.2"
|
||||
log = { version = "0.4.11" }
|
||||
memmap2 = "0.5.3"
|
||||
modular-bitfield = "0.11.2"
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
rand = "0.7.0"
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
tempfile = "3.3.0"
|
||||
modular-bitfield = "0.11.2"
|
||||
|
||||
[dev-dependencies]
|
||||
fs_extra = "1.2.0"
|
||||
rayon = "1.5.0"
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,365 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds a buildkite pipeline based on the environment variables
|
||||
#
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")"/..
|
||||
|
||||
output_file=${1:-/dev/stderr}
|
||||
|
||||
if [[ -n $CI_PULL_REQUEST ]]; then
|
||||
IFS=':' read -ra affected_files <<< "$(buildkite-agent meta-data get affected_files)"
|
||||
if [[ ${#affected_files[*]} -eq 0 ]]; then
|
||||
echo "Unable to determine the files affected by this PR"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
affected_files=()
|
||||
fi
|
||||
|
||||
annotate() {
|
||||
if [[ -n $BUILDKITE ]]; then
|
||||
buildkite-agent annotate "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if a CI pull request affects one or more path patterns. Each
|
||||
# pattern argument is checked in series. If one of them found to be affected,
|
||||
# return immediately as such.
|
||||
#
|
||||
# Bash regular expressions are permitted in the pattern:
|
||||
# affects .rs$ -- any file or directory ending in .rs
|
||||
# affects .rs -- also matches foo.rs.bar
|
||||
# affects ^snap/ -- anything under the snap/ subdirectory
|
||||
# affects snap/ -- also matches foo/snap/
|
||||
# Any pattern starting with the ! character will be negated:
|
||||
# affects !^docs/ -- anything *not* under the docs/ subdirectory
|
||||
#
|
||||
affects() {
|
||||
if [[ -z $CI_PULL_REQUEST ]]; then
|
||||
# affected_files metadata is not currently available for non-PR builds so assume
|
||||
# the worse (affected)
|
||||
return 0
|
||||
fi
|
||||
# Assume everyting needs to be tested when any Dockerfile changes
|
||||
for pattern in ^ci/docker-rust/Dockerfile ^ci/docker-rust-nightly/Dockerfile "$@"; do
|
||||
if [[ ${pattern:0:1} = "!" ]]; then
|
||||
for file in "${affected_files[@]}"; do
|
||||
if [[ ! $file =~ ${pattern:1} ]]; then
|
||||
return 0 # affected
|
||||
fi
|
||||
done
|
||||
else
|
||||
for file in "${affected_files[@]}"; do
|
||||
if [[ $file =~ $pattern ]]; then
|
||||
return 0 # affected
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
return 1 # not affected
|
||||
}
|
||||
|
||||
|
||||
# Checks if a CI pull request affects anything other than the provided path patterns
|
||||
#
|
||||
# Syntax is the same as `affects()` except that the negation prefix is not
|
||||
# supported
|
||||
#
|
||||
affects_other_than() {
|
||||
if [[ -z $CI_PULL_REQUEST ]]; then
|
||||
# affected_files metadata is not currently available for non-PR builds so assume
|
||||
# the worse (affected)
|
||||
return 0
|
||||
fi
|
||||
|
||||
for file in "${affected_files[@]}"; do
|
||||
declare matched=false
|
||||
for pattern in "$@"; do
|
||||
if [[ $file =~ $pattern ]]; then
|
||||
matched=true
|
||||
fi
|
||||
done
|
||||
if ! $matched; then
|
||||
return 0 # affected
|
||||
fi
|
||||
done
|
||||
|
||||
return 1 # not affected
|
||||
}
|
||||
|
||||
|
||||
start_pipeline() {
|
||||
echo "# $*" > "$output_file"
|
||||
echo "steps:" >> "$output_file"
|
||||
}
|
||||
|
||||
command_step() {
|
||||
cat >> "$output_file" <<EOF
|
||||
- name: "$1"
|
||||
command: "$2"
|
||||
timeout_in_minutes: $3
|
||||
artifact_paths: "log-*.txt"
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
trigger_secondary_step() {
|
||||
cat >> "$output_file" <<"EOF"
|
||||
- trigger: "solana-secondary"
|
||||
branches: "!pull/*"
|
||||
async: true
|
||||
build:
|
||||
message: "${BUILDKITE_MESSAGE}"
|
||||
commit: "${BUILDKITE_COMMIT}"
|
||||
branch: "${BUILDKITE_BRANCH}"
|
||||
env:
|
||||
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"
|
||||
EOF
|
||||
}
|
||||
|
||||
wait_step() {
|
||||
echo " - wait" >> "$output_file"
|
||||
}
|
||||
|
||||
all_test_steps() {
|
||||
command_step checks ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-checks.sh" 20
|
||||
wait_step
|
||||
|
||||
# Coverage...
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-coverage.sh \
|
||||
^scripts/coverage.sh \
|
||||
; then
|
||||
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 50
|
||||
wait_step
|
||||
else
|
||||
annotate --style info --context test-coverage \
|
||||
"Coverage skipped as no .rs files were modified"
|
||||
fi
|
||||
# Coverage in disk...
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-coverage.sh \
|
||||
^scripts/coverage-in-disk.sh \
|
||||
; then
|
||||
command_step coverage-in-disk ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 50
|
||||
wait_step
|
||||
else
|
||||
annotate --style info --context test-coverage \
|
||||
"Coverage skipped as no .rs files were modified"
|
||||
fi
|
||||
# Full test suite
|
||||
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 70
|
||||
wait_step
|
||||
|
||||
# BPF test suite
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-stable-bpf.sh \
|
||||
^ci/test-stable.sh \
|
||||
^ci/test-local-cluster.sh \
|
||||
^core/build.rs \
|
||||
^fetch-perf-libs.sh \
|
||||
^programs/ \
|
||||
^sdk/ \
|
||||
; then
|
||||
cat >> "$output_file" <<"EOF"
|
||||
- command: "ci/test-stable-bpf.sh"
|
||||
name: "stable-bpf"
|
||||
timeout_in_minutes: 20
|
||||
artifact_paths: "bpf-dumps.tar.bz2"
|
||||
agents:
|
||||
- "queue=default"
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
"Stable-BPF skipped as no relevant files were modified"
|
||||
fi
|
||||
|
||||
# Perf test suite
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-stable-perf.sh \
|
||||
^ci/test-stable.sh \
|
||||
^ci/test-local-cluster.sh \
|
||||
^core/build.rs \
|
||||
^fetch-perf-libs.sh \
|
||||
^programs/ \
|
||||
^sdk/ \
|
||||
; then
|
||||
cat >> "$output_file" <<"EOF"
|
||||
- command: "ci/test-stable-perf.sh"
|
||||
name: "stable-perf"
|
||||
timeout_in_minutes: 20
|
||||
artifact_paths: "log-*.txt"
|
||||
agents:
|
||||
- "queue=cuda"
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
"Stable-perf skipped as no relevant files were modified"
|
||||
fi
|
||||
|
||||
# Downstream backwards compatibility
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-stable-perf.sh \
|
||||
^ci/test-stable.sh \
|
||||
^ci/test-local-cluster.sh \
|
||||
^core/build.rs \
|
||||
^fetch-perf-libs.sh \
|
||||
^programs/ \
|
||||
^sdk/ \
|
||||
^scripts/build-downstream-projects.sh \
|
||||
; then
|
||||
cat >> "$output_file" <<"EOF"
|
||||
- command: "scripts/build-downstream-projects.sh"
|
||||
name: "downstream-projects"
|
||||
timeout_in_minutes: 30
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
"downstream-projects skipped as no relevant files were modified"
|
||||
fi
|
||||
|
||||
# Downstream Anchor projects backwards compatibility
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-stable-perf.sh \
|
||||
^ci/test-stable.sh \
|
||||
^ci/test-local-cluster.sh \
|
||||
^core/build.rs \
|
||||
^fetch-perf-libs.sh \
|
||||
^programs/ \
|
||||
^sdk/ \
|
||||
^scripts/build-downstream-anchor-projects.sh \
|
||||
; then
|
||||
cat >> "$output_file" <<"EOF"
|
||||
- command: "scripts/build-downstream-anchor-projects.sh"
|
||||
name: "downstream-anchor-projects"
|
||||
timeout_in_minutes: 10
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
"downstream-anchor-projects skipped as no relevant files were modified"
|
||||
fi
|
||||
|
||||
# Wasm support
|
||||
if affects \
|
||||
^ci/test-wasm.sh \
|
||||
^ci/test-stable.sh \
|
||||
^sdk/ \
|
||||
; then
|
||||
command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20
|
||||
else
|
||||
annotate --style info \
|
||||
"wasm skipped as no relevant files were modified"
|
||||
fi
|
||||
|
||||
# Benches...
|
||||
if affects \
|
||||
.rs$ \
|
||||
Cargo.lock$ \
|
||||
Cargo.toml$ \
|
||||
^ci/rust-version.sh \
|
||||
^ci/test-coverage.sh \
|
||||
^ci/test-bench.sh \
|
||||
; then
|
||||
command_step bench "ci/test-bench.sh" 30
|
||||
else
|
||||
annotate --style info --context test-bench \
|
||||
"Bench skipped as no .rs files were modified"
|
||||
fi
|
||||
|
||||
command_step "local-cluster" \
|
||||
". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-local-cluster.sh" \
|
||||
40
|
||||
|
||||
command_step "local-cluster-flakey" \
|
||||
". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-local-cluster-flakey.sh" \
|
||||
10
|
||||
|
||||
command_step "local-cluster-slow" \
|
||||
". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-local-cluster-slow.sh" \
|
||||
30
|
||||
}
|
||||
|
||||
pull_or_push_steps() {
|
||||
command_step sanity "ci/test-sanity.sh" 5
|
||||
wait_step
|
||||
|
||||
# Check for any .sh file changes
|
||||
if affects .sh$; then
|
||||
command_step shellcheck "ci/shellcheck.sh" 5
|
||||
wait_step
|
||||
fi
|
||||
|
||||
# Run the full test suite by default, skipping only if modifications are local
|
||||
# to some particular areas of the tree
|
||||
if affects_other_than ^.buildkite ^.mergify .md$ ^docs/ ^web3.js/ ^explorer/ ^.gitbook; then
|
||||
all_test_steps
|
||||
fi
|
||||
|
||||
# web3.js, explorer and docs changes run on Travis or Github actions...
|
||||
}
|
||||
|
||||
|
||||
if [[ -n $BUILDKITE_TAG ]]; then
|
||||
start_pipeline "Tag pipeline for $BUILDKITE_TAG"
|
||||
|
||||
annotate --style info --context release-tag \
|
||||
"https://github.com/solana-labs/solana/releases/$BUILDKITE_TAG"
|
||||
|
||||
# Jump directly to the secondary build to publish release artifacts quickly
|
||||
trigger_secondary_step
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if [[ $BUILDKITE_BRANCH =~ ^pull ]]; then
|
||||
echo "+++ Affected files in this PR"
|
||||
for file in "${affected_files[@]}"; do
|
||||
echo "- $file"
|
||||
done
|
||||
|
||||
start_pipeline "Pull request pipeline for $BUILDKITE_BRANCH"
|
||||
|
||||
# Add helpful link back to the corresponding Github Pull Request
|
||||
annotate --style info --context pr-backlink \
|
||||
"Github Pull Request: https://github.com/solana-labs/solana/$BUILDKITE_BRANCH"
|
||||
|
||||
if [[ $GITHUB_USER = "dependabot[bot]" ]]; then
|
||||
command_step dependabot "ci/dependabot-pr.sh" 5
|
||||
wait_step
|
||||
fi
|
||||
pull_or_push_steps
|
||||
exit 0
|
||||
fi
|
||||
|
||||
start_pipeline "Push pipeline for ${BUILDKITE_BRANCH:-?unknown branch?}"
|
||||
pull_or_push_steps
|
||||
wait_step
|
||||
trigger_secondary_step
|
||||
exit 0
|
@@ -102,8 +102,6 @@ command_step() {
|
||||
command: "$2"
|
||||
timeout_in_minutes: $3
|
||||
artifact_paths: "log-*.txt"
|
||||
agents:
|
||||
- "queue=solana"
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -139,7 +137,7 @@ all_test_steps() {
|
||||
^ci/test-coverage.sh \
|
||||
^scripts/coverage.sh \
|
||||
; then
|
||||
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 50
|
||||
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 40
|
||||
wait_step
|
||||
else
|
||||
annotate --style info --context test-coverage \
|
||||
@@ -147,7 +145,7 @@ all_test_steps() {
|
||||
fi
|
||||
|
||||
# Full test suite
|
||||
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 70
|
||||
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 60
|
||||
wait_step
|
||||
|
||||
# BPF test suite
|
||||
@@ -170,7 +168,7 @@ all_test_steps() {
|
||||
timeout_in_minutes: 20
|
||||
artifact_paths: "bpf-dumps.tar.bz2"
|
||||
agents:
|
||||
- "queue=solana"
|
||||
- "queue=default"
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
@@ -223,8 +221,6 @@ EOF
|
||||
- command: "scripts/build-downstream-projects.sh"
|
||||
name: "downstream-projects"
|
||||
timeout_in_minutes: 30
|
||||
agents:
|
||||
- "queue=solana"
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
@@ -250,8 +246,6 @@ EOF
|
||||
- command: "scripts/build-downstream-anchor-projects.sh"
|
||||
name: "downstream-anchor-projects"
|
||||
timeout_in_minutes: 10
|
||||
agents:
|
||||
- "queue=solana"
|
||||
EOF
|
||||
else
|
||||
annotate --style info \
|
||||
|
@@ -8,6 +8,11 @@ src_root="$(readlink -f "${here}/..")"
|
||||
cd "${src_root}"
|
||||
|
||||
cargo_audit_ignores=(
|
||||
# failure is officially deprecated/unmaintained
|
||||
#
|
||||
# Blocked on multiple upstream crates removing their `failure` dependency.
|
||||
--ignore RUSTSEC-2020-0036
|
||||
|
||||
# `net2` crate has been deprecated; use `socket2` instead
|
||||
#
|
||||
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
|
||||
@@ -25,10 +30,22 @@ cargo_audit_ignores=(
|
||||
|
||||
# generic-array: arr! macro erases lifetimes
|
||||
#
|
||||
# Blocked on new spl dependencies on solana-program v1.9
|
||||
# due to curve25519-dalek dependency
|
||||
# Blocked on libsecp256k1 releasing with upgraded dependencies
|
||||
# https://github.com/paritytech/libsecp256k1/issues/66
|
||||
--ignore RUSTSEC-2020-0146
|
||||
|
||||
# hyper: Lenient `hyper` header parsing of `Content-Length` could allow request smuggling
|
||||
#
|
||||
# Blocked on jsonrpc removing dependency on unmaintained `websocket`
|
||||
# https://github.com/paritytech/jsonrpc/issues/605
|
||||
--ignore RUSTSEC-2021-0078
|
||||
|
||||
# hyper: Integer overflow in `hyper`'s parsing of the `Transfer-Encoding` header leads to data loss
|
||||
#
|
||||
# Blocked on jsonrpc removing dependency on unmaintained `websocket`
|
||||
# https://github.com/paritytech/jsonrpc/issues/605
|
||||
--ignore RUSTSEC-2021-0079
|
||||
|
||||
# chrono: Potential segfault in `localtime_r` invocations
|
||||
#
|
||||
# Blocked due to no safe upgrade
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM solanalabs/rust:1.59.0
|
||||
FROM solanalabs/rust:1.58.1
|
||||
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.59.0
|
||||
FROM rust:1.58.1
|
||||
|
||||
# Add Google Protocol Buffers for Libra's metrics library.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
@@ -70,7 +70,7 @@ for Cargo_toml in $Cargo_tomls; do
|
||||
rm -rf crate-test
|
||||
"$cargo" stable init crate-test
|
||||
cd crate-test/
|
||||
echo "${crate_name} = \"=${expectedCrateVersion}\"" >> Cargo.toml
|
||||
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
|
||||
echo "[workspace]" >> Cargo.toml
|
||||
"$cargo" stable check
|
||||
) && really_uploaded=1
|
||||
|
@@ -150,7 +150,7 @@ elif [[ -n $BUILDKITE ]]; then
|
||||
cat > release.solana.com-install <<EOF
|
||||
SOLANA_RELEASE=$CHANNEL_OR_TAG
|
||||
SOLANA_INSTALL_INIT_ARGS=$CHANNEL_OR_TAG
|
||||
SOLANA_DOWNLOAD_ROOT=https://release.solana.com
|
||||
SOLANA_DOWNLOAD_ROOT=http://release.solana.com
|
||||
EOF
|
||||
cat install/solana-install-init.sh >> release.solana.com-install
|
||||
|
||||
|
@@ -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="--full-snapshot-interval-slots 200" timeout 120 ./scripts/run.sh &
|
||||
SOLANA_RUN_SH_VALIDATOR_ARGS="--snapshot-interval-slots 200" timeout 120 ./scripts/run.sh &
|
||||
pid=$!
|
||||
|
||||
attempts=20
|
||||
|
@@ -18,13 +18,13 @@
|
||||
if [[ -n $RUST_STABLE_VERSION ]]; then
|
||||
stable_version="$RUST_STABLE_VERSION"
|
||||
else
|
||||
stable_version=1.59.0
|
||||
stable_version=1.58.1
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2022-02-24
|
||||
nightly_version=2022-01-21
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Finds the version of sbf-tools used by this source tree.
|
||||
#
|
||||
# stdout of this script may be eval-ed.
|
||||
#
|
||||
|
||||
here="$(dirname "$0")"
|
||||
|
||||
SBF_TOOLS_VERSION=unknown
|
||||
|
||||
cargo_build_bpf_main="${here}/../sdk/cargo-build-bpf/src/main.rs"
|
||||
if [[ -f "${cargo_build_bpf_main}" ]]; then
|
||||
version=$(sed -e 's/^.*bpf_tools_version\s*=\s*"\(v[0-9.]\+\)".*/\1/;t;d' "${cargo_build_bpf_main}")
|
||||
if [[ ${version} != '' ]]; then
|
||||
SBF_TOOLS_VERSION="${version}"
|
||||
else
|
||||
echo '--- unable to parse SBF_TOOLS_VERSION'
|
||||
fi
|
||||
else
|
||||
echo "--- '${cargo_build_bpf_main}' not present"
|
||||
fi
|
||||
|
||||
echo SBF_TOOLS_VERSION="${SBF_TOOLS_VERSION}"
|
@@ -57,20 +57,32 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
exit "$check_status"
|
||||
fi
|
||||
|
||||
# Ensure nightly and --benches
|
||||
# Ensure nightly and --benches
|
||||
_ scripts/cargo-for-all-lock-files.sh nightly check --locked --all-targets
|
||||
else
|
||||
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
|
||||
fi
|
||||
|
||||
_ ci/order-crates-for-publishing.py
|
||||
_ ci/order-crates-for-publishing.py
|
||||
|
||||
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
|
||||
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
|
||||
_ scripts/cargo-for-all-lock-files.sh -- nightly clippy -Zunstable-options --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
|
||||
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
|
||||
|
||||
_ scripts/cargo-for-all-lock-files.sh -- nightly fmt --all -- --check
|
||||
_ "$cargo" stable fmt --all -- --check
|
||||
|
||||
_ ci/do-audit.sh
|
||||
_ ci/do-audit.sh
|
||||
|
||||
{
|
||||
cd programs/bpf
|
||||
for project in rust/*/ ; do
|
||||
echo "+++ do_bpf_checks $project"
|
||||
(
|
||||
cd "$project"
|
||||
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||
_ "$cargo" stable fmt -- --check
|
||||
)
|
||||
done
|
||||
}
|
||||
|
||||
echo --- ok
|
||||
|
@@ -21,16 +21,15 @@ export RUST_BACKTRACE=1
|
||||
export RUSTFLAGS="-D warnings"
|
||||
source scripts/ulimit-n.sh
|
||||
|
||||
# limit jobs to 4gb/thread
|
||||
JOBS=$(grep MemTotal /proc/meminfo | awk '{printf "%.0f", ($2 / (4 * 1024 * 1024))}')
|
||||
# Limit compiler jobs to reduce memory usage
|
||||
# on machines with 2gb/thread of memory
|
||||
NPROC=$(nproc)
|
||||
JOBS=$((JOBS>NPROC ? NPROC : JOBS))
|
||||
|
||||
NPROC=$((NPROC>14 ? 14 : NPROC))
|
||||
|
||||
echo "Executing $testName"
|
||||
case $testName in
|
||||
test-stable)
|
||||
_ "$cargo" stable test --jobs "$JOBS" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
||||
_ "$cargo" stable test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
||||
;;
|
||||
test-stable-bpf)
|
||||
# Clear the C dependency files, if dependency moves these files are not regenerated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,16 +10,16 @@ documentation = "https://docs.rs/solana-clap-utils"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
clap = "2.33.0"
|
||||
rpassword = "6.0"
|
||||
solana-perf = { path = "../perf", version = "=1.10.5" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.5", default-features = false }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
rpassword = "5.0"
|
||||
solana-perf = { path = "../perf", version = "=1.10.0" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.0", default-features = false}
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
thiserror = "1.0.30"
|
||||
tiny-bip39 = "0.8.2"
|
||||
uriparse = "0.6.3"
|
||||
url = "2.2.2"
|
||||
chrono = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3.0"
|
||||
|
@@ -17,7 +17,7 @@ use {
|
||||
},
|
||||
bip39::{Language, Mnemonic, Seed},
|
||||
clap::ArgMatches,
|
||||
rpassword::prompt_password,
|
||||
rpassword::prompt_password_stderr,
|
||||
solana_remote_wallet::{
|
||||
locator::{Locator as RemoteWalletLocator, LocatorError as RemoteWalletLocatorError},
|
||||
remote_keypair::generate_remote_keypair,
|
||||
@@ -945,9 +945,9 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
||||
|
||||
/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
|
||||
pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>> {
|
||||
let passphrase = prompt_password(prompt)?;
|
||||
let passphrase = prompt_password_stderr(prompt)?;
|
||||
if !passphrase.is_empty() {
|
||||
let confirmed = rpassword::prompt_password("Enter same passphrase again: ")?;
|
||||
let confirmed = rpassword::prompt_password_stderr("Enter same passphrase again: ")?;
|
||||
if confirmed != passphrase {
|
||||
return Err("Passphrases did not match".into());
|
||||
}
|
||||
@@ -1055,7 +1055,7 @@ pub fn keypair_from_seed_phrase(
|
||||
derivation_path: Option<DerivationPath>,
|
||||
legacy: bool,
|
||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||
let seed_phrase = prompt_password(&format!("[{}] seed phrase: ", keypair_name))?;
|
||||
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
||||
let seed_phrase = seed_phrase.trim();
|
||||
let passphrase_prompt = format!(
|
||||
"[{}] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: ",
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -12,13 +12,13 @@ documentation = "https://docs.rs/solana-cli-config"
|
||||
[dependencies]
|
||||
dirs-next = "2.0.0"
|
||||
lazy_static = "1.4.0"
|
||||
serde = "1.0.136"
|
||||
serde = "1.0.134"
|
||||
serde_derive = "1.0.103"
|
||||
serde_yaml = "0.8.23"
|
||||
url = "2.2.2"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.56"
|
||||
anyhow = "1.0.53"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -3,32 +3,29 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-cli-output"
|
||||
|
||||
[dependencies]
|
||||
Inflector = "0.11.4"
|
||||
base64 = "0.13.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.0"
|
||||
console = "0.15.0"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.16.2"
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.5" }
|
||||
solana-client = { path = "../client", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.5" }
|
||||
serde = "1.0.134"
|
||||
serde_json = "1.0.78"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
|
||||
solana-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ed25519-dalek = "=1.0.1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -28,7 +28,7 @@ use {
|
||||
signature::Signature,
|
||||
stake::state::{Authorized, Lockup},
|
||||
stake_history::StakeHistoryEntry,
|
||||
transaction::{Transaction, TransactionError, VersionedTransaction},
|
||||
transaction::{Transaction, TransactionError},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedTransaction, TransactionConfirmationStatus,
|
||||
@@ -46,8 +46,6 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
||||
static CROSS_MARK: Emoji = Emoji("❌ ", "");
|
||||
static WARNING: Emoji = Emoji("⚠️", "!");
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
@@ -393,19 +391,19 @@ impl fmt::Display for CliValidators {
|
||||
) -> fmt::Result {
|
||||
fn non_zero_or_dash(v: u64, max_v: u64) -> String {
|
||||
if v == 0 {
|
||||
" - ".into()
|
||||
"- ".into()
|
||||
} else if v == max_v {
|
||||
format!("{:>9} ( 0)", v)
|
||||
format!("{:>8} ( 0)", v)
|
||||
} else if v > max_v.saturating_sub(100) {
|
||||
format!("{:>9} ({:>3})", v, -(max_v.saturating_sub(v) as isize))
|
||||
format!("{:>8} ({:>3})", v, -(max_v.saturating_sub(v) as isize))
|
||||
} else {
|
||||
format!("{:>9} ", v)
|
||||
format!("{:>8} ", v)
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{} {:<44} {:<44} {:>3}% {:>14} {:>14} {:>7} {:>8} {:>7} {:>22} ({:.2}%)",
|
||||
"{} {:<44} {:<44} {:>3}% {:>14} {:>14} {:>7} {:>8} {:>7} {}",
|
||||
if validator.delinquent {
|
||||
WARNING.to_string()
|
||||
} else {
|
||||
@@ -419,19 +417,19 @@ impl fmt::Display for CliValidators {
|
||||
if let Some(skip_rate) = validator.skip_rate {
|
||||
format!("{:.2}%", skip_rate)
|
||||
} else {
|
||||
"- ".to_string()
|
||||
"- ".to_string()
|
||||
},
|
||||
validator.epoch_credits,
|
||||
validator.version,
|
||||
build_balance_message_with_config(
|
||||
validator.activated_stake,
|
||||
&BuildBalanceMessageConfig {
|
||||
use_lamports_unit,
|
||||
trim_trailing_zeros: false,
|
||||
..BuildBalanceMessageConfig::default()
|
||||
}
|
||||
),
|
||||
100. * validator.activated_stake as f64 / total_active_stake as f64,
|
||||
if validator.activated_stake > 0 {
|
||||
format!(
|
||||
"{} ({:.2}%)",
|
||||
build_balance_message(validator.activated_stake, use_lamports_unit, true),
|
||||
100. * validator.activated_stake as f64 / total_active_stake as f64,
|
||||
)
|
||||
} else {
|
||||
"-".into()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -441,13 +439,13 @@ impl fmt::Display for CliValidators {
|
||||
0
|
||||
};
|
||||
let header = style(format!(
|
||||
"{:padding$} {:<44} {:<38} {} {} {} {} {} {} {}",
|
||||
"{:padding$} {:<44} {:<38} {} {} {} {} {} {} {}",
|
||||
" ",
|
||||
"Identity",
|
||||
"Vote Account",
|
||||
"Commission",
|
||||
"Last Vote ",
|
||||
"Root Slot ",
|
||||
"Last Vote ",
|
||||
"Root Slot ",
|
||||
"Skip Rate",
|
||||
"Credits",
|
||||
"Version",
|
||||
@@ -2218,7 +2216,7 @@ pub enum CliSignatureVerificationStatus {
|
||||
}
|
||||
|
||||
impl CliSignatureVerificationStatus {
|
||||
pub fn verify_transaction(tx: &VersionedTransaction) -> Vec<Self> {
|
||||
pub fn verify_transaction(tx: &Transaction) -> Vec<Self> {
|
||||
tx.verify_with_results()
|
||||
.iter()
|
||||
.zip(&tx.signatures)
|
||||
@@ -2335,7 +2333,7 @@ impl fmt::Display for CliBlock {
|
||||
writeln_transaction(
|
||||
f,
|
||||
&transaction_with_meta.transaction.decode().unwrap(),
|
||||
transaction_with_meta.meta.as_ref(),
|
||||
&transaction_with_meta.meta,
|
||||
" ",
|
||||
None,
|
||||
None,
|
||||
@@ -2354,7 +2352,7 @@ pub struct CliTransaction {
|
||||
#[serde(skip_serializing)]
|
||||
pub slot: Option<Slot>,
|
||||
#[serde(skip_serializing)]
|
||||
pub decoded_transaction: VersionedTransaction,
|
||||
pub decoded_transaction: Transaction,
|
||||
#[serde(skip_serializing)]
|
||||
pub prefix: String,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
@@ -2369,7 +2367,7 @@ impl fmt::Display for CliTransaction {
|
||||
writeln_transaction(
|
||||
f,
|
||||
&self.decoded_transaction,
|
||||
self.meta.as_ref(),
|
||||
&self.meta,
|
||||
&self.prefix,
|
||||
if !self.sigverify_status.is_empty() {
|
||||
Some(&self.sigverify_status)
|
||||
@@ -2451,8 +2449,6 @@ pub struct CliGossipNode {
|
||||
pub rpc_host: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub feature_set: Option<u32>,
|
||||
}
|
||||
|
||||
impl CliGossipNode {
|
||||
@@ -2465,7 +2461,6 @@ impl CliGossipNode {
|
||||
tpu_port: info.tpu.map(|addr| addr.port()),
|
||||
rpc_host: info.rpc.map(|addr| addr.to_string()),
|
||||
version: info.version,
|
||||
feature_set: info.feature_set,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2491,7 +2486,7 @@ impl fmt::Display for CliGossipNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {:8}| {}",
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
unwrap_to_string_or_none(self.ip_address.as_ref()),
|
||||
self.identity_label
|
||||
.as_ref()
|
||||
@@ -2500,7 +2495,6 @@ impl fmt::Display for CliGossipNode {
|
||||
unwrap_to_string_or_none(self.tpu_port.as_ref()),
|
||||
unwrap_to_string_or_none(self.rpc_host.as_ref()),
|
||||
unwrap_to_string_or_default(self.version.as_ref(), "unknown"),
|
||||
unwrap_to_string_or_default(self.feature_set.as_ref(), "unknown"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2515,10 +2509,10 @@ impl fmt::Display for CliGossipNodes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"IP Address | Identity \
|
||||
| Gossip | TPU | RPC Address | Version | Feature Set\n\
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+---------+----------------",
|
||||
--------+-------+-----------------------+----------------",
|
||||
)?;
|
||||
for node in self.0.iter() {
|
||||
writeln!(f, "{}", node)?;
|
||||
@@ -2530,172 +2524,6 @@ impl fmt::Display for CliGossipNodes {
|
||||
impl QuietDisplay for CliGossipNodes {}
|
||||
impl VerboseDisplay for CliGossipNodes {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliPing {
|
||||
pub source_pubkey: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fixed_blockhash: Option<String>,
|
||||
#[serde(skip_serializing)]
|
||||
pub blockhash_from_cluster: bool,
|
||||
pub pings: Vec<CliPingData>,
|
||||
pub transaction_stats: CliPingTxStats,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub confirmation_stats: Option<CliPingConfirmationStats>,
|
||||
}
|
||||
|
||||
impl fmt::Display for CliPing {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
writeln_name_value(f, "Source Account:", &self.source_pubkey)?;
|
||||
if let Some(fixed_blockhash) = &self.fixed_blockhash {
|
||||
let blockhash_origin = if self.blockhash_from_cluster {
|
||||
"fetched from cluster"
|
||||
} else {
|
||||
"supplied from cli arguments"
|
||||
};
|
||||
writeln!(
|
||||
f,
|
||||
"Fixed blockhash is used: {} ({})",
|
||||
fixed_blockhash, blockhash_origin
|
||||
)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
for ping in &self.pings {
|
||||
write!(f, "{}", ping)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
writeln!(f, "--- transaction statistics ---")?;
|
||||
write!(f, "{}", self.transaction_stats)?;
|
||||
if let Some(confirmation_stats) = &self.confirmation_stats {
|
||||
write!(f, "{}", confirmation_stats)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliPing {}
|
||||
impl VerboseDisplay for CliPing {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliPingData {
|
||||
pub success: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub signature: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ms: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
#[serde(skip_serializing)]
|
||||
pub print_timestamp: bool,
|
||||
pub timestamp: String,
|
||||
pub sequence: u64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub lamports: Option<u64>,
|
||||
}
|
||||
impl fmt::Display for CliPingData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let (mark, msg) = if let Some(signature) = &self.signature {
|
||||
if self.success {
|
||||
(
|
||||
CHECK_MARK,
|
||||
format!(
|
||||
"{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
|
||||
self.lamports.unwrap(),
|
||||
self.sequence,
|
||||
self.ms.unwrap(),
|
||||
signature
|
||||
),
|
||||
)
|
||||
} else if let Some(error) = &self.error {
|
||||
(
|
||||
CROSS_MARK,
|
||||
format!(
|
||||
"Transaction failed: seq={:<3} error={:?} signature={}",
|
||||
self.sequence, error, signature
|
||||
),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
CROSS_MARK,
|
||||
format!(
|
||||
"Confirmation timeout: seq={:<3} signature={}",
|
||||
self.sequence, signature
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
CROSS_MARK,
|
||||
format!(
|
||||
"Submit failed: seq={:<3} error={:?}",
|
||||
self.sequence,
|
||||
self.error.as_ref().unwrap(),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{}{}{}",
|
||||
if self.print_timestamp {
|
||||
&self.timestamp
|
||||
} else {
|
||||
""
|
||||
},
|
||||
mark,
|
||||
msg
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliPingData {}
|
||||
impl VerboseDisplay for CliPingData {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliPingTxStats {
|
||||
pub num_transactions: u32,
|
||||
pub num_transaction_confirmed: u32,
|
||||
}
|
||||
impl fmt::Display for CliPingTxStats {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"{} transactions submitted, {} transactions confirmed, {:.1}% transaction loss",
|
||||
self.num_transactions,
|
||||
self.num_transaction_confirmed,
|
||||
(100.
|
||||
- f64::from(self.num_transaction_confirmed) / f64::from(self.num_transactions)
|
||||
* 100.)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliPingTxStats {}
|
||||
impl VerboseDisplay for CliPingTxStats {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliPingConfirmationStats {
|
||||
pub min: f64,
|
||||
pub mean: f64,
|
||||
pub max: f64,
|
||||
pub std_dev: f64,
|
||||
}
|
||||
impl fmt::Display for CliPingConfirmationStats {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"confirmation min/mean/max/stddev = {:.0}/{:.0}/{:.0}/{:.0} ms",
|
||||
self.min, self.mean, self.max, self.std_dev,
|
||||
)
|
||||
}
|
||||
}
|
||||
impl QuietDisplay for CliPingConfirmationStats {}
|
||||
impl VerboseDisplay for CliPingConfirmationStats {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {
|
||||
@@ -2776,10 +2604,10 @@ mod tests {
|
||||
|
||||
let expected_msg = "AwECBwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgTl3Dqh9\
|
||||
F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\
|
||||
BAQEBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBgYGBgYGBgYGBgYGBgYGBgYG\
|
||||
BgYGBgYGBgYGBgYGBgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAan1RcZLFaO\
|
||||
4IqEX3PSl4jPA1wxRbIas0TYBi6pQAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\
|
||||
BwcCBQMEBgIEBAAAAAUCAQMMAgAAACoAAAAAAAAA"
|
||||
BAQEBAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBQUFBQUFBQUFBQUFBQUFBQUF\
|
||||
BQUFBQUFBQUFBQUFBQUGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAAAAAAAAAAAAA\
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\
|
||||
BwcCBgMDBQIEBAAAAAYCAQQMAgAAACoAAAAAAAAA"
|
||||
.to_string();
|
||||
let config = ReturnSignersConfig {
|
||||
dump_transaction_message: true,
|
||||
|
@@ -4,18 +4,10 @@ use {
|
||||
console::style,
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
hash::Hash,
|
||||
instruction::CompiledInstruction,
|
||||
message::v0::MessageAddressTableLookup,
|
||||
native_token::lamports_to_sol,
|
||||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
stake,
|
||||
transaction::{TransactionError, TransactionVersion, VersionedTransaction},
|
||||
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
|
||||
program_utils::limited_deserialize, pubkey::Pubkey, stake, transaction::Transaction,
|
||||
},
|
||||
solana_transaction_status::{Rewards, UiTransactionStatusMeta},
|
||||
solana_transaction_status::UiTransactionStatusMeta,
|
||||
spl_memo::{id as spl_memo_id, v1::id as spl_memo_v1_id},
|
||||
std::{collections::HashMap, fmt, io},
|
||||
};
|
||||
@@ -139,28 +131,22 @@ pub fn println_signers(
|
||||
println!();
|
||||
}
|
||||
|
||||
struct CliAccountMeta {
|
||||
is_signer: bool,
|
||||
is_writable: bool,
|
||||
is_invoked: bool,
|
||||
}
|
||||
|
||||
fn format_account_mode(meta: CliAccountMeta) -> String {
|
||||
fn format_account_mode(message: &Message, index: usize) -> String {
|
||||
format!(
|
||||
"{}r{}{}", // accounts are always readable...
|
||||
if meta.is_signer {
|
||||
if message.is_signer(index) {
|
||||
"s" // stands for signer
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
if meta.is_writable {
|
||||
if message.is_writable(index) {
|
||||
"w" // comment for consistent rust fmt (no joking; lol)
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
// account may be executable on-chain while not being
|
||||
// designated as a program-id in the message
|
||||
if meta.is_invoked {
|
||||
if message.maybe_executable(index) {
|
||||
"x"
|
||||
} else {
|
||||
// programs to be executed via CPI cannot be identified as
|
||||
@@ -170,178 +156,41 @@ fn format_account_mode(meta: CliAccountMeta) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn write_transaction<W: io::Write>(
|
||||
pub fn write_transaction<W: io::Write>(
|
||||
w: &mut W,
|
||||
transaction: &VersionedTransaction,
|
||||
transaction_status: Option<&UiTransactionStatusMeta>,
|
||||
transaction: &Transaction,
|
||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||
prefix: &str,
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
timezone: CliTimezone,
|
||||
) -> io::Result<()> {
|
||||
write_block_time(w, block_time, timezone, prefix)?;
|
||||
|
||||
let message = &transaction.message;
|
||||
let account_keys: Vec<AccountKeyType> = {
|
||||
let static_keys_iter = message
|
||||
.static_account_keys()
|
||||
.iter()
|
||||
.map(AccountKeyType::Known);
|
||||
let dynamic_keys: Vec<AccountKeyType> = message
|
||||
.address_table_lookups()
|
||||
.map(transform_lookups_to_unknown_keys)
|
||||
.unwrap_or_default();
|
||||
static_keys_iter.chain(dynamic_keys).collect()
|
||||
};
|
||||
|
||||
write_version(w, transaction.version(), prefix)?;
|
||||
write_recent_blockhash(w, message.recent_blockhash(), prefix)?;
|
||||
write_signatures(w, &transaction.signatures, sigverify_status, prefix)?;
|
||||
|
||||
let mut fee_payer_index = None;
|
||||
for (account_index, account) in account_keys.iter().enumerate() {
|
||||
if fee_payer_index.is_none() && message.is_non_loader_key(account_index) {
|
||||
fee_payer_index = Some(account_index)
|
||||
}
|
||||
|
||||
let account_meta = CliAccountMeta {
|
||||
is_signer: message.is_signer(account_index),
|
||||
is_writable: message.is_maybe_writable(account_index),
|
||||
is_invoked: message.is_invoked(account_index),
|
||||
};
|
||||
|
||||
write_account(
|
||||
w,
|
||||
account_index,
|
||||
*account,
|
||||
format_account_mode(account_meta),
|
||||
Some(account_index) == fee_payer_index,
|
||||
prefix,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (instruction_index, instruction) in message.instructions().iter().enumerate() {
|
||||
let program_pubkey = account_keys[instruction.program_id_index as usize];
|
||||
let instruction_accounts = instruction
|
||||
.accounts
|
||||
.iter()
|
||||
.map(|account_index| (account_keys[*account_index as usize], *account_index));
|
||||
|
||||
write_instruction(
|
||||
w,
|
||||
instruction_index,
|
||||
program_pubkey,
|
||||
instruction,
|
||||
instruction_accounts,
|
||||
prefix,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(address_table_lookups) = message.address_table_lookups() {
|
||||
write_address_table_lookups(w, address_table_lookups, prefix)?;
|
||||
}
|
||||
|
||||
if let Some(transaction_status) = transaction_status {
|
||||
write_status(w, &transaction_status.status, prefix)?;
|
||||
write_fees(w, transaction_status.fee, prefix)?;
|
||||
write_balances(w, transaction_status, prefix)?;
|
||||
write_log_messages(w, transaction_status.log_messages.as_ref(), prefix)?;
|
||||
write_rewards(w, transaction_status.rewards.as_ref(), prefix)?;
|
||||
} else {
|
||||
writeln!(w, "{}Status: Unavailable", prefix)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transform_lookups_to_unknown_keys(lookups: &[MessageAddressTableLookup]) -> Vec<AccountKeyType> {
|
||||
let unknown_writable_keys = lookups
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(lookup_index, lookup)| {
|
||||
lookup
|
||||
.writable_indexes
|
||||
.iter()
|
||||
.map(move |table_index| AccountKeyType::Unknown {
|
||||
lookup_index,
|
||||
table_index: *table_index,
|
||||
})
|
||||
});
|
||||
|
||||
let unknown_readonly_keys = lookups
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(lookup_index, lookup)| {
|
||||
lookup
|
||||
.readonly_indexes
|
||||
.iter()
|
||||
.map(move |table_index| AccountKeyType::Unknown {
|
||||
lookup_index,
|
||||
table_index: *table_index,
|
||||
})
|
||||
});
|
||||
|
||||
unknown_writable_keys.chain(unknown_readonly_keys).collect()
|
||||
}
|
||||
|
||||
enum CliTimezone {
|
||||
Local,
|
||||
#[allow(dead_code)]
|
||||
Utc,
|
||||
}
|
||||
|
||||
fn write_block_time<W: io::Write>(
|
||||
w: &mut W,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
timezone: CliTimezone,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
if let Some(block_time) = block_time {
|
||||
let block_time_output = match timezone {
|
||||
CliTimezone::Local => format!("{:?}", Local.timestamp(block_time, 0)),
|
||||
CliTimezone::Utc => format!("{:?}", Utc.timestamp(block_time, 0)),
|
||||
};
|
||||
writeln!(w, "{}Block Time: {}", prefix, block_time_output,)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{}Block Time: {:?}",
|
||||
prefix,
|
||||
Local.timestamp(block_time, 0)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_version<W: io::Write>(
|
||||
w: &mut W,
|
||||
version: TransactionVersion,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
let version = match version {
|
||||
TransactionVersion::Legacy(_) => "legacy".to_string(),
|
||||
TransactionVersion::Number(number) => number.to_string(),
|
||||
};
|
||||
writeln!(w, "{}Version: {}", prefix, version)
|
||||
}
|
||||
|
||||
fn write_recent_blockhash<W: io::Write>(
|
||||
w: &mut W,
|
||||
recent_blockhash: &Hash,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
writeln!(w, "{}Recent Blockhash: {:?}", prefix, recent_blockhash)
|
||||
}
|
||||
|
||||
fn write_signatures<W: io::Write>(
|
||||
w: &mut W,
|
||||
signatures: &[Signature],
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
writeln!(
|
||||
w,
|
||||
"{}Recent Blockhash: {:?}",
|
||||
prefix, message.recent_blockhash
|
||||
)?;
|
||||
let sigverify_statuses = if let Some(sigverify_status) = sigverify_status {
|
||||
sigverify_status
|
||||
.iter()
|
||||
.map(|s| format!(" ({})", s))
|
||||
.collect()
|
||||
} else {
|
||||
vec!["".to_string(); signatures.len()]
|
||||
vec!["".to_string(); transaction.signatures.len()]
|
||||
};
|
||||
for (signature_index, (signature, sigverify_status)) in
|
||||
signatures.iter().zip(&sigverify_statuses).enumerate()
|
||||
for (signature_index, (signature, sigverify_status)) in transaction
|
||||
.signatures
|
||||
.iter()
|
||||
.zip(&sigverify_statuses)
|
||||
.enumerate()
|
||||
{
|
||||
writeln!(
|
||||
w,
|
||||
@@ -349,80 +198,44 @@ fn write_signatures<W: io::Write>(
|
||||
prefix, signature_index, signature, sigverify_status,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum AccountKeyType<'a> {
|
||||
Known(&'a Pubkey),
|
||||
Unknown {
|
||||
lookup_index: usize,
|
||||
table_index: u8,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for AccountKeyType<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Known(address) => write!(f, "{}", address),
|
||||
Self::Unknown {
|
||||
lookup_index,
|
||||
table_index,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Unknown Address (uses lookup {} and index {})",
|
||||
lookup_index, table_index
|
||||
)
|
||||
}
|
||||
let mut fee_payer_index = None;
|
||||
for (account_index, account) in message.account_keys.iter().enumerate() {
|
||||
if fee_payer_index.is_none() && message.is_non_loader_key(account_index) {
|
||||
fee_payer_index = Some(account_index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_account<W: io::Write>(
|
||||
w: &mut W,
|
||||
account_index: usize,
|
||||
account_address: AccountKeyType,
|
||||
account_mode: String,
|
||||
is_fee_payer: bool,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
writeln!(
|
||||
w,
|
||||
"{}Account {}: {} {}{}",
|
||||
prefix,
|
||||
account_index,
|
||||
account_mode,
|
||||
account_address,
|
||||
if is_fee_payer { " (fee payer)" } else { "" },
|
||||
)
|
||||
}
|
||||
|
||||
fn write_instruction<'a, W: io::Write>(
|
||||
w: &mut W,
|
||||
instruction_index: usize,
|
||||
program_pubkey: AccountKeyType,
|
||||
instruction: &CompiledInstruction,
|
||||
instruction_accounts: impl Iterator<Item = (AccountKeyType<'a>, u8)>,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} Program: {} ({})",
|
||||
prefix, program_pubkey, instruction.program_id_index
|
||||
)?;
|
||||
for (index, (account_address, account_index)) in instruction_accounts.enumerate() {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {}: {} ({})",
|
||||
prefix, index, account_address, account_index
|
||||
"{}Account {}: {} {}{}",
|
||||
prefix,
|
||||
account_index,
|
||||
format_account_mode(message, account_index),
|
||||
account,
|
||||
if Some(account_index) == fee_payer_index {
|
||||
" (fee payer)"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)?;
|
||||
}
|
||||
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
||||
let program_pubkey = message.account_keys[instruction.program_id_index as usize];
|
||||
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} Program: {} ({})",
|
||||
prefix, program_pubkey, instruction.program_id_index
|
||||
)?;
|
||||
for (account_index, account) in instruction.accounts.iter().enumerate() {
|
||||
let account_pubkey = message.account_keys[*account as usize];
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {}: {} ({})",
|
||||
prefix, account_index, account_pubkey, account
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut raw = true;
|
||||
if let AccountKeyType::Known(program_pubkey) = program_pubkey {
|
||||
if program_pubkey == &solana_vote_program::id() {
|
||||
let mut raw = true;
|
||||
if program_pubkey == solana_vote_program::id() {
|
||||
if let Ok(vote_instruction) = limited_deserialize::<
|
||||
solana_vote_program::vote_instruction::VoteInstruction,
|
||||
>(&instruction.data)
|
||||
@@ -430,14 +243,14 @@ fn write_instruction<'a, W: io::Write>(
|
||||
writeln!(w, "{} {:?}", prefix, vote_instruction)?;
|
||||
raw = false;
|
||||
}
|
||||
} else if program_pubkey == &stake::program::id() {
|
||||
} else if program_pubkey == stake::program::id() {
|
||||
if let Ok(stake_instruction) =
|
||||
limited_deserialize::<stake::instruction::StakeInstruction>(&instruction.data)
|
||||
{
|
||||
writeln!(w, "{} {:?}", prefix, stake_instruction)?;
|
||||
raw = false;
|
||||
}
|
||||
} else if program_pubkey == &solana_sdk::system_program::id() {
|
||||
} else if program_pubkey == solana_sdk::system_program::id() {
|
||||
if let Ok(system_instruction) = limited_deserialize::<
|
||||
solana_sdk::system_instruction::SystemInstruction,
|
||||
>(&instruction.data)
|
||||
@@ -445,156 +258,110 @@ fn write_instruction<'a, W: io::Write>(
|
||||
writeln!(w, "{} {:?}", prefix, system_instruction)?;
|
||||
raw = false;
|
||||
}
|
||||
} else if is_memo_program(program_pubkey) {
|
||||
} else if is_memo_program(&program_pubkey) {
|
||||
if let Ok(s) = std::str::from_utf8(&instruction.data) {
|
||||
writeln!(w, "{} Data: \"{}\"", prefix, s)?;
|
||||
raw = false;
|
||||
}
|
||||
}
|
||||
|
||||
if raw {
|
||||
writeln!(w, "{} Data: {:?}", prefix, instruction.data)?;
|
||||
}
|
||||
}
|
||||
|
||||
if raw {
|
||||
writeln!(w, "{} Data: {:?}", prefix, instruction.data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_address_table_lookups<W: io::Write>(
|
||||
w: &mut W,
|
||||
address_table_lookups: &[MessageAddressTableLookup],
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
for (lookup_index, lookup) in address_table_lookups.iter().enumerate() {
|
||||
writeln!(w, "{}Address Table Lookup {}", prefix, lookup_index,)?;
|
||||
writeln!(w, "{} Table Account: {}", prefix, lookup.account_key,)?;
|
||||
if let Some(transaction_status) = transaction_status {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Writable Indexes: {:?}",
|
||||
"{}Status: {}",
|
||||
prefix,
|
||||
&lookup.writable_indexes[..],
|
||||
match &transaction_status.status {
|
||||
Ok(_) => "Ok".into(),
|
||||
Err(err) => err.to_string(),
|
||||
}
|
||||
)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} Readonly Indexes: {:?}",
|
||||
"{} Fee: ◎{}",
|
||||
prefix,
|
||||
&lookup.readonly_indexes[..],
|
||||
lamports_to_sol(transaction_status.fee)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_rewards<W: io::Write>(
|
||||
w: &mut W,
|
||||
rewards: Option<&Rewards>,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
if let Some(rewards) = rewards {
|
||||
if !rewards.is_empty() {
|
||||
writeln!(w, "{}Rewards:", prefix,)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<16} {:<20}",
|
||||
prefix, "Address", "Type", "Amount", "New Balance"
|
||||
)?;
|
||||
for reward in rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
assert_eq!(
|
||||
transaction_status.pre_balances.len(),
|
||||
transaction_status.post_balances.len()
|
||||
);
|
||||
for (i, (pre, post)) in transaction_status
|
||||
.pre_balances
|
||||
.iter()
|
||||
.zip(transaction_status.post_balances.iter())
|
||||
.enumerate()
|
||||
{
|
||||
if pre == post {
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {}◎{:<14.9} ◎{:<18.9}",
|
||||
"{} Account {} balance: ◎{}",
|
||||
prefix,
|
||||
reward.pubkey,
|
||||
if let Some(reward_type) = reward.reward_type {
|
||||
format!("{}", reward_type)
|
||||
} else {
|
||||
"-".to_string()
|
||||
},
|
||||
sign,
|
||||
lamports_to_sol(reward.lamports.abs() as u64),
|
||||
lamports_to_sol(reward.post_balance)
|
||||
i,
|
||||
lamports_to_sol(*pre)
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {} balance: ◎{} -> ◎{}",
|
||||
prefix,
|
||||
i,
|
||||
lamports_to_sol(*pre),
|
||||
lamports_to_sol(*post)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_status<W: io::Write>(
|
||||
w: &mut W,
|
||||
transaction_status: &Result<(), TransactionError>,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
writeln!(
|
||||
w,
|
||||
"{}Status: {}",
|
||||
prefix,
|
||||
match transaction_status {
|
||||
Ok(_) => "Ok".into(),
|
||||
Err(err) => err.to_string(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn write_fees<W: io::Write>(w: &mut W, transaction_fee: u64, prefix: &str) -> io::Result<()> {
|
||||
writeln!(w, "{} Fee: ◎{}", prefix, lamports_to_sol(transaction_fee))
|
||||
}
|
||||
|
||||
fn write_balances<W: io::Write>(
|
||||
w: &mut W,
|
||||
transaction_status: &UiTransactionStatusMeta,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
assert_eq!(
|
||||
transaction_status.pre_balances.len(),
|
||||
transaction_status.post_balances.len()
|
||||
);
|
||||
for (i, (pre, post)) in transaction_status
|
||||
.pre_balances
|
||||
.iter()
|
||||
.zip(transaction_status.post_balances.iter())
|
||||
.enumerate()
|
||||
{
|
||||
if pre == post {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {} balance: ◎{}",
|
||||
prefix,
|
||||
i,
|
||||
lamports_to_sol(*pre)
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {} balance: ◎{} -> ◎{}",
|
||||
prefix,
|
||||
i,
|
||||
lamports_to_sol(*pre),
|
||||
lamports_to_sol(*post)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_log_messages<W: io::Write>(
|
||||
w: &mut W,
|
||||
log_messages: Option<&Vec<String>>,
|
||||
prefix: &str,
|
||||
) -> io::Result<()> {
|
||||
if let Some(log_messages) = log_messages {
|
||||
if !log_messages.is_empty() {
|
||||
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||
for log_message in log_messages {
|
||||
writeln!(w, "{} {}", prefix, log_message)?;
|
||||
if let Some(log_messages) = &transaction_status.log_messages {
|
||||
if !log_messages.is_empty() {
|
||||
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||
for log_message in log_messages {
|
||||
writeln!(w, "{} {}", prefix, log_message)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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} {}◎{:<14.9} ◎{:<18.9}",
|
||||
prefix,
|
||||
reward.pubkey,
|
||||
if let Some(reward_type) = reward.reward_type {
|
||||
format!("{}", reward_type)
|
||||
} else {
|
||||
"-".to_string()
|
||||
},
|
||||
sign,
|
||||
lamports_to_sol(reward.lamports.abs() as u64),
|
||||
lamports_to_sol(reward.post_balance)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeln!(w, "{}Status: Unavailable", prefix)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn println_transaction(
|
||||
transaction: &VersionedTransaction,
|
||||
transaction_status: Option<&UiTransactionStatusMeta>,
|
||||
transaction: &Transaction,
|
||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||
prefix: &str,
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
@@ -607,7 +374,6 @@ pub fn println_transaction(
|
||||
prefix,
|
||||
sigverify_status,
|
||||
block_time,
|
||||
CliTimezone::Local,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -619,24 +385,23 @@ pub fn println_transaction(
|
||||
|
||||
pub fn writeln_transaction(
|
||||
f: &mut dyn fmt::Write,
|
||||
transaction: &VersionedTransaction,
|
||||
transaction_status: Option<&UiTransactionStatusMeta>,
|
||||
transaction: &Transaction,
|
||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||
prefix: &str,
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
) -> fmt::Result {
|
||||
let mut w = Vec::new();
|
||||
let write_result = write_transaction(
|
||||
if write_transaction(
|
||||
&mut w,
|
||||
transaction,
|
||||
transaction_status,
|
||||
prefix,
|
||||
sigverify_status,
|
||||
block_time,
|
||||
CliTimezone::Local,
|
||||
);
|
||||
|
||||
if write_result.is_ok() {
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
if let Ok(s) = String::from_utf8(w) {
|
||||
write!(f, "{}", s)?;
|
||||
}
|
||||
@@ -662,215 +427,7 @@ pub fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
solana_sdk::{
|
||||
message::{
|
||||
v0::{self, LoadedAddresses},
|
||||
Message as LegacyMessage, MessageHeader, VersionedMessage,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
transaction::Transaction,
|
||||
},
|
||||
solana_transaction_status::{Reward, RewardType, TransactionStatusMeta},
|
||||
std::io::BufWriter,
|
||||
};
|
||||
|
||||
fn new_test_keypair() -> Keypair {
|
||||
let secret = ed25519_dalek::SecretKey::from_bytes(&[0u8; 32]).unwrap();
|
||||
let public = ed25519_dalek::PublicKey::from(&secret);
|
||||
let keypair = ed25519_dalek::Keypair { secret, public };
|
||||
Keypair::from_bytes(&keypair.to_bytes()).unwrap()
|
||||
}
|
||||
|
||||
fn new_test_v0_transaction() -> VersionedTransaction {
|
||||
let keypair = new_test_keypair();
|
||||
let account_key = Pubkey::new_from_array([1u8; 32]);
|
||||
let address_table_key = Pubkey::new_from_array([2u8; 32]);
|
||||
VersionedTransaction::try_new(
|
||||
VersionedMessage::V0(v0::Message {
|
||||
header: MessageHeader {
|
||||
num_required_signatures: 1,
|
||||
num_readonly_signed_accounts: 0,
|
||||
num_readonly_unsigned_accounts: 1,
|
||||
},
|
||||
recent_blockhash: Hash::default(),
|
||||
account_keys: vec![keypair.pubkey(), account_key],
|
||||
address_table_lookups: vec![MessageAddressTableLookup {
|
||||
account_key: address_table_key,
|
||||
writable_indexes: vec![0],
|
||||
readonly_indexes: vec![1],
|
||||
}],
|
||||
instructions: vec![CompiledInstruction::new_from_raw_parts(
|
||||
3,
|
||||
vec![],
|
||||
vec![1, 2],
|
||||
)],
|
||||
}),
|
||||
&[&keypair],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_legacy_transaction() {
|
||||
let keypair = new_test_keypair();
|
||||
let account_key = Pubkey::new_from_array([1u8; 32]);
|
||||
let transaction = VersionedTransaction::from(Transaction::new(
|
||||
&[&keypair],
|
||||
LegacyMessage {
|
||||
header: MessageHeader {
|
||||
num_required_signatures: 1,
|
||||
num_readonly_signed_accounts: 0,
|
||||
num_readonly_unsigned_accounts: 1,
|
||||
},
|
||||
recent_blockhash: Hash::default(),
|
||||
account_keys: vec![keypair.pubkey(), account_key],
|
||||
instructions: vec![CompiledInstruction::new_from_raw_parts(1, vec![], vec![0])],
|
||||
},
|
||||
Hash::default(),
|
||||
));
|
||||
|
||||
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(&transaction);
|
||||
let meta = TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 5000,
|
||||
pre_balances: vec![5000, 10_000],
|
||||
post_balances: vec![0, 9_900],
|
||||
inner_instructions: None,
|
||||
log_messages: Some(vec!["Test message".to_string()]),
|
||||
pre_token_balances: None,
|
||||
post_token_balances: None,
|
||||
rewards: Some(vec![Reward {
|
||||
pubkey: account_key.to_string(),
|
||||
lamports: -100,
|
||||
post_balance: 9_900,
|
||||
reward_type: Some(RewardType::Rent),
|
||||
commission: None,
|
||||
}]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
};
|
||||
|
||||
let output = {
|
||||
let mut write_buffer = BufWriter::new(Vec::new());
|
||||
write_transaction(
|
||||
&mut write_buffer,
|
||||
&transaction,
|
||||
Some(&meta.into()),
|
||||
"",
|
||||
Some(&sigverify_status),
|
||||
Some(1628633791),
|
||||
CliTimezone::Utc,
|
||||
)
|
||||
.unwrap();
|
||||
let bytes = write_buffer.into_inner().unwrap();
|
||||
String::from_utf8(bytes).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
output,
|
||||
r#"Block Time: 2021-08-10T22:16:31Z
|
||||
Version: legacy
|
||||
Recent Blockhash: 11111111111111111111111111111111
|
||||
Signature 0: 5pkjrE4VBa3Bu9CMKXgh1U345cT1gGo8QBVRTzHAo6gHeiPae5BTbShP15g6NgqRMNqu8Qrhph1ATmrfC1Ley3rx (pass)
|
||||
Account 0: srw- 4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS (fee payer)
|
||||
Account 1: -r-x 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi
|
||||
Instruction 0
|
||||
Program: 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi (1)
|
||||
Account 0: 4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS (0)
|
||||
Data: []
|
||||
Status: Ok
|
||||
Fee: ◎0.000005
|
||||
Account 0 balance: ◎0.000005 -> ◎0
|
||||
Account 1 balance: ◎0.00001 -> ◎0.0000099
|
||||
Log Messages:
|
||||
Test message
|
||||
Rewards:
|
||||
Address Type Amount New Balance \0
|
||||
4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi rent -◎0.000000100 ◎0.000009900 \0
|
||||
"#.replace("\\0", "") // replace marker used to subvert trailing whitespace linter on CI
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_v0_transaction() {
|
||||
let versioned_tx = new_test_v0_transaction();
|
||||
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(&versioned_tx);
|
||||
let address_table_entry1 = Pubkey::new_from_array([3u8; 32]);
|
||||
let address_table_entry2 = Pubkey::new_from_array([4u8; 32]);
|
||||
let loaded_addresses = LoadedAddresses {
|
||||
writable: vec![address_table_entry1],
|
||||
readonly: vec![address_table_entry2],
|
||||
};
|
||||
let meta = TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 5000,
|
||||
pre_balances: vec![5000, 10_000, 15_000, 20_000],
|
||||
post_balances: vec![0, 10_000, 14_900, 20_000],
|
||||
inner_instructions: None,
|
||||
log_messages: Some(vec!["Test message".to_string()]),
|
||||
pre_token_balances: None,
|
||||
post_token_balances: None,
|
||||
rewards: Some(vec![Reward {
|
||||
pubkey: address_table_entry1.to_string(),
|
||||
lamports: -100,
|
||||
post_balance: 14_900,
|
||||
reward_type: Some(RewardType::Rent),
|
||||
commission: None,
|
||||
}]),
|
||||
loaded_addresses,
|
||||
};
|
||||
|
||||
let output = {
|
||||
let mut write_buffer = BufWriter::new(Vec::new());
|
||||
write_transaction(
|
||||
&mut write_buffer,
|
||||
&versioned_tx,
|
||||
Some(&meta.into()),
|
||||
"",
|
||||
Some(&sigverify_status),
|
||||
Some(1628633791),
|
||||
CliTimezone::Utc,
|
||||
)
|
||||
.unwrap();
|
||||
let bytes = write_buffer.into_inner().unwrap();
|
||||
String::from_utf8(bytes).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
output,
|
||||
r#"Block Time: 2021-08-10T22:16:31Z
|
||||
Version: 0
|
||||
Recent Blockhash: 11111111111111111111111111111111
|
||||
Signature 0: 5iEy3TT3ZhTA1NkuCY8GrQGNVY8d5m1bpjdh5FT3Ca4Py81fMipAZjafDuKJKrkw5q5UAAd8oPcgZ4nyXpHt4Fp7 (pass)
|
||||
Account 0: srw- 4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS (fee payer)
|
||||
Account 1: -r-- 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi
|
||||
Account 2: -rw- Unknown Address (uses lookup 0 and index 0)
|
||||
Account 3: -r-x Unknown Address (uses lookup 0 and index 1)
|
||||
Instruction 0
|
||||
Program: Unknown Address (uses lookup 0 and index 1) (3)
|
||||
Account 0: 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi (1)
|
||||
Account 1: Unknown Address (uses lookup 0 and index 0) (2)
|
||||
Data: []
|
||||
Address Table Lookup 0
|
||||
Table Account: 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR
|
||||
Writable Indexes: [0]
|
||||
Readonly Indexes: [1]
|
||||
Status: Ok
|
||||
Fee: ◎0.000005
|
||||
Account 0 balance: ◎0.000005 -> ◎0
|
||||
Account 1 balance: ◎0.00001
|
||||
Account 2 balance: ◎0.000015 -> ◎0.0000149
|
||||
Account 3 balance: ◎0.00002
|
||||
Log Messages:
|
||||
Test message
|
||||
Rewards:
|
||||
Address Type Amount New Balance \0
|
||||
CktRuQ2mttgRGkXJtyksdKHjUdc2C4TgDzyB98oEzy8 rent -◎0.000000100 ◎0.000014900 \0
|
||||
"#.replace("\\0", "") // replace marker used to subvert trailing whitespace linter on CI
|
||||
);
|
||||
}
|
||||
use {super::*, solana_sdk::pubkey::Pubkey};
|
||||
|
||||
#[test]
|
||||
fn test_format_labeled_address() {
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2021"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,43 +13,43 @@ documentation = "https://docs.rs/solana-cli"
|
||||
bincode = "1.3.3"
|
||||
bs58 = "0.4.0"
|
||||
clap = "2.33.1"
|
||||
criterion-stats = "0.3.0"
|
||||
ctrlc = { version = "3.2.1", features = ["termination"] }
|
||||
console = "0.15.0"
|
||||
const_format = "0.2.22"
|
||||
criterion-stats = "0.3.0"
|
||||
crossbeam-channel = "0.5"
|
||||
ctrlc = { version = "3.2.1", features = ["termination"] }
|
||||
humantime = "2.0.1"
|
||||
log = "0.4.14"
|
||||
humantime = "2.0.1"
|
||||
num-traits = "0.2"
|
||||
pretty-hex = "0.2.1"
|
||||
reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
semver = "1.0.6"
|
||||
serde = "1.0.136"
|
||||
reqwest = { version = "0.11.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
semver = "1.0.4"
|
||||
serde = "1.0.134"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.79"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.5" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.10.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.5" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.10.5" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.10.5" }
|
||||
solana-client = { path = "../client", version = "=1.10.5" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.10.5" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.10.5" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.5" }
|
||||
solana_rbpf = "=0.2.24"
|
||||
serde_json = "1.0.78"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.10.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.10.0" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.10.0" }
|
||||
solana-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.10.0" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.0" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.10.0" }
|
||||
solana_rbpf = "=0.2.21"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0.30"
|
||||
tiny-bip39 = "0.8.2"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-test-validator = { path = "../test-validator", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-test-validator = { path = "../test-validator", version = "=1.10.0" }
|
||||
tempfile = "3.3.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -30,7 +30,7 @@ use {
|
||||
pubkey::Pubkey,
|
||||
signature::{Signature, Signer, SignerError},
|
||||
stake::{instruction::LockupArgs, state::Lockup},
|
||||
transaction::{TransactionError, VersionedTransaction},
|
||||
transaction::{Transaction, TransactionError},
|
||||
},
|
||||
solana_vote_program::vote_state::VoteAuthorize,
|
||||
std::{collections::HashMap, error, io::stdout, str::FromStr, sync::Arc, time::Duration},
|
||||
@@ -88,7 +88,6 @@ pub enum CliCommand {
|
||||
timeout: Duration,
|
||||
blockhash: Option<Hash>,
|
||||
print_timestamp: bool,
|
||||
additional_fee: Option<u32>,
|
||||
},
|
||||
Rent {
|
||||
data_length: usize,
|
||||
@@ -162,7 +161,6 @@ pub enum CliCommand {
|
||||
address: Option<SignerIndex>,
|
||||
use_deprecated_loader: bool,
|
||||
allow_excessive_balance: bool,
|
||||
skip_fee_check: bool,
|
||||
},
|
||||
Program(ProgramCliCommand),
|
||||
// Stake Commands
|
||||
@@ -386,7 +384,7 @@ pub enum CliCommand {
|
||||
seed: String,
|
||||
program_id: Pubkey,
|
||||
},
|
||||
DecodeTransaction(VersionedTransaction),
|
||||
DecodeTransaction(Transaction),
|
||||
ResolveSigner(Option<String>),
|
||||
ShowAccount {
|
||||
pubkey: Pubkey,
|
||||
@@ -745,7 +743,6 @@ pub fn parse_command(
|
||||
signers.push(signer);
|
||||
1
|
||||
});
|
||||
let skip_fee_check = matches.is_present("skip_fee_check");
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Deploy {
|
||||
@@ -753,7 +750,6 @@ pub fn parse_command(
|
||||
address,
|
||||
use_deprecated_loader: matches.is_present("use_deprecated_loader"),
|
||||
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
|
||||
skip_fee_check,
|
||||
},
|
||||
signers,
|
||||
})
|
||||
@@ -981,7 +977,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
timeout,
|
||||
blockhash,
|
||||
print_timestamp,
|
||||
additional_fee,
|
||||
} => process_ping(
|
||||
&rpc_client,
|
||||
config,
|
||||
@@ -990,7 +985,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
timeout,
|
||||
blockhash,
|
||||
*print_timestamp,
|
||||
additional_fee,
|
||||
),
|
||||
CliCommand::Rent {
|
||||
data_length,
|
||||
@@ -1132,7 +1126,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
address,
|
||||
use_deprecated_loader,
|
||||
allow_excessive_balance,
|
||||
skip_fee_check,
|
||||
} => process_deploy(
|
||||
rpc_client,
|
||||
config,
|
||||
@@ -1140,7 +1133,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
*address,
|
||||
*use_deprecated_loader,
|
||||
*allow_excessive_balance,
|
||||
*skip_fee_check,
|
||||
),
|
||||
CliCommand::Program(program_subcommand) => {
|
||||
process_program_subcommand(rpc_client, config, program_subcommand)
|
||||
@@ -1972,7 +1964,6 @@ mod tests {
|
||||
address: None,
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
},
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -1995,7 +1986,6 @@ mod tests {
|
||||
address: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -2389,7 +2379,6 @@ mod tests {
|
||||
address: None,
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
};
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let result = process_command(&config);
|
||||
@@ -2410,7 +2399,6 @@ mod tests {
|
||||
address: None,
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
};
|
||||
assert!(process_command(&config).is_err());
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ use {
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
},
|
||||
clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand},
|
||||
console::style,
|
||||
console::{style, Emoji},
|
||||
crossbeam_channel::unbounded,
|
||||
serde::{Deserialize, Serialize},
|
||||
solana_clap_utils::{
|
||||
@@ -16,7 +16,7 @@ use {
|
||||
solana_cli_output::{
|
||||
display::{
|
||||
build_balance_message, format_labeled_address, new_spinner_progress_bar,
|
||||
println_transaction, unix_timestamp_to_string, writeln_name_value,
|
||||
println_name_value, println_transaction, unix_timestamp_to_string, writeln_name_value,
|
||||
},
|
||||
*,
|
||||
},
|
||||
@@ -33,14 +33,12 @@ use {
|
||||
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
|
||||
rpc_response::SlotInfo,
|
||||
},
|
||||
solana_program_runtime::compute_budget,
|
||||
solana_remote_wallet::remote_wallet::RemoteWalletManager,
|
||||
solana_sdk::{
|
||||
account::from_account,
|
||||
account_utils::StateMut,
|
||||
clock::{self, Clock, Slot},
|
||||
commitment_config::CommitmentConfig,
|
||||
compute_budget::ComputeBudgetInstruction,
|
||||
epoch_schedule::Epoch,
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
@@ -77,6 +75,9 @@ use {
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
||||
static CROSS_MARK: Emoji = Emoji("❌ ", "");
|
||||
|
||||
pub trait ClusterQuerySubCommands {
|
||||
fn cluster_query_subcommands(self) -> Self;
|
||||
}
|
||||
@@ -271,13 +272,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.default_value("15")
|
||||
.help("Wait up to timeout seconds for transaction confirmation"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("additional_fee")
|
||||
.long("additional-fee")
|
||||
.value_name("NUMBER")
|
||||
.takes_value(true)
|
||||
.help("Request additional-fee for transaction"),
|
||||
)
|
||||
.arg(blockhash_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -522,7 +516,6 @@ pub fn parse_cluster_ping(
|
||||
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
||||
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
|
||||
let print_timestamp = matches.is_present("print_timestamp");
|
||||
let additional_fee = value_of(matches, "additional_fee");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Ping {
|
||||
interval,
|
||||
@@ -530,7 +523,6 @@ pub fn parse_cluster_ping(
|
||||
timeout,
|
||||
blockhash,
|
||||
print_timestamp,
|
||||
additional_fee,
|
||||
},
|
||||
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
||||
})
|
||||
@@ -1058,7 +1050,6 @@ pub fn process_get_block(
|
||||
RpcBlockConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
max_supported_transaction_version: Some(0),
|
||||
..RpcBlockConfig::default()
|
||||
},
|
||||
)?
|
||||
@@ -1362,16 +1353,16 @@ pub fn process_ping(
|
||||
timeout: &Duration,
|
||||
fixed_blockhash: &Option<Hash>,
|
||||
print_timestamp: bool,
|
||||
additional_fee: &Option<u32>,
|
||||
) -> ProcessResult {
|
||||
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
||||
println!();
|
||||
|
||||
let (signal_sender, signal_receiver) = unbounded();
|
||||
ctrlc::set_handler(move || {
|
||||
let _ = signal_sender.send(());
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
let mut cli_pings = vec![];
|
||||
|
||||
let mut submit_count = 0;
|
||||
let mut confirmed_count = 0;
|
||||
let mut confirmation_time: VecDeque<u64> = VecDeque::with_capacity(1024);
|
||||
@@ -1379,15 +1370,18 @@ pub fn process_ping(
|
||||
let mut blockhash = rpc_client.get_latest_blockhash()?;
|
||||
let mut lamports = 0;
|
||||
let mut blockhash_acquired = Instant::now();
|
||||
let mut blockhash_from_cluster = false;
|
||||
if let Some(fixed_blockhash) = fixed_blockhash {
|
||||
if *fixed_blockhash != Hash::default() {
|
||||
let blockhash_origin = if *fixed_blockhash != Hash::default() {
|
||||
blockhash = *fixed_blockhash;
|
||||
"supplied from cli arguments"
|
||||
} else {
|
||||
blockhash_from_cluster = true;
|
||||
}
|
||||
"fetched from cluster"
|
||||
};
|
||||
println!(
|
||||
"Fixed blockhash is used: {} ({})",
|
||||
blockhash, blockhash_origin
|
||||
);
|
||||
}
|
||||
|
||||
'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) {
|
||||
let now = Instant::now();
|
||||
if fixed_blockhash.is_none() && now.duration_since(blockhash_acquired).as_secs() > 60 {
|
||||
@@ -1402,18 +1396,8 @@ pub fn process_ping(
|
||||
lamports += 1;
|
||||
|
||||
let build_message = |lamports| {
|
||||
let mut ixs = vec![system_instruction::transfer(
|
||||
&config.signers[0].pubkey(),
|
||||
&to,
|
||||
lamports,
|
||||
)];
|
||||
if let Some(additional_fee) = additional_fee {
|
||||
ixs.push(ComputeBudgetInstruction::request_units(
|
||||
compute_budget::DEFAULT_UNITS,
|
||||
*additional_fee,
|
||||
));
|
||||
}
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
|
||||
Message::new(&[ix], Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
@@ -1432,7 +1416,11 @@ pub fn process_ping(
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_micros();
|
||||
format!("[{}.{:06}] ", micros / 1_000_000, micros % 1_000_000)
|
||||
if print_timestamp {
|
||||
format!("[{}.{:06}] ", micros / 1_000_000, micros % 1_000_000)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
match rpc_client.send_transaction(&tx) {
|
||||
@@ -1446,51 +1434,35 @@ pub fn process_ping(
|
||||
Ok(()) => {
|
||||
let elapsed_time_millis = elapsed_time.as_millis() as u64;
|
||||
confirmation_time.push_back(elapsed_time_millis);
|
||||
let cli_ping_data = CliPingData {
|
||||
success: true,
|
||||
signature: Some(signature.to_string()),
|
||||
ms: Some(elapsed_time_millis),
|
||||
error: None,
|
||||
timestamp: timestamp(),
|
||||
print_timestamp,
|
||||
sequence: seq,
|
||||
lamports: Some(lamports),
|
||||
};
|
||||
eprint!("{}", cli_ping_data);
|
||||
cli_pings.push(cli_ping_data);
|
||||
println!(
|
||||
"{}{}{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
|
||||
timestamp(),
|
||||
CHECK_MARK, lamports, seq, elapsed_time_millis, signature
|
||||
);
|
||||
confirmed_count += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
let cli_ping_data = CliPingData {
|
||||
success: false,
|
||||
signature: Some(signature.to_string()),
|
||||
ms: None,
|
||||
error: Some(err.to_string()),
|
||||
timestamp: timestamp(),
|
||||
print_timestamp,
|
||||
sequence: seq,
|
||||
lamports: None,
|
||||
};
|
||||
eprint!("{}", cli_ping_data);
|
||||
cli_pings.push(cli_ping_data);
|
||||
println!(
|
||||
"{}{}Transaction failed: seq={:<3} error={:?} signature={}",
|
||||
timestamp(),
|
||||
CROSS_MARK,
|
||||
seq,
|
||||
err,
|
||||
signature
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if elapsed_time >= *timeout {
|
||||
let cli_ping_data = CliPingData {
|
||||
success: false,
|
||||
signature: Some(signature.to_string()),
|
||||
ms: None,
|
||||
error: None,
|
||||
timestamp: timestamp(),
|
||||
print_timestamp,
|
||||
sequence: seq,
|
||||
lamports: None,
|
||||
};
|
||||
eprint!("{}", cli_ping_data);
|
||||
cli_pings.push(cli_ping_data);
|
||||
println!(
|
||||
"{}{}Confirmation timeout: seq={:<3} signature={}",
|
||||
timestamp(),
|
||||
CROSS_MARK,
|
||||
seq,
|
||||
signature
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1504,18 +1476,13 @@ pub fn process_ping(
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let cli_ping_data = CliPingData {
|
||||
success: false,
|
||||
signature: None,
|
||||
ms: None,
|
||||
error: Some(err.to_string()),
|
||||
timestamp: timestamp(),
|
||||
print_timestamp,
|
||||
sequence: seq,
|
||||
lamports: None,
|
||||
};
|
||||
eprint!("{}", cli_ping_data);
|
||||
cli_pings.push(cli_ping_data);
|
||||
println!(
|
||||
"{}{}Submit failed: seq={:<3} error={:?}",
|
||||
timestamp(),
|
||||
CROSS_MARK,
|
||||
seq,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
submit_count += 1;
|
||||
@@ -1525,34 +1492,28 @@ pub fn process_ping(
|
||||
}
|
||||
}
|
||||
|
||||
let transaction_stats = CliPingTxStats {
|
||||
num_transactions: submit_count,
|
||||
num_transaction_confirmed: confirmed_count,
|
||||
};
|
||||
let confirmation_stats = if !confirmation_time.is_empty() {
|
||||
println!();
|
||||
println!("--- transaction statistics ---");
|
||||
println!(
|
||||
"{} transactions submitted, {} transactions confirmed, {:.1}% transaction loss",
|
||||
submit_count,
|
||||
confirmed_count,
|
||||
(100. - f64::from(confirmed_count) / f64::from(submit_count) * 100.)
|
||||
);
|
||||
if !confirmation_time.is_empty() {
|
||||
let samples: Vec<f64> = confirmation_time.iter().map(|t| *t as f64).collect();
|
||||
let dist = criterion_stats::Distribution::from(samples.into_boxed_slice());
|
||||
let mean = dist.mean();
|
||||
Some(CliPingConfirmationStats {
|
||||
min: dist.min(),
|
||||
println!(
|
||||
"confirmation min/mean/max/stddev = {:.0}/{:.0}/{:.0}/{:.0} ms",
|
||||
dist.min(),
|
||||
mean,
|
||||
max: dist.max(),
|
||||
std_dev: dist.std_dev(Some(mean)),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
dist.max(),
|
||||
dist.std_dev(Some(mean))
|
||||
);
|
||||
}
|
||||
|
||||
let cli_ping = CliPing {
|
||||
source_pubkey: config.signers[0].pubkey().to_string(),
|
||||
fixed_blockhash: fixed_blockhash.map(|_| blockhash.to_string()),
|
||||
blockhash_from_cluster,
|
||||
pings: cli_pings,
|
||||
transaction_stats,
|
||||
confirmation_stats,
|
||||
};
|
||||
|
||||
Ok(config.output_format.formatted_string(&cli_ping))
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
pub fn parse_logs(
|
||||
@@ -2043,7 +2004,6 @@ pub fn process_transaction_history(
|
||||
RpcTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
max_supported_transaction_version: Some(0),
|
||||
},
|
||||
) {
|
||||
Ok(confirmed_transaction) => {
|
||||
@@ -2053,7 +2013,7 @@ pub fn process_transaction_history(
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode"),
|
||||
confirmed_transaction.transaction.meta.as_ref(),
|
||||
&confirmed_transaction.transaction.meta,
|
||||
" ",
|
||||
None,
|
||||
None,
|
||||
@@ -2337,7 +2297,6 @@ mod tests {
|
||||
Hash::from_str("4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX").unwrap()
|
||||
),
|
||||
print_timestamp: true,
|
||||
additional_fee: None,
|
||||
},
|
||||
signers: vec![default_keypair.into()],
|
||||
}
|
||||
|
@@ -66,7 +66,6 @@ pub enum ProgramCliCommand {
|
||||
is_final: bool,
|
||||
max_len: Option<usize>,
|
||||
allow_excessive_balance: bool,
|
||||
skip_fee_check: bool,
|
||||
},
|
||||
WriteBuffer {
|
||||
program_location: String,
|
||||
@@ -74,7 +73,6 @@ pub enum ProgramCliCommand {
|
||||
buffer_pubkey: Option<Pubkey>,
|
||||
buffer_authority_signer_index: Option<SignerIndex>,
|
||||
max_len: Option<usize>,
|
||||
skip_fee_check: bool,
|
||||
},
|
||||
SetBufferAuthority {
|
||||
buffer_pubkey: Pubkey,
|
||||
@@ -116,13 +114,6 @@ impl ProgramSubCommands for App<'_, '_> {
|
||||
SubCommand::with_name("program")
|
||||
.about("Program management")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.arg(
|
||||
Arg::with_name("skip_fee_check")
|
||||
.long("skip-fee-check")
|
||||
.hidden(true)
|
||||
.takes_value(false)
|
||||
.global(true)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deploy")
|
||||
.about("Deploy a program")
|
||||
@@ -415,12 +406,6 @@ impl ProgramSubCommands for App<'_, '_> {
|
||||
.long("allow-excessive-deploy-account-balance")
|
||||
.takes_value(false)
|
||||
.help("Use the designated program id, even if the account already holds a large balance of SOL")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("skip_fee_check")
|
||||
.long("skip-fee-check")
|
||||
.hidden(true)
|
||||
.takes_value(false)
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -431,14 +416,7 @@ pub fn parse_program_subcommand(
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let (subcommand, sub_matches) = matches.subcommand();
|
||||
let matches_skip_fee_check = matches.is_present("skip_fee_check");
|
||||
let sub_matches_skip_fee_check = sub_matches
|
||||
.map(|m| m.is_present("skip_fee_check"))
|
||||
.unwrap_or(false);
|
||||
let skip_fee_check = matches_skip_fee_check || sub_matches_skip_fee_check;
|
||||
|
||||
let response = match (subcommand, sub_matches) {
|
||||
let response = match matches.subcommand() {
|
||||
("deploy", Some(matches)) => {
|
||||
let mut bulk_signers = vec![Some(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
@@ -498,7 +476,6 @@ pub fn parse_program_subcommand(
|
||||
is_final: matches.is_present("final"),
|
||||
max_len,
|
||||
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
|
||||
skip_fee_check,
|
||||
}),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
@@ -544,7 +521,6 @@ pub fn parse_program_subcommand(
|
||||
buffer_authority_signer_index: signer_info
|
||||
.index_of_or_none(buffer_authority_pubkey),
|
||||
max_len,
|
||||
skip_fee_check,
|
||||
}),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
@@ -693,7 +669,6 @@ pub fn process_program_subcommand(
|
||||
is_final,
|
||||
max_len,
|
||||
allow_excessive_balance,
|
||||
skip_fee_check,
|
||||
} => process_program_deploy(
|
||||
rpc_client,
|
||||
config,
|
||||
@@ -706,7 +681,6 @@ pub fn process_program_subcommand(
|
||||
*is_final,
|
||||
*max_len,
|
||||
*allow_excessive_balance,
|
||||
*skip_fee_check,
|
||||
),
|
||||
ProgramCliCommand::WriteBuffer {
|
||||
program_location,
|
||||
@@ -714,7 +688,6 @@ pub fn process_program_subcommand(
|
||||
buffer_pubkey,
|
||||
buffer_authority_signer_index,
|
||||
max_len,
|
||||
skip_fee_check,
|
||||
} => process_write_buffer(
|
||||
rpc_client,
|
||||
config,
|
||||
@@ -723,7 +696,6 @@ pub fn process_program_subcommand(
|
||||
*buffer_pubkey,
|
||||
*buffer_authority_signer_index,
|
||||
*max_len,
|
||||
*skip_fee_check,
|
||||
),
|
||||
ProgramCliCommand::SetBufferAuthority {
|
||||
buffer_pubkey,
|
||||
@@ -821,7 +793,6 @@ fn process_program_deploy(
|
||||
is_final: bool,
|
||||
max_len: Option<usize>,
|
||||
allow_excessive_balance: bool,
|
||||
skip_fee_check: bool,
|
||||
) -> ProcessResult {
|
||||
let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
|
||||
let (buffer_provided, buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index {
|
||||
@@ -976,7 +947,6 @@ fn process_program_deploy(
|
||||
&buffer_pubkey,
|
||||
Some(upgrade_authority_signer),
|
||||
allow_excessive_balance,
|
||||
skip_fee_check,
|
||||
)
|
||||
} else {
|
||||
do_process_program_upgrade(
|
||||
@@ -987,7 +957,6 @@ fn process_program_deploy(
|
||||
config.signers[upgrade_authority_signer_index],
|
||||
&buffer_pubkey,
|
||||
buffer_signer,
|
||||
skip_fee_check,
|
||||
)
|
||||
};
|
||||
if result.is_ok() && is_final {
|
||||
@@ -1014,7 +983,6 @@ fn process_write_buffer(
|
||||
buffer_pubkey: Option<Pubkey>,
|
||||
buffer_authority_signer_index: Option<SignerIndex>,
|
||||
max_len: Option<usize>,
|
||||
skip_fee_check: bool,
|
||||
) -> ProcessResult {
|
||||
// Create ephemeral keypair to use for Buffer account, if not provided
|
||||
let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
|
||||
@@ -1082,7 +1050,6 @@ fn process_write_buffer(
|
||||
&buffer_pubkey,
|
||||
Some(buffer_authority),
|
||||
true,
|
||||
skip_fee_check,
|
||||
);
|
||||
|
||||
if result.is_err() && buffer_signer_index.is_none() && buffer_signer.is_some() {
|
||||
@@ -1669,7 +1636,6 @@ pub fn process_deploy(
|
||||
buffer_signer_index: Option<SignerIndex>,
|
||||
use_deprecated_loader: bool,
|
||||
allow_excessive_balance: bool,
|
||||
skip_fee_check: bool,
|
||||
) -> ProcessResult {
|
||||
// Create ephemeral keypair to use for Buffer account, if not provided
|
||||
let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
|
||||
@@ -1700,7 +1666,6 @@ pub fn process_deploy(
|
||||
&buffer_signer.pubkey(),
|
||||
Some(buffer_signer),
|
||||
allow_excessive_balance,
|
||||
skip_fee_check,
|
||||
);
|
||||
if result.is_err() && buffer_signer_index.is_none() {
|
||||
report_ephemeral_mnemonic(words, mnemonic);
|
||||
@@ -1739,7 +1704,6 @@ fn do_process_program_write_and_deploy(
|
||||
buffer_pubkey: &Pubkey,
|
||||
buffer_authority_signer: Option<&dyn Signer>,
|
||||
allow_excessive_balance: bool,
|
||||
skip_fee_check: bool,
|
||||
) -> ProcessResult {
|
||||
// Build messages to calculate fees
|
||||
let mut messages: Vec<&Message> = Vec::new();
|
||||
@@ -1870,9 +1834,7 @@ fn do_process_program_write_and_deploy(
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
if !skip_fee_check {
|
||||
check_payer(&rpc_client, config, balance_needed, &messages)?;
|
||||
}
|
||||
check_payer(&rpc_client, config, balance_needed, &messages)?;
|
||||
|
||||
send_deploy_messages(
|
||||
rpc_client,
|
||||
@@ -1906,7 +1868,6 @@ fn do_process_program_upgrade(
|
||||
upgrade_authority: &dyn Signer,
|
||||
buffer_pubkey: &Pubkey,
|
||||
buffer_signer: Option<&dyn Signer>,
|
||||
skip_fee_check: bool,
|
||||
) -> ProcessResult {
|
||||
let loader_id = bpf_loader_upgradeable::id();
|
||||
let data_len = program_data.len();
|
||||
@@ -2006,10 +1967,7 @@ fn do_process_program_upgrade(
|
||||
);
|
||||
messages.push(&final_message);
|
||||
|
||||
if !skip_fee_check {
|
||||
check_payer(&rpc_client, config, balance_needed, &messages)?;
|
||||
}
|
||||
|
||||
check_payer(&rpc_client, config, balance_needed, &messages)?;
|
||||
send_deploy_messages(
|
||||
rpc_client,
|
||||
config,
|
||||
@@ -2041,7 +1999,10 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
|
||||
&program_data,
|
||||
Some(verifier::check),
|
||||
Config {
|
||||
reject_broken_elfs: true,
|
||||
reject_unresolved_syscalls: true,
|
||||
verify_mul64_imm_nonzero: false,
|
||||
verify_shift32_imm: true,
|
||||
reject_section_virtual_address_file_offset_mismatch: true,
|
||||
..Config::default()
|
||||
},
|
||||
register_syscalls(&mut invoke_context).unwrap(),
|
||||
@@ -2297,7 +2258,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2324,7 +2284,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: Some(42),
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2353,7 +2312,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -2384,7 +2342,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2414,7 +2371,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -2447,7 +2403,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -2475,7 +2430,6 @@ mod tests {
|
||||
upgrade_authority_signer_index: 0,
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
allow_excessive_balance: false,
|
||||
}),
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
@@ -2509,7 +2463,6 @@ mod tests {
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: Some(0),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2533,7 +2486,6 @@ mod tests {
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: Some(0),
|
||||
max_len: Some(42),
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2560,7 +2512,6 @@ mod tests {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: Some(0),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -2590,7 +2541,6 @@ mod tests {
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: Some(1),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -2625,7 +2575,6 @@ mod tests {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: Some(2),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![
|
||||
read_keypair_file(&keypair_file).unwrap().into(),
|
||||
@@ -3068,7 +3017,6 @@ mod tests {
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
}),
|
||||
signers: vec![&default_keypair],
|
||||
output_format: OutputFormat::JsonCompact,
|
||||
|
@@ -1383,12 +1383,11 @@ pub fn process_stake_authorize(
|
||||
};
|
||||
if let Some(authorized) = authorized {
|
||||
match authorization_type {
|
||||
StakeAuthorize::Staker => check_current_authority(
|
||||
&[authorized.withdrawer, authorized.staker],
|
||||
&authority.pubkey(),
|
||||
)?,
|
||||
StakeAuthorize::Staker => {
|
||||
check_current_authority(&authorized.staker, &authority.pubkey())?;
|
||||
}
|
||||
StakeAuthorize::Withdrawer => {
|
||||
check_current_authority(&[authorized.withdrawer], &authority.pubkey())?;
|
||||
check_current_authority(&authorized.withdrawer, &authority.pubkey())?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1930,7 +1929,7 @@ pub fn process_stake_set_lockup(
|
||||
};
|
||||
if let Some(lockup) = lockup {
|
||||
if lockup.custodian != Pubkey::default() {
|
||||
check_current_authority(&[lockup.custodian], &custodian.pubkey())?;
|
||||
check_current_authority(&lockup.custodian, &custodian.pubkey())?;
|
||||
}
|
||||
} else {
|
||||
return Err(CliError::RpcRequestError(format!(
|
||||
@@ -2114,13 +2113,13 @@ fn get_stake_account_state(
|
||||
}
|
||||
|
||||
pub(crate) fn check_current_authority(
|
||||
permitted_authorities: &[Pubkey],
|
||||
account_current_authority: &Pubkey,
|
||||
provided_current_authority: &Pubkey,
|
||||
) -> Result<(), CliError> {
|
||||
if !permitted_authorities.contains(provided_current_authority) {
|
||||
if account_current_authority != provided_current_authority {
|
||||
Err(CliError::RpcRequestError(format!(
|
||||
"Invalid authority provided: {:?}, expected {:?}",
|
||||
provided_current_authority, permitted_authorities
|
||||
"Invalid current authority provided: {:?}, expected {:?}",
|
||||
provided_current_authority, account_current_authority
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
|
@@ -291,12 +291,8 @@ pub fn process_set_validator_info(
|
||||
// Check existence of validator-info account
|
||||
let balance = rpc_client.get_balance(&info_pubkey).unwrap_or(0);
|
||||
|
||||
let keys = vec![
|
||||
(validator_info::id(), false),
|
||||
(config.signers[0].pubkey(), true),
|
||||
];
|
||||
let data_len = ValidatorInfo::max_space() + ConfigKeys::serialized_size(keys.clone());
|
||||
let lamports = rpc_client.get_minimum_balance_for_rent_exemption(data_len as usize)?;
|
||||
let lamports =
|
||||
rpc_client.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;
|
||||
|
||||
let signers = if balance == 0 {
|
||||
if info_pubkey != info_keypair.pubkey() {
|
||||
@@ -312,7 +308,10 @@ pub fn process_set_validator_info(
|
||||
};
|
||||
|
||||
let build_message = |lamports| {
|
||||
let keys = keys.clone();
|
||||
let keys = vec![
|
||||
(validator_info::id(), false),
|
||||
(config.signers[0].pubkey(), true),
|
||||
];
|
||||
if balance == 0 {
|
||||
println!(
|
||||
"Publishing info for Validator {:?}",
|
||||
|
@@ -910,10 +910,7 @@ pub fn process_vote_authorize(
|
||||
"Invalid vote account state; no authorized voters found".to_string(),
|
||||
)
|
||||
})?;
|
||||
check_current_authority(
|
||||
&[current_authorized_voter, vote_state.authorized_withdrawer],
|
||||
&authorized.pubkey(),
|
||||
)?;
|
||||
check_current_authority(¤t_authorized_voter, &authorized.pubkey())?;
|
||||
if let Some(signer) = new_authorized_signer {
|
||||
if signer.is_interactive() {
|
||||
return Err(CliError::BadParameter(format!(
|
||||
@@ -930,7 +927,7 @@ pub fn process_vote_authorize(
|
||||
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
|
||||
)?;
|
||||
if let Some(vote_state) = vote_state {
|
||||
check_current_authority(&[vote_state.authorized_withdrawer], &authorized.pubkey())?
|
||||
check_current_authority(&vote_state.authorized_withdrawer, &authorized.pubkey())?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,12 +37,9 @@ use {
|
||||
stake,
|
||||
system_instruction::{self, SystemError},
|
||||
system_program,
|
||||
transaction::{Transaction, VersionedTransaction},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
EncodableWithMeta, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction,
|
||||
TransactionBinaryEncoding, UiTransactionEncoding,
|
||||
transaction::Transaction,
|
||||
},
|
||||
solana_transaction_status::{Encodable, EncodedTransaction, UiTransactionEncoding},
|
||||
std::{fmt::Write as FmtWrite, fs::File, io::Write, sync::Arc},
|
||||
};
|
||||
|
||||
@@ -192,7 +189,7 @@ impl WalletSubCommands for App<'_, '_> {
|
||||
Arg::with_name("encoding")
|
||||
.index(2)
|
||||
.value_name("ENCODING")
|
||||
.possible_values(&["base58", "base64"]) // Variants of `TransactionBinaryEncoding` enum
|
||||
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
|
||||
.default_value("base58")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
@@ -276,14 +273,11 @@ impl WalletSubCommands for App<'_, '_> {
|
||||
}
|
||||
|
||||
fn resolve_derived_address_program_id(matches: &ArgMatches<'_>, arg_name: &str) -> Option<Pubkey> {
|
||||
matches.value_of(arg_name).and_then(|v| {
|
||||
let upper = v.to_ascii_uppercase();
|
||||
match upper.as_str() {
|
||||
"NONCE" | "SYSTEM" => Some(system_program::id()),
|
||||
"STAKE" => Some(stake::program::id()),
|
||||
"VOTE" => Some(solana_vote_program::id()),
|
||||
_ => pubkey_of(matches, arg_name),
|
||||
}
|
||||
matches.value_of(arg_name).and_then(|v| match v {
|
||||
"NONCE" => Some(system_program::id()),
|
||||
"STAKE" => Some(stake::program::id()),
|
||||
"VOTE" => Some(solana_vote_program::id()),
|
||||
_ => pubkey_of(matches, arg_name),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -344,13 +338,13 @@ pub fn parse_balance(
|
||||
|
||||
pub fn parse_decode_transaction(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let blob = value_t_or_exit!(matches, "transaction", String);
|
||||
let binary_encoding = match matches.value_of("encoding").unwrap() {
|
||||
"base58" => TransactionBinaryEncoding::Base58,
|
||||
"base64" => TransactionBinaryEncoding::Base64,
|
||||
let encoding = match matches.value_of("encoding").unwrap() {
|
||||
"base58" => UiTransactionEncoding::Base58,
|
||||
"base64" => UiTransactionEncoding::Base64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let encoded_transaction = EncodedTransaction::Binary(blob, binary_encoding);
|
||||
let encoded_transaction = EncodedTransaction::Binary(blob, encoding);
|
||||
if let Some(transaction) = encoded_transaction.decode() {
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::DecodeTransaction(transaction),
|
||||
@@ -562,25 +556,22 @@ pub fn process_confirm(
|
||||
RpcTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
max_supported_transaction_version: Some(0),
|
||||
},
|
||||
) {
|
||||
Ok(confirmed_transaction) => {
|
||||
let EncodedConfirmedTransactionWithStatusMeta {
|
||||
block_time,
|
||||
slot,
|
||||
transaction: transaction_with_meta,
|
||||
} = confirmed_transaction;
|
||||
|
||||
let decoded_transaction =
|
||||
transaction_with_meta.transaction.decode().unwrap();
|
||||
let json_transaction = decoded_transaction.json_encode();
|
||||
let decoded_transaction = confirmed_transaction
|
||||
.transaction
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode");
|
||||
let json_transaction =
|
||||
decoded_transaction.encode(UiTransactionEncoding::Json);
|
||||
|
||||
transaction = Some(CliTransaction {
|
||||
transaction: json_transaction,
|
||||
meta: transaction_with_meta.meta,
|
||||
block_time,
|
||||
slot: Some(slot),
|
||||
meta: confirmed_transaction.transaction.meta,
|
||||
block_time: confirmed_transaction.block_time,
|
||||
slot: Some(confirmed_transaction.slot),
|
||||
decoded_transaction,
|
||||
prefix: " ".to_string(),
|
||||
sigverify_status: vec![],
|
||||
@@ -612,14 +603,11 @@ pub fn process_confirm(
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
pub fn process_decode_transaction(
|
||||
config: &CliConfig,
|
||||
transaction: &VersionedTransaction,
|
||||
) -> ProcessResult {
|
||||
pub fn process_decode_transaction(config: &CliConfig, transaction: &Transaction) -> ProcessResult {
|
||||
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(transaction);
|
||||
let decode_transaction = CliTransaction {
|
||||
decoded_transaction: transaction.clone(),
|
||||
transaction: transaction.json_encode(),
|
||||
transaction: transaction.encode(UiTransactionEncoding::Json),
|
||||
meta: None,
|
||||
block_time: None,
|
||||
slot: None,
|
||||
|
@@ -238,7 +238,6 @@ fn full_battery_tests(
|
||||
#[test]
|
||||
#[allow(clippy::redundant_closure)]
|
||||
fn test_create_account_with_seed() {
|
||||
const ONE_SIG_FEE: f64 = 0.000005;
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
@@ -311,7 +310,7 @@ fn test_create_account_with_seed() {
|
||||
&offline_nonce_authority_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(4001.0 - ONE_SIG_FEE),
|
||||
sol_to_lamports(4000.999999999),
|
||||
&rpc_client,
|
||||
&online_nonce_creator_signer.pubkey(),
|
||||
);
|
||||
@@ -382,12 +381,12 @@ fn test_create_account_with_seed() {
|
||||
process_command(&submit_config).unwrap();
|
||||
check_balance!(sol_to_lamports(241.0), &rpc_client, &nonce_address);
|
||||
check_balance!(
|
||||
sol_to_lamports(32.0 - ONE_SIG_FEE),
|
||||
sol_to_lamports(31.999999999),
|
||||
&rpc_client,
|
||||
&offline_nonce_authority_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(4001.0 - ONE_SIG_FEE),
|
||||
sol_to_lamports(4000.999999999),
|
||||
&rpc_client,
|
||||
&online_nonce_creator_signer.pubkey(),
|
||||
);
|
||||
|
@@ -62,7 +62,6 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
address: None,
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
};
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
@@ -92,7 +91,6 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
address: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let account1 = rpc_client
|
||||
@@ -120,7 +118,6 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
address: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
skip_fee_check: false,
|
||||
};
|
||||
process_command(&config).unwrap_err();
|
||||
|
||||
@@ -130,7 +127,6 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
address: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: true,
|
||||
skip_fee_check: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let account2 = rpc_client
|
||||
@@ -197,7 +193,6 @@ fn test_cli_program_deploy_no_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
@@ -223,7 +218,6 @@ fn test_cli_program_deploy_no_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap_err();
|
||||
}
|
||||
@@ -284,7 +278,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
@@ -332,7 +325,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -374,7 +366,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
@@ -429,7 +420,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
@@ -504,7 +494,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap_err();
|
||||
|
||||
@@ -520,7 +509,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -623,7 +611,6 @@ fn test_cli_program_close_program() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
process_command(&config).unwrap();
|
||||
@@ -708,7 +695,6 @@ fn test_cli_program_write_buffer() {
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: None,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
@@ -743,7 +729,6 @@ fn test_cli_program_write_buffer() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: None,
|
||||
max_len: Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -805,7 +790,6 @@ fn test_cli_program_write_buffer() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: Some(2),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -843,7 +827,6 @@ fn test_cli_program_write_buffer() {
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: Some(2),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -916,7 +899,6 @@ fn test_cli_program_write_buffer() {
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: None,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
@@ -956,7 +938,6 @@ fn test_cli_program_write_buffer() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: None,
|
||||
max_len: None, //Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
config.signers = vec![&keypair, &buffer_keypair];
|
||||
@@ -970,7 +951,6 @@ fn test_cli_program_write_buffer() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let error = process_command(&config).unwrap_err();
|
||||
@@ -1028,7 +1008,6 @@ fn test_cli_program_set_buffer_authority() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: None,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
|
||||
@@ -1144,7 +1123,6 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: Some(2),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
|
||||
@@ -1167,7 +1145,6 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap_err();
|
||||
|
||||
@@ -1183,7 +1160,6 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
}
|
||||
@@ -1240,7 +1216,6 @@ fn test_cli_program_show() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: Some(2),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
|
||||
@@ -1300,7 +1275,6 @@ fn test_cli_program_show() {
|
||||
upgrade_authority_signer_index: 1,
|
||||
is_final: false,
|
||||
max_len: Some(max_len),
|
||||
skip_fee_check: false,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let min_slot = rpc_client.get_slot().unwrap();
|
||||
@@ -1427,7 +1401,6 @@ fn test_cli_program_dump() {
|
||||
buffer_pubkey: Some(buffer_keypair.pubkey()),
|
||||
buffer_authority_signer_index: Some(2),
|
||||
max_len: None,
|
||||
skip_fee_check: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
|
||||
|
@@ -18,7 +18,6 @@ use {
|
||||
solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
fee::FeeStructure,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
@@ -877,15 +876,14 @@ fn test_stake_authorize() {
|
||||
#[test]
|
||||
fn test_stake_authorize_with_fee_payer() {
|
||||
solana_logger::setup();
|
||||
let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
|
||||
let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
|
||||
const SIG_FEE: u64 = 42;
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
SIG_FEE,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
@@ -914,14 +912,14 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 5_000_000).unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 5_000_000).unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &payer_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &payer_pubkey);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 5_000_000).unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &offline_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
|
||||
@@ -936,7 +934,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
withdrawer: None,
|
||||
withdrawer_signer: None,
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(1_000_000),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
@@ -947,7 +945,8 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
from: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||
// `config` balance should be 50,000 - 1 stake account sig - 1 fee sig
|
||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||
|
||||
// Assign authority with separate fee payer
|
||||
config.signers = vec![&default_signer, &payer_keypair];
|
||||
@@ -971,10 +970,10 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config` balance has not changed, despite submitting the TX
|
||||
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||
// `config_payer` however has paid `config`'s authority sig
|
||||
// and `config_payer`'s fee sig
|
||||
check_balance!(5_000_000 - fee_two_sig, &rpc_client, &payer_pubkey);
|
||||
check_balance!(100_000 - SIG_FEE - SIG_FEE, &rpc_client, &payer_pubkey);
|
||||
|
||||
// Assign authority with offline fee payer
|
||||
let blockhash = rpc_client.get_latest_blockhash().unwrap();
|
||||
@@ -1022,10 +1021,10 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config`'s balance again has not changed
|
||||
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||
// `config_offline` however has paid 1 sig due to being both authority
|
||||
// and fee payer
|
||||
check_balance!(5_000_000 - fee_one_sig, &rpc_client, &offline_pubkey);
|
||||
check_balance!(100_000 - SIG_FEE, &rpc_client, &offline_pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1059,17 +1058,12 @@ fn test_stake_split() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config,
|
||||
&config.signers[0].pubkey(),
|
||||
50_000_000,
|
||||
)
|
||||
.unwrap();
|
||||
check_balance!(50_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
check_balance!(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 1_000_000).unwrap();
|
||||
check_balance!(1_000_000, &rpc_client, &offline_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let minimum_stake_balance = rpc_client
|
||||
@@ -1213,12 +1207,12 @@ fn test_stake_set_lockup() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 5_000_000)
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||
check_balance!(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 1_000_000).unwrap();
|
||||
check_balance!(1_000_000, &rpc_client, &offline_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let minimum_stake_balance = rpc_client
|
||||
|
@@ -16,7 +16,6 @@ use {
|
||||
solana_faucet::faucet::run_local_faucet,
|
||||
solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
fee::FeeStructure,
|
||||
native_token::sol_to_lamports,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
@@ -30,8 +29,6 @@ use {
|
||||
#[test]
|
||||
fn test_transfer() {
|
||||
solana_logger::setup();
|
||||
let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
|
||||
let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
@@ -80,11 +77,7 @@ fn test_transfer() {
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(
|
||||
sol_to_lamports(4.0) - fee_one_sig,
|
||||
&rpc_client,
|
||||
&sender_pubkey
|
||||
);
|
||||
check_balance!(sol_to_lamports(4.0) - 1, &rpc_client, &sender_pubkey);
|
||||
check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
||||
@@ -105,11 +98,7 @@ fn test_transfer() {
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
assert!(process_command(&config).is_err());
|
||||
check_balance!(
|
||||
sol_to_lamports(4.0) - fee_one_sig,
|
||||
&rpc_client,
|
||||
&sender_pubkey
|
||||
);
|
||||
check_balance!(sol_to_lamports(4.0) - 1, &rpc_client, &sender_pubkey);
|
||||
check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
|
||||
|
||||
let mut offline = CliConfig::recent_for_tests();
|
||||
@@ -165,11 +154,7 @@ fn test_transfer() {
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(
|
||||
sol_to_lamports(0.5) - fee_one_sig,
|
||||
&rpc_client,
|
||||
&offline_pubkey
|
||||
);
|
||||
check_balance!(sol_to_lamports(0.5) - 1, &rpc_client, &offline_pubkey);
|
||||
check_balance!(sol_to_lamports(1.5), &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Create nonce account
|
||||
@@ -187,7 +172,7 @@ fn test_transfer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(
|
||||
sol_to_lamports(4.0) - fee_one_sig - fee_two_sig - minimum_nonce_balance,
|
||||
sol_to_lamports(4.0) - 3 - minimum_nonce_balance,
|
||||
&rpc_client,
|
||||
&sender_pubkey,
|
||||
);
|
||||
@@ -225,7 +210,7 @@ fn test_transfer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(
|
||||
sol_to_lamports(3.0) - 2 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
|
||||
sol_to_lamports(3.0) - 4 - minimum_nonce_balance,
|
||||
&rpc_client,
|
||||
&sender_pubkey,
|
||||
);
|
||||
@@ -250,7 +235,7 @@ fn test_transfer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(
|
||||
sol_to_lamports(3.0) - 3 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
|
||||
sol_to_lamports(3.0) - 5 - minimum_nonce_balance,
|
||||
&rpc_client,
|
||||
&sender_pubkey,
|
||||
);
|
||||
@@ -308,18 +293,13 @@ fn test_transfer() {
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(
|
||||
sol_to_lamports(0.1) - 2 * fee_one_sig,
|
||||
&rpc_client,
|
||||
&offline_pubkey
|
||||
);
|
||||
check_balance!(sol_to_lamports(0.1) - 2, &rpc_client, &offline_pubkey);
|
||||
check_balance!(sol_to_lamports(2.9), &rpc_client, &recipient_pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transfer_multisession_signing() {
|
||||
solana_logger::setup();
|
||||
let fee = FeeStructure::default().get_max_fee(2, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
@@ -349,7 +329,7 @@ fn test_transfer_multisession_signing() {
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
sol_to_lamports(1.0) + 2 * fee,
|
||||
sol_to_lamports(1.0) + 3,
|
||||
)
|
||||
.unwrap();
|
||||
check_balance!(
|
||||
@@ -358,7 +338,7 @@ fn test_transfer_multisession_signing() {
|
||||
&offline_from_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(1.0) + 2 * fee,
|
||||
sol_to_lamports(1.0) + 3,
|
||||
&rpc_client,
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
);
|
||||
@@ -458,7 +438,7 @@ fn test_transfer_multisession_signing() {
|
||||
&offline_from_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(1.0) + fee,
|
||||
sol_to_lamports(1.0) + 1,
|
||||
&rpc_client,
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
);
|
||||
@@ -468,7 +448,6 @@ fn test_transfer_multisession_signing() {
|
||||
#[test]
|
||||
fn test_transfer_all() {
|
||||
solana_logger::setup();
|
||||
let fee = FeeStructure::default().get_max_fee(1, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
@@ -491,8 +470,8 @@ fn test_transfer_all() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 500_000).unwrap();
|
||||
check_balance!(500_000, &rpc_client, &sender_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
check_balance!(50_000, &rpc_client, &sender_pubkey);
|
||||
check_balance!(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
@@ -516,7 +495,7 @@ fn test_transfer_all() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(0, &rpc_client, &sender_pubkey);
|
||||
check_balance!(500_000 - fee, &rpc_client, &recipient_pubkey);
|
||||
check_balance!(49_999, &rpc_client, &recipient_pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -575,7 +554,6 @@ fn test_transfer_unfunded_recipient() {
|
||||
#[test]
|
||||
fn test_transfer_with_seed() {
|
||||
solana_logger::setup();
|
||||
let fee = FeeStructure::default().get_max_fee(1, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
@@ -634,7 +612,7 @@ fn test_transfer_with_seed() {
|
||||
derived_address_program_id: Some(derived_address_program_id),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance!(sol_to_lamports(1.0) - fee, &rpc_client, &sender_pubkey);
|
||||
check_balance!(sol_to_lamports(1.0) - 1, &rpc_client, &sender_pubkey);
|
||||
check_balance!(sol_to_lamports(5.0), &rpc_client, &recipient_pubkey);
|
||||
check_balance!(0, &rpc_client, &derived_address);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client-test"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana RPC Test"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -8,31 +8,28 @@ license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-client-test"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
futures-util = "0.3.21"
|
||||
serde_json = "1.0.79"
|
||||
serial_test = "0.6.0"
|
||||
solana-client = { path = "../client", version = "=1.10.5" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.10.5" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.5" }
|
||||
solana-perf = { path = "../perf", version = "=1.10.5" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.5" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-test-validator = { path = "../test-validator", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
serde_json = "1.0.78"
|
||||
serial_test = "0.5.1"
|
||||
solana-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.10.0" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.0" }
|
||||
solana-perf = { path = "../perf", version = "=1.10.0" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.0" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-test-validator = { path = "../test-validator", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
systemstat = "0.1.10"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,21 +1,19 @@
|
||||
use {
|
||||
futures_util::StreamExt,
|
||||
serde_json::{json, Value},
|
||||
serial_test::serial,
|
||||
solana_client::{
|
||||
nonblocking,
|
||||
pubsub_client::PubsubClient,
|
||||
rpc_client::RpcClient,
|
||||
rpc_config::{
|
||||
RpcAccountInfoConfig, RpcBlockSubscribeConfig, RpcBlockSubscribeFilter,
|
||||
RpcProgramAccountsConfig,
|
||||
},
|
||||
rpc_response::SlotInfo,
|
||||
rpc_response::{RpcBlockUpdate, SlotInfo},
|
||||
},
|
||||
solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path},
|
||||
solana_rpc::{
|
||||
optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank,
|
||||
rpc::{create_test_transaction_entries, populate_blockstore_for_tests},
|
||||
rpc::create_test_transactions_and_populate_blockstore,
|
||||
rpc_pubsub_service::{PubSubConfig, PubSubService},
|
||||
rpc_subscriptions::RpcSubscriptions,
|
||||
},
|
||||
@@ -36,9 +34,7 @@ use {
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_test_validator::TestValidator,
|
||||
solana_transaction_status::{
|
||||
BlockEncodingOptions, ConfirmedBlock, TransactionDetails, UiTransactionEncoding,
|
||||
},
|
||||
solana_transaction_status::{TransactionDetails, UiTransactionEncoding},
|
||||
std::{
|
||||
collections::HashSet,
|
||||
net::{IpAddr, SocketAddr},
|
||||
@@ -216,7 +212,6 @@ fn test_block_subscription() {
|
||||
..
|
||||
} = create_genesis_config(10_000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
|
||||
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||
|
||||
// setup Blockstore
|
||||
@@ -230,14 +225,9 @@ fn test_block_subscription() {
|
||||
let keypair2 = Keypair::new();
|
||||
let keypair3 = Keypair::new();
|
||||
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root()));
|
||||
bank.transfer(rent_exempt_amount, &alice, &keypair2.pubkey())
|
||||
.unwrap();
|
||||
populate_blockstore_for_tests(
|
||||
create_test_transaction_entries(
|
||||
vec![&alice, &keypair1, &keypair2, &keypair3],
|
||||
bank.clone(),
|
||||
)
|
||||
.0,
|
||||
let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
|
||||
vec![&alice, &keypair1, &keypair2, &keypair3],
|
||||
0,
|
||||
bank,
|
||||
blockstore.clone(),
|
||||
max_complete_transaction_status_slot,
|
||||
@@ -275,7 +265,6 @@ fn test_block_subscription() {
|
||||
encoding: Some(UiTransactionEncoding::Json),
|
||||
transaction_details: Some(TransactionDetails::Signatures),
|
||||
show_rewards: None,
|
||||
max_supported_transaction_version: None,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -287,18 +276,23 @@ fn test_block_subscription() {
|
||||
match maybe_actual {
|
||||
Ok(actual) => {
|
||||
let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let confirmed_block = ConfirmedBlock::from(versioned_block);
|
||||
let block = confirmed_block
|
||||
.encode_with_options(
|
||||
UiTransactionEncoding::Json,
|
||||
BlockEncodingOptions {
|
||||
transaction_details: TransactionDetails::Signatures,
|
||||
show_rewards: false,
|
||||
max_supported_transaction_version: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(actual.value.slot, slot);
|
||||
let legacy_block = versioned_block.into_legacy_block().unwrap();
|
||||
let block = legacy_block.clone().configure(
|
||||
UiTransactionEncoding::Json,
|
||||
TransactionDetails::Signatures,
|
||||
false,
|
||||
);
|
||||
let expected = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
err: None,
|
||||
};
|
||||
let block = legacy_block.configure(
|
||||
UiTransactionEncoding::Json,
|
||||
TransactionDetails::Signatures,
|
||||
false,
|
||||
);
|
||||
assert_eq!(actual.value.slot, expected.slot);
|
||||
assert!(block.eq(&actual.value.block.unwrap()));
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -520,86 +514,3 @@ fn test_slot_subscription() {
|
||||
|
||||
assert_eq!(errors, [].to_vec());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_slot_subscription_async() {
|
||||
let sync_service = Arc::new(AtomicU64::new(0));
|
||||
let sync_client = Arc::clone(&sync_service);
|
||||
fn wait_until(atomic: &Arc<AtomicU64>, value: u64) {
|
||||
while atomic.load(Ordering::Relaxed) != value {
|
||||
sleep(Duration::from_millis(1))
|
||||
}
|
||||
}
|
||||
|
||||
let pubsub_addr = SocketAddr::new(
|
||||
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||
rpc_port::DEFAULT_RPC_PUBSUB_PORT,
|
||||
);
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||
let optimistically_confirmed_bank =
|
||||
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
|
||||
let max_complete_transaction_status_slot = Arc::new(AtomicU64::default());
|
||||
let subscriptions = Arc::new(RpcSubscriptions::new_for_tests(
|
||||
&exit,
|
||||
max_complete_transaction_status_slot,
|
||||
bank_forks,
|
||||
Arc::new(RwLock::new(BlockCommitmentCache::default())),
|
||||
optimistically_confirmed_bank,
|
||||
));
|
||||
let (trigger, pubsub_service) =
|
||||
PubSubService::new(PubSubConfig::default(), &subscriptions, pubsub_addr);
|
||||
sleep(Duration::from_millis(100));
|
||||
sync_service.store(1, Ordering::Relaxed);
|
||||
|
||||
wait_until(&sync_service, 2);
|
||||
subscriptions.notify_slot(1, 0, 0);
|
||||
sync_service.store(3, Ordering::Relaxed);
|
||||
|
||||
wait_until(&sync_service, 4);
|
||||
subscriptions.notify_slot(2, 1, 1);
|
||||
sync_service.store(5, Ordering::Relaxed);
|
||||
|
||||
wait_until(&sync_service, 6);
|
||||
exit.store(true, Ordering::Relaxed);
|
||||
trigger.cancel();
|
||||
pubsub_service.close().unwrap();
|
||||
});
|
||||
|
||||
wait_until(&sync_client, 1);
|
||||
let url = format!("ws://0.0.0.0:{}/", pubsub_addr.port());
|
||||
let pubsub_client = nonblocking::pubsub_client::PubsubClient::new(&url)
|
||||
.await
|
||||
.unwrap();
|
||||
let (mut notifications, unsubscribe) = pubsub_client.slot_subscribe().await.unwrap();
|
||||
sync_client.store(2, Ordering::Relaxed);
|
||||
|
||||
wait_until(&sync_client, 3);
|
||||
assert_eq!(
|
||||
tokio::time::timeout(Duration::from_millis(25), notifications.next()).await,
|
||||
Ok(Some(SlotInfo {
|
||||
slot: 1,
|
||||
parent: 0,
|
||||
root: 0,
|
||||
}))
|
||||
);
|
||||
sync_client.store(4, Ordering::Relaxed);
|
||||
|
||||
wait_until(&sync_client, 5);
|
||||
assert_eq!(
|
||||
tokio::time::timeout(Duration::from_millis(25), notifications.next()).await,
|
||||
Ok(Some(SlotInfo {
|
||||
slot: 2,
|
||||
parent: 1,
|
||||
root: 1,
|
||||
}))
|
||||
);
|
||||
sync_client.store(6, Ordering::Relaxed);
|
||||
|
||||
unsubscribe().await;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,52 +10,39 @@ license = "Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-mutex = "1.4.0"
|
||||
async-trait = "0.1.52"
|
||||
base64 = "0.13.0"
|
||||
bincode = "1.3.3"
|
||||
bs58 = "0.4.0"
|
||||
bytes = "1.1.0"
|
||||
clap = "2.33.0"
|
||||
crossbeam-channel = "0.5"
|
||||
futures = "0.3"
|
||||
futures-util = "0.3.21"
|
||||
indicatif = "0.16.2"
|
||||
itertools = "0.10.2"
|
||||
jsonrpc-core = "18.0.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.14"
|
||||
quinn = "0.8.0"
|
||||
rand = "0.7.0"
|
||||
rand_chacha = "0.2.2"
|
||||
rayon = "1.5.1"
|
||||
reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
rustls = { version = "0.20.2", features = ["dangerous_configuration"] }
|
||||
semver = "1.0.6"
|
||||
serde = "1.0.136"
|
||||
reqwest = { version = "0.11.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
semver = "1.0.4"
|
||||
serde = "1.0.134"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.79"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.5" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.5" }
|
||||
serde_json = "1.0.78"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.10.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = "0.1.8"
|
||||
tokio-tungstenite = { version = "0.17.1", features = ["rustls-tls-webpki-roots"] }
|
||||
tungstenite = { version = "0.17.2", features = ["rustls-tls-webpki-roots"] }
|
||||
tungstenite = { version = "0.16.0", features = ["rustls-tls-webpki-roots"] }
|
||||
url = "2.2.2"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
jsonrpc-http-server = "18.0.0"
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,7 +1,6 @@
|
||||
pub use reqwest;
|
||||
use {
|
||||
crate::{rpc_request, rpc_response},
|
||||
quinn::{ConnectError, WriteError},
|
||||
solana_faucet::faucet::FaucetError,
|
||||
solana_sdk::{
|
||||
signature::SignerError, transaction::TransactionError, transport::TransportError,
|
||||
@@ -73,18 +72,6 @@ impl From<ClientErrorKind> for TransportError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WriteError> for ClientErrorKind {
|
||||
fn from(write_error: WriteError) -> Self {
|
||||
Self::Custom(format!("{:?}", write_error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConnectError> for ClientErrorKind {
|
||||
fn from(connect_error: ConnectError) -> Self {
|
||||
Self::Custom(format!("{:?}", connect_error))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{kind}")]
|
||||
pub struct ClientError {
|
||||
|
@@ -1,241 +0,0 @@
|
||||
use {
|
||||
crate::{
|
||||
quic_client::QuicTpuConnection, tpu_connection::TpuConnection, udp_client::UdpTpuConnection,
|
||||
},
|
||||
lazy_static::lazy_static,
|
||||
solana_sdk::{transaction::VersionedTransaction, transport::TransportError},
|
||||
std::{
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap},
|
||||
net::{SocketAddr, UdpSocket},
|
||||
sync::{Arc, Mutex},
|
||||
},
|
||||
};
|
||||
|
||||
// Should be non-zero
|
||||
static MAX_CONNECTIONS: usize = 64;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Connection {
|
||||
Udp(Arc<UdpTpuConnection>),
|
||||
Quic(Arc<QuicTpuConnection>),
|
||||
}
|
||||
|
||||
struct ConnMap {
|
||||
// Keeps track of the connection associated with an addr and the last time it was used
|
||||
map: HashMap<SocketAddr, (Connection, u64)>,
|
||||
// Helps to find the least recently used connection. The search and inserts are O(log(n))
|
||||
// but since we're bounding the size of the collections, this should be constant
|
||||
// (and hopefully negligible) time. In theory, we can do this in constant time
|
||||
// with a queue implemented as a doubly-linked list (and all the
|
||||
// HashMap entries holding a "pointer" to the corresponding linked-list node),
|
||||
// so we can push, pop and bump a used connection back to the end of the queue in O(1) time, but
|
||||
// that seems non-"Rust-y" and low bang/buck. This is still pretty terrible though...
|
||||
last_used_times: BTreeMap<u64, SocketAddr>,
|
||||
ticks: u64,
|
||||
use_quic: bool,
|
||||
}
|
||||
|
||||
impl ConnMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
last_used_times: BTreeMap::new(),
|
||||
ticks: 0,
|
||||
use_quic: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_use_quic(&mut self, use_quic: bool) {
|
||||
self.use_quic = use_quic;
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CONNECTION_MAP: Mutex<ConnMap> = Mutex::new(ConnMap::new());
|
||||
}
|
||||
|
||||
pub fn set_use_quic(use_quic: bool) {
|
||||
let mut map = (*CONNECTION_MAP).lock().unwrap();
|
||||
map.set_use_quic(use_quic);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
// TODO: see https://github.com/solana-labs/solana/issues/23661
|
||||
// remove lazy_static and optimize and refactor this
|
||||
fn get_connection(addr: &SocketAddr) -> Connection {
|
||||
let mut map = (*CONNECTION_MAP).lock().unwrap();
|
||||
let ticks = map.ticks;
|
||||
let use_quic = map.use_quic;
|
||||
let (conn, target_ticks) = match map.map.entry(*addr) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
let mut pair = entry.get_mut();
|
||||
let old_ticks = pair.1;
|
||||
pair.1 = ticks;
|
||||
(pair.0.clone(), old_ticks)
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
let send_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
// TODO: see https://github.com/solana-labs/solana/issues/23659
|
||||
// make it configurable (e.g. via the command line) whether to use UDP or Quic
|
||||
|
||||
let conn = if use_quic {
|
||||
Connection::Quic(Arc::new(QuicTpuConnection::new(send_socket, *addr)))
|
||||
} else {
|
||||
Connection::Udp(Arc::new(UdpTpuConnection::new(send_socket, *addr)))
|
||||
};
|
||||
|
||||
entry.insert((conn.clone(), ticks));
|
||||
(conn, ticks)
|
||||
}
|
||||
};
|
||||
|
||||
let num_connections = map.map.len();
|
||||
if num_connections > MAX_CONNECTIONS {
|
||||
let (old_ticks, target_addr) = {
|
||||
let (old_ticks, target_addr) = map.last_used_times.iter().next().unwrap();
|
||||
(*old_ticks, *target_addr)
|
||||
};
|
||||
map.map.remove(&target_addr);
|
||||
map.last_used_times.remove(&old_ticks);
|
||||
}
|
||||
|
||||
if target_ticks != ticks {
|
||||
map.last_used_times.remove(&target_ticks);
|
||||
}
|
||||
map.last_used_times.insert(ticks, *addr);
|
||||
|
||||
map.ticks += 1;
|
||||
conn
|
||||
}
|
||||
|
||||
// TODO: see https://github.com/solana-labs/solana/issues/23851
|
||||
// use enum_dispatch and get rid of this tedious code.
|
||||
// The main blocker to using enum_dispatch right now is that
|
||||
// the it doesn't work with static methods like TpuConnection::new
|
||||
// which is used by thin_client. This will be eliminated soon
|
||||
// once thin_client is moved to using this connection cache.
|
||||
// Once that is done, we will migrate to using enum_dispatch
|
||||
// This will be done in a followup to
|
||||
// https://github.com/solana-labs/solana/pull/23817
|
||||
pub fn send_wire_transaction_batch(
|
||||
packets: &[&[u8]],
|
||||
addr: &SocketAddr,
|
||||
) -> Result<(), TransportError> {
|
||||
let conn = get_connection(addr);
|
||||
match conn {
|
||||
Connection::Udp(conn) => conn.send_wire_transaction_batch(packets),
|
||||
Connection::Quic(conn) => conn.send_wire_transaction_batch(packets),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_wire_transaction(
|
||||
wire_transaction: &[u8],
|
||||
addr: &SocketAddr,
|
||||
) -> Result<(), TransportError> {
|
||||
let conn = get_connection(addr);
|
||||
match conn {
|
||||
Connection::Udp(conn) => conn.send_wire_transaction(wire_transaction),
|
||||
Connection::Quic(conn) => conn.send_wire_transaction(wire_transaction),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_and_send_transaction(
|
||||
transaction: &VersionedTransaction,
|
||||
addr: &SocketAddr,
|
||||
) -> Result<(), TransportError> {
|
||||
let conn = get_connection(addr);
|
||||
match conn {
|
||||
Connection::Udp(conn) => conn.serialize_and_send_transaction(transaction),
|
||||
Connection::Quic(conn) => conn.serialize_and_send_transaction(transaction),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_serialize_and_send_transaction_batch(
|
||||
transactions: &[VersionedTransaction],
|
||||
addr: &SocketAddr,
|
||||
) -> Result<(), TransportError> {
|
||||
let conn = get_connection(addr);
|
||||
match conn {
|
||||
Connection::Udp(conn) => conn.par_serialize_and_send_transaction_batch(transactions),
|
||||
Connection::Quic(conn) => conn.par_serialize_and_send_transaction_batch(transactions),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {
|
||||
crate::{
|
||||
connection_cache::{get_connection, Connection, CONNECTION_MAP, MAX_CONNECTIONS},
|
||||
tpu_connection::TpuConnection,
|
||||
},
|
||||
rand::{Rng, SeedableRng},
|
||||
rand_chacha::ChaChaRng,
|
||||
std::net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
fn get_addr(rng: &mut ChaChaRng) -> SocketAddr {
|
||||
let a = rng.gen_range(1, 255);
|
||||
let b = rng.gen_range(1, 255);
|
||||
let c = rng.gen_range(1, 255);
|
||||
let d = rng.gen_range(1, 255);
|
||||
|
||||
let addr_str = format!("{}.{}.{}.{}:80", a, b, c, d);
|
||||
|
||||
addr_str.parse().expect("Invalid address")
|
||||
}
|
||||
|
||||
fn ip(conn: Connection) -> IpAddr {
|
||||
match conn {
|
||||
Connection::Udp(conn) => conn.tpu_addr().ip(),
|
||||
Connection::Quic(conn) => conn.tpu_addr().ip(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connection_cache() {
|
||||
// Allow the test to run deterministically
|
||||
// with the same pseudorandom sequence between runs
|
||||
// and on different platforms - the cryptographic security
|
||||
// property isn't important here but ChaChaRng provides a way
|
||||
// to get the same pseudorandom sequence on different platforms
|
||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||
|
||||
// Generate a bunch of random addresses and create TPUConnections to them
|
||||
// Since TPUConnection::new is infallible, it should't matter whether or not
|
||||
// we can actually connect to those addresses - TPUConnection implementations should either
|
||||
// be lazy and not connect until first use or handle connection errors somehow
|
||||
// (without crashing, as would be required in a real practical validator)
|
||||
let first_addr = get_addr(&mut rng);
|
||||
assert!(ip(get_connection(&first_addr)) == first_addr.ip());
|
||||
let addrs = (0..MAX_CONNECTIONS)
|
||||
.into_iter()
|
||||
.map(|_| {
|
||||
let addr = get_addr(&mut rng);
|
||||
get_connection(&addr);
|
||||
addr
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
{
|
||||
let map = (*CONNECTION_MAP).lock().unwrap();
|
||||
addrs.iter().for_each(|a| {
|
||||
let conn = map.map.get(a).expect("Address not found");
|
||||
assert!(a.ip() == ip(conn.0.clone()));
|
||||
});
|
||||
|
||||
assert!(map.map.get(&first_addr).is_none());
|
||||
}
|
||||
|
||||
// Test that get_connection updates which connection is next up for eviction
|
||||
// when an existing connection is used. Initially, addrs[0] should be next up for eviction, since
|
||||
// it was the earliest added. But we do get_connection(&addrs[0]), thereby using
|
||||
// that connection, and bumping it back to the end of the queue. So addrs[1] should be
|
||||
// the next up for eviction. So we add a new connection, and test that addrs[0] is not
|
||||
// evicted but addrs[1] is.
|
||||
get_connection(&addrs[0]);
|
||||
get_connection(&get_addr(&mut rng));
|
||||
|
||||
let map = (*CONNECTION_MAP).lock().unwrap();
|
||||
assert!(map.map.get(&addrs[0]).is_some());
|
||||
assert!(map.map.get(&addrs[1]).is_none());
|
||||
}
|
||||
}
|
@@ -38,14 +38,14 @@ impl HttpSender {
|
||||
///
|
||||
/// The URL is an HTTP URL, usually for port 8899, as in
|
||||
/// "http://localhost:8899". The sender has a default timeout of 30 seconds.
|
||||
pub fn new<U: ToString>(url: U) -> Self {
|
||||
pub fn new(url: String) -> Self {
|
||||
Self::new_with_timeout(url, Duration::from_secs(30))
|
||||
}
|
||||
|
||||
/// Create an HTTP RPC sender.
|
||||
///
|
||||
/// The URL is an HTTP URL, usually for port 8899.
|
||||
pub fn new_with_timeout<U: ToString>(url: U, timeout: Duration) -> Self {
|
||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||
let client = Arc::new(
|
||||
reqwest::Client::builder()
|
||||
.timeout(timeout)
|
||||
@@ -55,7 +55,7 @@ impl HttpSender {
|
||||
|
||||
Self {
|
||||
client,
|
||||
url: url.to_string(),
|
||||
url,
|
||||
request_id: AtomicU64::new(0),
|
||||
stats: RwLock::new(RpcTransportStats::default()),
|
||||
}
|
||||
@@ -63,9 +63,9 @@ impl HttpSender {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub(crate) struct RpcErrorObject {
|
||||
pub code: i64,
|
||||
pub message: String,
|
||||
struct RpcErrorObject {
|
||||
code: i64,
|
||||
message: String,
|
||||
}
|
||||
|
||||
struct StatsUpdater<'a> {
|
||||
|
@@ -4,14 +4,12 @@ extern crate serde_derive;
|
||||
|
||||
pub mod blockhash_query;
|
||||
pub mod client_error;
|
||||
pub mod connection_cache;
|
||||
pub(crate) mod http_sender;
|
||||
pub(crate) mod mock_sender;
|
||||
pub mod nonblocking;
|
||||
pub mod nonce_utils;
|
||||
pub mod perf_utils;
|
||||
pub mod pubsub_client;
|
||||
pub mod quic_client;
|
||||
pub mod rpc_cache;
|
||||
pub mod rpc_client;
|
||||
pub mod rpc_config;
|
||||
@@ -20,13 +18,11 @@ pub mod rpc_deprecated_config;
|
||||
pub mod rpc_filter;
|
||||
pub mod rpc_request;
|
||||
pub mod rpc_response;
|
||||
pub mod rpc_sender;
|
||||
pub(crate) mod rpc_sender;
|
||||
pub mod spinner;
|
||||
pub mod thin_client;
|
||||
pub mod tpu_client;
|
||||
pub mod tpu_connection;
|
||||
pub mod transaction_executor;
|
||||
pub mod udp_client;
|
||||
|
||||
pub mod mock_sender_for_cli {
|
||||
/// Magic `SIGNATURE` value used by `solana-cli` unit tests.
|
||||
|
@@ -28,13 +28,13 @@ use {
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
sysvar::epoch_schedule::EpochSchedule,
|
||||
transaction::{self, Transaction, TransactionError, TransactionVersion},
|
||||
transaction::{self, Transaction, TransactionError},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction,
|
||||
EncodedTransactionWithStatusMeta, Rewards, TransactionBinaryEncoding,
|
||||
TransactionConfirmationStatus, TransactionStatus, UiCompiledInstruction, UiMessage,
|
||||
UiRawMessage, UiTransaction, UiTransactionStatusMeta,
|
||||
EncodedTransactionWithStatusMeta, Rewards, TransactionConfirmationStatus,
|
||||
TransactionStatus, UiCompiledInstruction, UiMessage, UiRawMessage, UiTransaction,
|
||||
UiTransactionEncoding, UiTransactionStatusMeta,
|
||||
},
|
||||
solana_version::Version,
|
||||
std::{collections::HashMap, net::SocketAddr, str::FromStr, sync::RwLock},
|
||||
@@ -74,13 +74,13 @@ pub struct MockSender {
|
||||
/// from [`RpcRequest`] to a JSON [`Value`] response, Any entries in this map
|
||||
/// override the default behavior for the given request.
|
||||
impl MockSender {
|
||||
pub fn new<U: ToString>(url: U) -> Self {
|
||||
pub fn new(url: String) -> Self {
|
||||
Self::new_with_mocks(url, Mocks::default())
|
||||
}
|
||||
|
||||
pub fn new_with_mocks<U: ToString>(url: U, mocks: Mocks) -> Self {
|
||||
pub fn new_with_mocks(url: String, mocks: Mocks) -> Self {
|
||||
Self {
|
||||
url: url.to_string(),
|
||||
url,
|
||||
mocks: RwLock::new(mocks),
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,6 @@ impl RpcSender for MockSender {
|
||||
"getTransaction" => serde_json::to_value(EncodedConfirmedTransactionWithStatusMeta {
|
||||
slot: 2,
|
||||
transaction: EncodedTransactionWithStatusMeta {
|
||||
version: Some(TransactionVersion::LEGACY),
|
||||
transaction: EncodedTransaction::Json(
|
||||
UiTransaction {
|
||||
signatures: vec!["3AsdoALgZFuq2oUVWrDYhg2pNeaLJKPLf8hU2mQ6U8qJxeJ6hsrPVpMn9ma39DtfYCrDQSvngWRP8NnTpEhezJpE".to_string()],
|
||||
@@ -214,7 +213,6 @@ impl RpcSender for MockSender {
|
||||
accounts: vec![0, 1],
|
||||
data: "3Bxs49DitAvXtoDR".to_string(),
|
||||
}],
|
||||
address_table_lookups: None,
|
||||
})
|
||||
}),
|
||||
meta: Some(UiTransactionStatusMeta {
|
||||
@@ -228,7 +226,6 @@ impl RpcSender for MockSender {
|
||||
pre_token_balances: None,
|
||||
post_token_balances: None,
|
||||
rewards: None,
|
||||
loaded_addresses: None,
|
||||
}),
|
||||
},
|
||||
block_time: Some(1628633791),
|
||||
@@ -381,10 +378,9 @@ impl RpcSender for MockSender {
|
||||
pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\
|
||||
hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK"
|
||||
.to_string(),
|
||||
TransactionBinaryEncoding::Base58,
|
||||
UiTransactionEncoding::Base58,
|
||||
),
|
||||
meta: None,
|
||||
version: Some(TransactionVersion::LEGACY),
|
||||
}],
|
||||
rewards: Rewards::new(),
|
||||
block_time: None,
|
||||
|
@@ -1,2 +1 @@
|
||||
pub mod pubsub_client;
|
||||
pub mod rpc_client;
|
||||
|
@@ -1,341 +0,0 @@
|
||||
use {
|
||||
crate::{
|
||||
http_sender::RpcErrorObject,
|
||||
rpc_config::{
|
||||
RpcAccountInfoConfig, RpcBlockSubscribeConfig, RpcBlockSubscribeFilter,
|
||||
RpcProgramAccountsConfig, RpcSignatureSubscribeConfig, RpcTransactionLogsConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_response::{
|
||||
Response as RpcResponse, RpcBlockUpdate, RpcKeyedAccount, RpcLogsResponse,
|
||||
RpcSignatureResult, RpcVote, SlotInfo, SlotUpdate,
|
||||
},
|
||||
},
|
||||
futures_util::{
|
||||
future::{ready, BoxFuture, FutureExt},
|
||||
sink::SinkExt,
|
||||
stream::{BoxStream, StreamExt},
|
||||
},
|
||||
log::*,
|
||||
serde::de::DeserializeOwned,
|
||||
serde_json::{json, Map, Value},
|
||||
solana_account_decoder::UiAccount,
|
||||
solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature},
|
||||
std::collections::BTreeMap,
|
||||
thiserror::Error,
|
||||
tokio::{
|
||||
net::TcpStream,
|
||||
sync::{mpsc, oneshot},
|
||||
task::JoinHandle,
|
||||
time::{sleep, Duration},
|
||||
},
|
||||
tokio_stream::wrappers::UnboundedReceiverStream,
|
||||
tokio_tungstenite::{
|
||||
connect_async,
|
||||
tungstenite::{
|
||||
protocol::frame::{coding::CloseCode, CloseFrame},
|
||||
Message,
|
||||
},
|
||||
MaybeTlsStream, WebSocketStream,
|
||||
},
|
||||
url::Url,
|
||||
};
|
||||
|
||||
pub type PubsubClientResult<T = ()> = Result<T, PubsubClientError>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PubsubClientError {
|
||||
#[error("url parse error")]
|
||||
UrlParseError(#[from] url::ParseError),
|
||||
|
||||
#[error("unable to connect to server")]
|
||||
ConnectionError(tokio_tungstenite::tungstenite::Error),
|
||||
|
||||
#[error("websocket error")]
|
||||
WsError(#[from] tokio_tungstenite::tungstenite::Error),
|
||||
|
||||
#[error("connection closed (({0})")]
|
||||
ConnectionClosed(String),
|
||||
|
||||
#[error("json parse error")]
|
||||
JsonParseError(#[from] serde_json::error::Error),
|
||||
|
||||
#[error("subscribe failed: {reason}")]
|
||||
SubscribeFailed { reason: String, message: String },
|
||||
}
|
||||
|
||||
type UnsubscribeFn = Box<dyn FnOnce() -> BoxFuture<'static, ()> + Send>;
|
||||
type SubscribeResponseMsg =
|
||||
Result<(mpsc::UnboundedReceiver<Value>, UnsubscribeFn), PubsubClientError>;
|
||||
type SubscribeRequestMsg = (String, Value, oneshot::Sender<SubscribeResponseMsg>);
|
||||
type SubscribeResult<'a, T> = PubsubClientResult<(BoxStream<'a, T>, UnsubscribeFn)>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PubsubClient {
|
||||
subscribe_tx: mpsc::UnboundedSender<SubscribeRequestMsg>,
|
||||
shutdown_tx: oneshot::Sender<()>,
|
||||
ws: JoinHandle<PubsubClientResult>,
|
||||
}
|
||||
|
||||
impl PubsubClient {
|
||||
pub async fn new(url: &str) -> PubsubClientResult<Self> {
|
||||
let url = Url::parse(url)?;
|
||||
let (ws, _response) = connect_async(url)
|
||||
.await
|
||||
.map_err(PubsubClientError::ConnectionError)?;
|
||||
|
||||
let (subscribe_tx, subscribe_rx) = mpsc::unbounded_channel();
|
||||
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
||||
|
||||
Ok(Self {
|
||||
subscribe_tx,
|
||||
shutdown_tx,
|
||||
ws: tokio::spawn(PubsubClient::run_ws(ws, subscribe_rx, shutdown_rx)),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn shutdown(self) -> PubsubClientResult {
|
||||
let _ = self.shutdown_tx.send(());
|
||||
self.ws.await.unwrap() // WS future should not be cancelled or panicked
|
||||
}
|
||||
|
||||
async fn subscribe<'a, T>(&self, operation: &str, params: Value) -> SubscribeResult<'a, T>
|
||||
where
|
||||
T: DeserializeOwned + Send + 'a,
|
||||
{
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
self.subscribe_tx
|
||||
.send((operation.to_string(), params, response_tx))
|
||||
.map_err(|err| PubsubClientError::ConnectionClosed(err.to_string()))?;
|
||||
|
||||
let (notifications, unsubscribe) = response_rx
|
||||
.await
|
||||
.map_err(|err| PubsubClientError::ConnectionClosed(err.to_string()))??;
|
||||
Ok((
|
||||
UnboundedReceiverStream::new(notifications)
|
||||
.filter_map(|value| ready(serde_json::from_value::<T>(value).ok()))
|
||||
.boxed(),
|
||||
unsubscribe,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn account_subscribe(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
config: Option<RpcAccountInfoConfig>,
|
||||
) -> SubscribeResult<'_, RpcResponse<UiAccount>> {
|
||||
let params = json!([pubkey.to_string(), config]);
|
||||
self.subscribe("account", params).await
|
||||
}
|
||||
|
||||
pub async fn block_subscribe(
|
||||
&self,
|
||||
filter: RpcBlockSubscribeFilter,
|
||||
config: Option<RpcBlockSubscribeConfig>,
|
||||
) -> SubscribeResult<'_, RpcResponse<RpcBlockUpdate>> {
|
||||
self.subscribe("block", json!([filter, config])).await
|
||||
}
|
||||
|
||||
pub async fn logs_subscribe(
|
||||
&self,
|
||||
filter: RpcTransactionLogsFilter,
|
||||
config: RpcTransactionLogsConfig,
|
||||
) -> SubscribeResult<'_, RpcResponse<RpcLogsResponse>> {
|
||||
self.subscribe("logs", json!([filter, config])).await
|
||||
}
|
||||
|
||||
pub async fn program_subscribe(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
config: Option<RpcProgramAccountsConfig>,
|
||||
) -> SubscribeResult<'_, RpcResponse<RpcKeyedAccount>> {
|
||||
let params = json!([pubkey.to_string(), config]);
|
||||
self.subscribe("program", params).await
|
||||
}
|
||||
|
||||
pub async fn vote_subscribe(&self) -> SubscribeResult<'_, RpcVote> {
|
||||
self.subscribe("vote", json!([])).await
|
||||
}
|
||||
|
||||
pub async fn root_subscribe(&self) -> SubscribeResult<'_, Slot> {
|
||||
self.subscribe("root", json!([])).await
|
||||
}
|
||||
|
||||
pub async fn signature_subscribe(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
config: Option<RpcSignatureSubscribeConfig>,
|
||||
) -> SubscribeResult<'_, RpcResponse<RpcSignatureResult>> {
|
||||
let params = json!([signature.to_string(), config]);
|
||||
self.subscribe("signature", params).await
|
||||
}
|
||||
|
||||
pub async fn slot_subscribe(&self) -> SubscribeResult<'_, SlotInfo> {
|
||||
self.subscribe("slot", json!([])).await
|
||||
}
|
||||
|
||||
pub async fn slot_updates_subscribe(&self) -> SubscribeResult<'_, SlotUpdate> {
|
||||
self.subscribe("slotsUpdates", json!([])).await
|
||||
}
|
||||
|
||||
async fn run_ws(
|
||||
mut ws: WebSocketStream<MaybeTlsStream<TcpStream>>,
|
||||
mut subscribe_rx: mpsc::UnboundedReceiver<SubscribeRequestMsg>,
|
||||
mut shutdown_rx: oneshot::Receiver<()>,
|
||||
) -> PubsubClientResult {
|
||||
let mut request_id: u64 = 0;
|
||||
|
||||
let mut requests_subscribe = BTreeMap::new();
|
||||
let mut requests_unsubscribe = BTreeMap::<u64, oneshot::Sender<()>>::new();
|
||||
let mut subscriptions = BTreeMap::new();
|
||||
let (unsubscribe_tx, mut unsubscribe_rx) = mpsc::unbounded_channel();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Send close on shutdown signal
|
||||
_ = (&mut shutdown_rx) => {
|
||||
let frame = CloseFrame { code: CloseCode::Normal, reason: "".into() };
|
||||
ws.send(Message::Close(Some(frame))).await?;
|
||||
ws.flush().await?;
|
||||
break;
|
||||
},
|
||||
// Send `Message::Ping` each 10s if no any other communication
|
||||
() = sleep(Duration::from_secs(10)) => {
|
||||
ws.send(Message::Ping(Vec::new())).await?;
|
||||
},
|
||||
// Read message for subscribe
|
||||
Some((operation, params, response_tx)) = subscribe_rx.recv() => {
|
||||
request_id += 1;
|
||||
let method = format!("{}Subscribe", operation);
|
||||
let text = json!({"jsonrpc":"2.0","id":request_id,"method":method,"params":params}).to_string();
|
||||
ws.send(Message::Text(text)).await?;
|
||||
requests_subscribe.insert(request_id, (operation, response_tx));
|
||||
},
|
||||
// Read message for unsubscribe
|
||||
Some((operation, sid, response_tx)) = unsubscribe_rx.recv() => {
|
||||
subscriptions.remove(&sid);
|
||||
request_id += 1;
|
||||
let method = format!("{}Unsubscribe", operation);
|
||||
let text = json!({"jsonrpc":"2.0","id":request_id,"method":method,"params":[sid]}).to_string();
|
||||
ws.send(Message::Text(text)).await?;
|
||||
requests_unsubscribe.insert(request_id, response_tx);
|
||||
},
|
||||
// Read incoming WebSocket message
|
||||
next_msg = ws.next() => {
|
||||
let msg = match next_msg {
|
||||
Some(msg) => msg?,
|
||||
None => break,
|
||||
};
|
||||
trace!("ws.next(): {:?}", &msg);
|
||||
|
||||
// Get text from the message
|
||||
let text = match msg {
|
||||
Message::Text(text) => text,
|
||||
Message::Binary(_data) => continue, // Ignore
|
||||
Message::Ping(data) => {
|
||||
ws.send(Message::Pong(data)).await?;
|
||||
continue
|
||||
},
|
||||
Message::Pong(_data) => continue,
|
||||
Message::Close(_frame) => break,
|
||||
Message::Frame(_frame) => continue,
|
||||
};
|
||||
|
||||
|
||||
let mut json: Map<String, Value> = serde_json::from_str(&text)?;
|
||||
|
||||
// Subscribe/Unsubscribe response, example:
|
||||
// `{"jsonrpc":"2.0","result":5308752,"id":1}`
|
||||
if let Some(id) = json.get("id") {
|
||||
let id = id.as_u64().ok_or_else(|| {
|
||||
PubsubClientError::SubscribeFailed { reason: "invalid `id` field".into(), message: text.clone() }
|
||||
})?;
|
||||
|
||||
let err = json.get("error").map(|error_object| {
|
||||
match serde_json::from_value::<RpcErrorObject>(error_object.clone()) {
|
||||
Ok(rpc_error_object) => {
|
||||
format!("{} ({})", rpc_error_object.message, rpc_error_object.code)
|
||||
}
|
||||
Err(err) => format!(
|
||||
"Failed to deserialize RPC error response: {} [{}]",
|
||||
serde_json::to_string(error_object).unwrap(),
|
||||
err
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(response_tx) = requests_unsubscribe.remove(&id) {
|
||||
let _ = response_tx.send(()); // do not care if receiver is closed
|
||||
} else if let Some((operation, response_tx)) = requests_subscribe.remove(&id) {
|
||||
match err {
|
||||
Some(reason) => {
|
||||
let _ = response_tx.send(Err(PubsubClientError::SubscribeFailed { reason, message: text.clone()}));
|
||||
},
|
||||
None => {
|
||||
// Subscribe Id
|
||||
let sid = json.get("result").and_then(Value::as_u64).ok_or_else(|| {
|
||||
PubsubClientError::SubscribeFailed { reason: "invalid `result` field".into(), message: text.clone() }
|
||||
})?;
|
||||
|
||||
// Create notifications channel and unsubscribe function
|
||||
let (notifications_tx, notifications_rx) = mpsc::unbounded_channel();
|
||||
let unsubscribe_tx = unsubscribe_tx.clone();
|
||||
let unsubscribe = Box::new(move || async move {
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
// do nothing if ws already closed
|
||||
if unsubscribe_tx.send((operation, sid, response_tx)).is_ok() {
|
||||
let _ = response_rx.await; // channel can be closed only if ws is closed
|
||||
}
|
||||
}.boxed());
|
||||
|
||||
if response_tx.send(Ok((notifications_rx, unsubscribe))).is_err() {
|
||||
break;
|
||||
}
|
||||
subscriptions.insert(sid, notifications_tx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Unknown request id: {}", id);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Notification, example:
|
||||
// `{"jsonrpc":"2.0","method":"logsNotification","params":{"result":{...},"subscription":3114862}}`
|
||||
if let Some(Value::Object(params)) = json.get_mut("params") {
|
||||
if let Some(sid) = params.get("subscription").and_then(Value::as_u64) {
|
||||
let mut unsubscribe_required = false;
|
||||
|
||||
if let Some(notifications_tx) = subscriptions.get(&sid) {
|
||||
if let Some(result) = params.remove("result") {
|
||||
if notifications_tx.send(result).is_err() {
|
||||
unsubscribe_required = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unsubscribe_required = true;
|
||||
}
|
||||
|
||||
if unsubscribe_required {
|
||||
if let Some(Value::String(method)) = json.remove("method") {
|
||||
if let Some(operation) = method.strip_suffix("Notification") {
|
||||
let (response_tx, _response_rx) = oneshot::channel();
|
||||
let _ = unsubscribe_tx.send((operation.to_string(), sid, response_tx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// see client-test/test/client.rs
|
||||
}
|
@@ -6,7 +6,6 @@
|
||||
//!
|
||||
//! [JSON-RPC]: https://www.jsonrpc.org/specification
|
||||
|
||||
pub use crate::mock_sender::Mocks;
|
||||
#[allow(deprecated)]
|
||||
use crate::rpc_deprecated_config::{
|
||||
RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
|
||||
@@ -16,7 +15,7 @@ use {
|
||||
crate::{
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
http_sender::HttpSender,
|
||||
mock_sender::MockSender,
|
||||
mock_sender::{MockSender, Mocks},
|
||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClientConfig},
|
||||
rpc_config::{RpcAccountInfoConfig, *},
|
||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
||||
@@ -53,9 +52,10 @@ use {
|
||||
cmp::min,
|
||||
net::SocketAddr,
|
||||
str::FromStr,
|
||||
sync::RwLock,
|
||||
time::{Duration, Instant},
|
||||
},
|
||||
tokio::{sync::RwLock, time::sleep},
|
||||
tokio::time::sleep,
|
||||
};
|
||||
|
||||
/// A client of a remote Solana node.
|
||||
@@ -147,9 +147,9 @@ impl RpcClient {
|
||||
///
|
||||
/// This is the basic constructor, allowing construction with any type of
|
||||
/// `RpcSender`. Most applications should use one of the other constructors,
|
||||
/// such as [`RpcClient::new`], [`RpcClient::new_with_commitment`] or
|
||||
/// [`RpcClient::new_with_timeout`].
|
||||
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(
|
||||
/// such as [`new`] and [`new_mock`], which create an `RpcClient`
|
||||
/// encapsulating an [`HttpSender`] and [`MockSender`] respectively.
|
||||
pub(crate) fn new_sender<T: RpcSender + Send + Sync + 'static>(
|
||||
sender: T,
|
||||
config: RpcClientConfig,
|
||||
) -> Self {
|
||||
@@ -315,34 +315,8 @@ impl RpcClient {
|
||||
|
||||
/// Create a mock `RpcClient`.
|
||||
///
|
||||
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
|
||||
/// not use the network, and instead returns synthetic responses, for use in
|
||||
/// tests.
|
||||
///
|
||||
/// It is primarily for internal use, with limited customizability, and
|
||||
/// behaviors determined by internal Solana test cases. New users should
|
||||
/// consider implementing `RpcSender` themselves and constructing
|
||||
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
|
||||
///
|
||||
/// Unless directed otherwise, a mock `RpcClient` will generally return a
|
||||
/// reasonable default response to any request, at least for [`RpcRequest`]
|
||||
/// values for which responses have been implemented.
|
||||
///
|
||||
/// This mock can be customized by changing the `url` argument, which is not
|
||||
/// actually a URL, but a simple string directive that changes the mock
|
||||
/// behavior in specific scenarios:
|
||||
///
|
||||
/// - It is customary to set the `url` to "succeeds" for mocks that should
|
||||
/// return sucessfully, though this value is not actually interpreted.
|
||||
///
|
||||
/// - If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
|
||||
///
|
||||
/// - Other possible values of `url` are specific to different `RpcRequest`
|
||||
/// values. Read the implementation of (non-public) `MockSender` for
|
||||
/// details.
|
||||
///
|
||||
/// The [`RpcClient::new_mock_with_mocks`] function offers further
|
||||
/// customization options.
|
||||
/// See the [`MockSender`] documentation for an explanation of
|
||||
/// how it treats the `url` argument.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -368,43 +342,8 @@ impl RpcClient {
|
||||
|
||||
/// Create a mock `RpcClient`.
|
||||
///
|
||||
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
|
||||
/// not use the network, and instead returns synthetic responses, for use in
|
||||
/// tests.
|
||||
///
|
||||
/// It is primarily for internal use, with limited customizability, and
|
||||
/// behaviors determined by internal Solana test cases. New users should
|
||||
/// consider implementing `RpcSender` themselves and constructing
|
||||
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
|
||||
///
|
||||
/// Unless directed otherwise, a mock `RpcClient` will generally return a
|
||||
/// reasonable default response to any request, at least for [`RpcRequest`]
|
||||
/// values for which responses have been implemented.
|
||||
///
|
||||
/// This mock can be customized in two ways:
|
||||
///
|
||||
/// 1) By changing the `url` argument, which is not actually a URL, but a
|
||||
/// simple string directive that changes the mock behavior in specific
|
||||
/// scenarios.
|
||||
///
|
||||
/// It is customary to set the `url` to "succeeds" for mocks that should
|
||||
/// return sucessfully, though this value is not actually interpreted.
|
||||
///
|
||||
/// If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
|
||||
///
|
||||
/// Other possible values of `url` are specific to different `RpcRequest`
|
||||
/// values. Read the implementation of `MockSender` (which is non-public)
|
||||
/// for details.
|
||||
///
|
||||
/// 2) Custom responses can be configured by providing [`Mocks`]. This type
|
||||
/// is a [`HashMap`] from [`RpcRequest`] to a JSON [`Value`] response,
|
||||
/// Any entries in this map override the default behavior for the given
|
||||
/// request.
|
||||
///
|
||||
/// The [`RpcClient::new_mock_with_mocks`] function offers further
|
||||
/// customization options.
|
||||
///
|
||||
/// [`HashMap`]: std::collections::HashMap
|
||||
/// See the [`MockSender`] documentation for an explanation of how it treats
|
||||
/// the `url` argument.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -503,12 +442,12 @@ impl RpcClient {
|
||||
}
|
||||
|
||||
async fn get_node_version(&self) -> Result<semver::Version, RpcError> {
|
||||
let r_node_version = self.node_version.read().await;
|
||||
let r_node_version = self.node_version.read().unwrap();
|
||||
if let Some(version) = &*r_node_version {
|
||||
Ok(version.clone())
|
||||
} else {
|
||||
drop(r_node_version);
|
||||
let mut w_node_version = self.node_version.write().await;
|
||||
let mut w_node_version = self.node_version.write().unwrap();
|
||||
let node_version = self.get_version().await.map_err(|e| {
|
||||
RpcError::RpcRequestError(format!("cluster version query failed: {}", e))
|
||||
})?;
|
||||
@@ -2473,7 +2412,6 @@ impl RpcClient {
|
||||
/// transaction_details: Some(TransactionDetails::None),
|
||||
/// rewards: Some(true),
|
||||
/// commitment: None,
|
||||
/// max_supported_transaction_version: Some(0),
|
||||
/// };
|
||||
/// let block = rpc_client.get_block_with_config(
|
||||
/// slot,
|
||||
@@ -3114,7 +3052,6 @@ impl RpcClient {
|
||||
/// let config = RpcTransactionConfig {
|
||||
/// encoding: Some(UiTransactionEncoding::Json),
|
||||
/// commitment: Some(CommitmentConfig::confirmed()),
|
||||
/// max_supported_transaction_version: Some(0),
|
||||
/// };
|
||||
/// let transaction = rpc_client.get_transaction_with_config(
|
||||
/// &signature,
|
||||
|
@@ -615,5 +615,5 @@ impl PubsubClient {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// see client-test/test/client.rs
|
||||
// see core/tests/client.rs#test_slot_subscription()
|
||||
}
|
||||
|
@@ -1,213 +0,0 @@
|
||||
//! Simple client that connects to a given UDP port with the QUIC protocol and provides
|
||||
//! an interface for sending transactions which is restricted by the server's flow control.
|
||||
|
||||
use {
|
||||
crate::{client_error::ClientErrorKind, tpu_connection::TpuConnection},
|
||||
async_mutex::Mutex,
|
||||
futures::future::join_all,
|
||||
itertools::Itertools,
|
||||
quinn::{ClientConfig, Endpoint, EndpointConfig, NewConnection, WriteError},
|
||||
solana_sdk::{
|
||||
quic::{QUIC_MAX_CONCURRENT_STREAMS, QUIC_PORT_OFFSET},
|
||||
transport::Result as TransportResult,
|
||||
},
|
||||
std::{
|
||||
net::{SocketAddr, UdpSocket},
|
||||
sync::Arc,
|
||||
},
|
||||
tokio::runtime::Runtime,
|
||||
};
|
||||
|
||||
struct SkipServerVerification;
|
||||
|
||||
impl SkipServerVerification {
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::client::ServerCertVerifier for SkipServerVerification {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_end_entity: &rustls::Certificate,
|
||||
_intermediates: &[rustls::Certificate],
|
||||
_server_name: &rustls::ServerName,
|
||||
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||
_ocsp_response: &[u8],
|
||||
_now: std::time::SystemTime,
|
||||
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
|
||||
Ok(rustls::client::ServerCertVerified::assertion())
|
||||
}
|
||||
}
|
||||
|
||||
struct QuicClient {
|
||||
runtime: Runtime,
|
||||
endpoint: Endpoint,
|
||||
connection: Arc<Mutex<Option<Arc<NewConnection>>>>,
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
pub struct QuicTpuConnection {
|
||||
client: Arc<QuicClient>,
|
||||
}
|
||||
|
||||
impl TpuConnection for QuicTpuConnection {
|
||||
fn new(client_socket: UdpSocket, tpu_addr: SocketAddr) -> Self {
|
||||
let tpu_addr = SocketAddr::new(tpu_addr.ip(), tpu_addr.port() + QUIC_PORT_OFFSET);
|
||||
let client = Arc::new(QuicClient::new(client_socket, tpu_addr));
|
||||
|
||||
Self { client }
|
||||
}
|
||||
|
||||
fn tpu_addr(&self) -> &SocketAddr {
|
||||
&self.client.addr
|
||||
}
|
||||
|
||||
fn send_wire_transaction<T>(&self, wire_transaction: T) -> TransportResult<()>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
let _guard = self.client.runtime.enter();
|
||||
let send_buffer = self.client.send_buffer(wire_transaction);
|
||||
self.client.runtime.block_on(send_buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_wire_transaction_batch<T>(&self, buffers: &[T]) -> TransportResult<()>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
let _guard = self.client.runtime.enter();
|
||||
let send_batch = self.client.send_batch(buffers);
|
||||
self.client.runtime.block_on(send_batch)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl QuicClient {
|
||||
pub fn new(client_socket: UdpSocket, addr: SocketAddr) -> Self {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _guard = runtime.enter();
|
||||
|
||||
let crypto = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_custom_certificate_verifier(SkipServerVerification::new())
|
||||
.with_no_client_auth();
|
||||
|
||||
let create_endpoint = QuicClient::create_endpoint(EndpointConfig::default(), client_socket);
|
||||
|
||||
let mut endpoint = runtime.block_on(create_endpoint);
|
||||
|
||||
endpoint.set_default_client_config(ClientConfig::new(Arc::new(crypto)));
|
||||
|
||||
Self {
|
||||
runtime,
|
||||
endpoint,
|
||||
connection: Arc::new(Mutex::new(None)),
|
||||
addr,
|
||||
}
|
||||
}
|
||||
|
||||
// If this function becomes public, it should be changed to
|
||||
// not expose details of the specific Quic implementation we're using
|
||||
async fn create_endpoint(config: EndpointConfig, client_socket: UdpSocket) -> Endpoint {
|
||||
quinn::Endpoint::new(config, None, client_socket).unwrap().0
|
||||
}
|
||||
|
||||
async fn _send_buffer_using_conn(
|
||||
data: &[u8],
|
||||
connection: &NewConnection,
|
||||
) -> Result<(), WriteError> {
|
||||
let mut send_stream = connection.connection.open_uni().await?;
|
||||
send_stream.write_all(data).await?;
|
||||
send_stream.finish().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Attempts to send data, connecting/reconnecting as necessary
|
||||
// On success, returns the connection used to successfully send the data
|
||||
async fn _send_buffer(&self, data: &[u8]) -> Result<Arc<NewConnection>, WriteError> {
|
||||
let connection = {
|
||||
let mut conn_guard = self.connection.lock().await;
|
||||
|
||||
let maybe_conn = (*conn_guard).clone();
|
||||
match maybe_conn {
|
||||
Some(conn) => conn.clone(),
|
||||
None => {
|
||||
let connecting = self.endpoint.connect(self.addr, "connect").unwrap();
|
||||
let connection = Arc::new(connecting.await?);
|
||||
*conn_guard = Some(connection.clone());
|
||||
connection
|
||||
}
|
||||
}
|
||||
};
|
||||
match Self::_send_buffer_using_conn(data, &connection).await {
|
||||
Ok(()) => Ok(connection),
|
||||
_ => {
|
||||
let connection = {
|
||||
let connecting = self.endpoint.connect(self.addr, "connect").unwrap();
|
||||
let connection = Arc::new(connecting.await?);
|
||||
let mut conn_guard = self.connection.lock().await;
|
||||
*conn_guard = Some(connection.clone());
|
||||
connection
|
||||
};
|
||||
Self::_send_buffer_using_conn(data, &connection).await?;
|
||||
Ok(connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_buffer<T>(&self, data: T) -> Result<(), ClientErrorKind>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
self._send_buffer(data.as_ref()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_batch<T>(&self, buffers: &[T]) -> Result<(), ClientErrorKind>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
// Start off by "testing" the connection by sending the first transaction
|
||||
// This will also connect to the server if not already connected
|
||||
// and reconnect and retry if the first send attempt failed
|
||||
// (for example due to a timed out connection), returning an error
|
||||
// or the connection that was used to successfully send the transaction.
|
||||
// We will use the returned connection to send the rest of the transactions in the batch
|
||||
// to avoid touching the mutex in self, and not bother reconnecting if we fail along the way
|
||||
// since testing even in the ideal GCE environment has found no cases
|
||||
// where reconnecting and retrying in the middle of a batch send
|
||||
// (i.e. we encounter a connection error in the middle of a batch send, which presumably cannot
|
||||
// be due to a timed out connection) has succeeded
|
||||
if buffers.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let connection = self._send_buffer(buffers[0].as_ref()).await?;
|
||||
|
||||
// Used to avoid dereferencing the Arc multiple times below
|
||||
// by just getting a reference to the NewConnection once
|
||||
let connection_ref: &NewConnection = &connection;
|
||||
|
||||
let chunks = buffers[1..buffers.len()]
|
||||
.iter()
|
||||
.chunks(QUIC_MAX_CONCURRENT_STREAMS);
|
||||
|
||||
let futures = chunks.into_iter().map(|buffs| {
|
||||
join_all(
|
||||
buffs
|
||||
.into_iter()
|
||||
.map(|buf| Self::_send_buffer_using_conn(buf.as_ref(), connection_ref)),
|
||||
)
|
||||
});
|
||||
|
||||
for f in futures {
|
||||
f.await.into_iter().try_for_each(|res| res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -6,14 +6,13 @@
|
||||
//!
|
||||
//! [JSON-RPC]: https://www.jsonrpc.org/specification
|
||||
|
||||
pub use crate::mock_sender::Mocks;
|
||||
#[allow(deprecated)]
|
||||
use crate::rpc_deprecated_config::{RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig};
|
||||
use {
|
||||
crate::{
|
||||
client_error::Result as ClientResult,
|
||||
http_sender::HttpSender,
|
||||
mock_sender::MockSender,
|
||||
mock_sender::{MockSender, Mocks},
|
||||
nonblocking::{self, rpc_client::get_rpc_request_str},
|
||||
rpc_config::{RpcAccountInfoConfig, *},
|
||||
rpc_request::{RpcRequest, TokenAccountsFilter},
|
||||
@@ -104,8 +103,8 @@ pub struct GetConfirmedSignaturesForAddress2Config {
|
||||
/// [`Processed`] commitment level. These exceptions are noted in the method
|
||||
/// documentation.
|
||||
///
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
/// [`Processed`]: solana_sdk::commitment_config::CommitmentLevel::Processed
|
||||
/// [`Finalized`]: CommitmentLevel::Finalized
|
||||
/// [`Processed`]: CommitmentLevel::Processed
|
||||
/// [jsonprot]: https://docs.solana.com/developing/clients/jsonrpc-api
|
||||
/// [JSON-RPC]: https://www.jsonrpc.org/specification
|
||||
/// [slots]: https://docs.solana.com/terminology#slot
|
||||
@@ -146,10 +145,6 @@ pub struct GetConfirmedSignaturesForAddress2Config {
|
||||
/// [`is_timeout`](crate::client_error::reqwest::Error::is_timeout) method
|
||||
/// returns `true`. The default timeout is 30 seconds, and may be changed by
|
||||
/// calling an appropriate constructor with a `timeout` parameter.
|
||||
///
|
||||
/// [`ClientError`]: crate::client_error::ClientError
|
||||
/// [`ClientErrorKind`]: crate::client_error::ClientErrorKind
|
||||
/// [`ClientErrorKind::Reqwest`]: crate::client_error::ClientErrorKind::Reqwest
|
||||
pub struct RpcClient {
|
||||
rpc_client: nonblocking::rpc_client::RpcClient,
|
||||
runtime: Option<tokio::runtime::Runtime>,
|
||||
@@ -166,9 +161,9 @@ impl RpcClient {
|
||||
///
|
||||
/// This is the basic constructor, allowing construction with any type of
|
||||
/// `RpcSender`. Most applications should use one of the other constructors,
|
||||
/// such as [`RpcClient::new`], [`RpcClient::new_with_commitment`] or
|
||||
/// [`RpcClient::new_with_timeout`].
|
||||
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(
|
||||
/// such as [`new`] and [`new_mock`], which create an `RpcClient`
|
||||
/// encapsulating an [`HttpSender`] and [`MockSender`] respectively.
|
||||
fn new_sender<T: RpcSender + Send + Sync + 'static>(
|
||||
sender: T,
|
||||
config: RpcClientConfig,
|
||||
) -> Self {
|
||||
@@ -191,10 +186,9 @@ impl RpcClient {
|
||||
/// "http://localhost:8899".
|
||||
///
|
||||
/// The client has a default timeout of 30 seconds, and a default [commitment
|
||||
/// level][cl] of [`Finalized`].
|
||||
/// level][cl] of [`Finalized`](CommitmentLevel::Finalized).
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -203,7 +197,7 @@ impl RpcClient {
|
||||
/// let url = "http://localhost:8899".to_string();
|
||||
/// let client = RpcClient::new(url);
|
||||
/// ```
|
||||
pub fn new<U: ToString>(url: U) -> Self {
|
||||
pub fn new(url: String) -> Self {
|
||||
Self::new_with_commitment(url, CommitmentConfig::default())
|
||||
}
|
||||
|
||||
@@ -217,8 +211,6 @@ impl RpcClient {
|
||||
/// The client has a default timeout of 30 seconds, and a user-specified
|
||||
/// [`CommitmentLevel`] via [`CommitmentConfig`].
|
||||
///
|
||||
/// [`CommitmentLevel`]: solana_sdk::commitment_config::CommitmentLevel
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -228,7 +220,7 @@ impl RpcClient {
|
||||
/// let commitment_config = CommitmentConfig::processed();
|
||||
/// let client = RpcClient::new_with_commitment(url, commitment_config);
|
||||
/// ```
|
||||
pub fn new_with_commitment<U: ToString>(url: U, commitment_config: CommitmentConfig) -> Self {
|
||||
pub fn new_with_commitment(url: String, commitment_config: CommitmentConfig) -> Self {
|
||||
Self::new_sender(
|
||||
HttpSender::new(url),
|
||||
RpcClientConfig::with_commitment(commitment_config),
|
||||
@@ -241,10 +233,9 @@ impl RpcClient {
|
||||
/// "http://localhost:8899".
|
||||
///
|
||||
/// The client has and a default [commitment level][cl] of
|
||||
/// [`Finalized`].
|
||||
/// [`Finalized`](CommitmentLevel::Finalized).
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -255,7 +246,7 @@ impl RpcClient {
|
||||
/// let timeout = Duration::from_secs(1);
|
||||
/// let client = RpcClient::new_with_timeout(url, timeout);
|
||||
/// ```
|
||||
pub fn new_with_timeout<U: ToString>(url: U, timeout: Duration) -> Self {
|
||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||
Self::new_sender(
|
||||
HttpSender::new_with_timeout(url, timeout),
|
||||
RpcClientConfig::with_commitment(CommitmentConfig::default()),
|
||||
@@ -284,8 +275,8 @@ impl RpcClient {
|
||||
/// commitment_config,
|
||||
/// );
|
||||
/// ```
|
||||
pub fn new_with_timeout_and_commitment<U: ToString>(
|
||||
url: U,
|
||||
pub fn new_with_timeout_and_commitment(
|
||||
url: String,
|
||||
timeout: Duration,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> Self {
|
||||
@@ -327,8 +318,8 @@ impl RpcClient {
|
||||
/// confirm_transaction_initial_timeout,
|
||||
/// );
|
||||
/// ```
|
||||
pub fn new_with_timeouts_and_commitment<U: ToString>(
|
||||
url: U,
|
||||
pub fn new_with_timeouts_and_commitment(
|
||||
url: String,
|
||||
timeout: Duration,
|
||||
commitment_config: CommitmentConfig,
|
||||
confirm_transaction_initial_timeout: Duration,
|
||||
@@ -344,34 +335,8 @@ impl RpcClient {
|
||||
|
||||
/// Create a mock `RpcClient`.
|
||||
///
|
||||
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
|
||||
/// not use the network, and instead returns synthetic responses, for use in
|
||||
/// tests.
|
||||
///
|
||||
/// It is primarily for internal use, with limited customizability, and
|
||||
/// behaviors determined by internal Solana test cases. New users should
|
||||
/// consider implementing `RpcSender` themselves and constructing
|
||||
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
|
||||
///
|
||||
/// Unless directed otherwise, a mock `RpcClient` will generally return a
|
||||
/// reasonable default response to any request, at least for [`RpcRequest`]
|
||||
/// values for which responses have been implemented.
|
||||
///
|
||||
/// This mock can be customized by changing the `url` argument, which is not
|
||||
/// actually a URL, but a simple string directive that changes the mock
|
||||
/// behavior in specific scenarios:
|
||||
///
|
||||
/// - It is customary to set the `url` to "succeeds" for mocks that should
|
||||
/// return sucessfully, though this value is not actually interpreted.
|
||||
///
|
||||
/// - If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
|
||||
///
|
||||
/// - Other possible values of `url` are specific to different `RpcRequest`
|
||||
/// values. Read the implementation of (non-public) `MockSender` for
|
||||
/// details.
|
||||
///
|
||||
/// The [`RpcClient::new_mock_with_mocks`] function offers further
|
||||
/// customization options.
|
||||
/// See the [`MockSender`] documentation for an explanation of
|
||||
/// how it treats the `url` argument.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -388,7 +353,7 @@ impl RpcClient {
|
||||
/// let url = "fails".to_string();
|
||||
/// let successful_client = RpcClient::new_mock(url);
|
||||
/// ```
|
||||
pub fn new_mock<U: ToString>(url: U) -> Self {
|
||||
pub fn new_mock(url: String) -> Self {
|
||||
Self::new_sender(
|
||||
MockSender::new(url),
|
||||
RpcClientConfig::with_commitment(CommitmentConfig::default()),
|
||||
@@ -397,43 +362,8 @@ impl RpcClient {
|
||||
|
||||
/// Create a mock `RpcClient`.
|
||||
///
|
||||
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
|
||||
/// not use the network, and instead returns synthetic responses, for use in
|
||||
/// tests.
|
||||
///
|
||||
/// It is primarily for internal use, with limited customizability, and
|
||||
/// behaviors determined by internal Solana test cases. New users should
|
||||
/// consider implementing `RpcSender` themselves and constructing
|
||||
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
|
||||
///
|
||||
/// Unless directed otherwise, a mock `RpcClient` will generally return a
|
||||
/// reasonable default response to any request, at least for [`RpcRequest`]
|
||||
/// values for which responses have been implemented.
|
||||
///
|
||||
/// This mock can be customized in two ways:
|
||||
///
|
||||
/// 1) By changing the `url` argument, which is not actually a URL, but a
|
||||
/// simple string directive that changes the mock behavior in specific
|
||||
/// scenarios.
|
||||
///
|
||||
/// It is customary to set the `url` to "succeeds" for mocks that should
|
||||
/// return sucessfully, though this value is not actually interpreted.
|
||||
///
|
||||
/// If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
|
||||
///
|
||||
/// Other possible values of `url` are specific to different `RpcRequest`
|
||||
/// values. Read the implementation of `MockSender` (which is non-public)
|
||||
/// for details.
|
||||
///
|
||||
/// 2) Custom responses can be configured by providing [`Mocks`]. This type
|
||||
/// is a [`HashMap`] from [`RpcRequest`] to a JSON [`Value`] response,
|
||||
/// Any entries in this map override the default behavior for the given
|
||||
/// request.
|
||||
///
|
||||
/// The [`RpcClient::new_mock_with_mocks`] function offers further
|
||||
/// customization options.
|
||||
///
|
||||
/// [`HashMap`]: std::collections::HashMap
|
||||
/// See the [`MockSender`] documentation for an explanation of how it treats
|
||||
/// the `url` argument.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -457,7 +387,7 @@ impl RpcClient {
|
||||
/// let url = "succeeds".to_string();
|
||||
/// let client = RpcClient::new_mock_with_mocks(url, mocks);
|
||||
/// ```
|
||||
pub fn new_mock_with_mocks<U: ToString>(url: U, mocks: Mocks) -> Self {
|
||||
pub fn new_mock_with_mocks(url: String, mocks: Mocks) -> Self {
|
||||
Self::new_sender(
|
||||
MockSender::new_with_mocks(url, mocks),
|
||||
RpcClientConfig::with_commitment(CommitmentConfig::default()),
|
||||
@@ -467,10 +397,9 @@ impl RpcClient {
|
||||
/// Create an HTTP `RpcClient` from a [`SocketAddr`].
|
||||
///
|
||||
/// The client has a default timeout of 30 seconds, and a default [commitment
|
||||
/// level][cl] of [`Finalized`].
|
||||
/// level][cl] of [`Finalized`](CommitmentLevel::Finalized).
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -491,8 +420,6 @@ impl RpcClient {
|
||||
/// The client has a default timeout of 30 seconds, and a user-specified
|
||||
/// [`CommitmentLevel`] via [`CommitmentConfig`].
|
||||
///
|
||||
/// [`CommitmentLevel`]: solana_sdk::commitment_config::CommitmentLevel
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -515,10 +442,9 @@ impl RpcClient {
|
||||
|
||||
/// Create an HTTP `RpcClient` from a [`SocketAddr`] with specified timeout.
|
||||
///
|
||||
/// The client has a default [commitment level][cl] of [`Finalized`].
|
||||
/// The client has a default [commitment level][cl] of [`Finalized`](CommitmentLevel::Finalized).
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -543,9 +469,7 @@ impl RpcClient {
|
||||
/// determines how thoroughly committed a transaction must be when waiting
|
||||
/// for its confirmation or otherwise checking for confirmation. If not
|
||||
/// specified, the default commitment level is
|
||||
/// [`Finalized`].
|
||||
///
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
/// [`Finalized`](CommitmentLevel::Finalized).
|
||||
///
|
||||
/// The default commitment level is overridden when calling methods that
|
||||
/// explicitly provide a [`CommitmentConfig`], like
|
||||
@@ -579,8 +503,7 @@ impl RpcClient {
|
||||
/// containing an [`RpcResponseError`] with `code` set to
|
||||
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
|
||||
///
|
||||
/// [`RpcError`]: crate::rpc_request::RpcError
|
||||
/// [`RpcResponseError`]: crate::rpc_request::RpcError::RpcResponseError
|
||||
/// [`RpcResponseError`]: RpcError::RpcResponseError
|
||||
/// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
|
||||
/// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
|
||||
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
|
||||
@@ -693,8 +616,7 @@ impl RpcClient {
|
||||
/// containing an [`RpcResponseError`] with `code` set to
|
||||
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
|
||||
///
|
||||
/// [`RpcError`]: crate::rpc_request::RpcError
|
||||
/// [`RpcResponseError`]: crate::rpc_request::RpcError::RpcResponseError
|
||||
/// [`RpcResponseError`]: RpcError::RpcResponseError
|
||||
/// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
|
||||
/// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
|
||||
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
|
||||
@@ -768,8 +690,7 @@ impl RpcClient {
|
||||
/// containing an [`RpcResponseError`] with `code` set to
|
||||
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
|
||||
///
|
||||
/// [`RpcError`]: crate::rpc_request::RpcError
|
||||
/// [`RpcResponseError`]: crate::rpc_request::RpcError::RpcResponseError
|
||||
/// [`RpcResponseError`]: RpcError::RpcResponseError
|
||||
/// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
|
||||
/// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
|
||||
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
|
||||
@@ -2113,7 +2034,6 @@ impl RpcClient {
|
||||
/// transaction_details: Some(TransactionDetails::None),
|
||||
/// rewards: Some(true),
|
||||
/// commitment: None,
|
||||
/// max_supported_transaction_version: Some(0),
|
||||
/// };
|
||||
/// let block = rpc_client.get_block_with_config(
|
||||
/// slot,
|
||||
@@ -2181,7 +2101,7 @@ impl RpcClient {
|
||||
///
|
||||
/// This method uses the [`Finalized`] [commitment level][cl].
|
||||
///
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
/// [`Finalized`]: CommitmentLevel::Finalized
|
||||
/// [`get_blocks_with_limit`]: RpcClient::get_blocks_with_limit.
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
///
|
||||
@@ -2240,7 +2160,7 @@ impl RpcClient {
|
||||
/// This method returns an error if the given commitment level is below
|
||||
/// [`Confirmed`].
|
||||
///
|
||||
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
|
||||
/// [`Confirmed`]: CommitmentLevel::Confirmed
|
||||
///
|
||||
/// # RPC Reference
|
||||
///
|
||||
@@ -2333,7 +2253,7 @@ impl RpcClient {
|
||||
/// [`Confirmed`].
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
|
||||
/// [`Confirmed`]: CommitmentLevel::Confirmed
|
||||
///
|
||||
/// # RPC Reference
|
||||
///
|
||||
@@ -2494,7 +2414,7 @@ impl RpcClient {
|
||||
/// [`Confirmed`].
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
|
||||
/// [`Confirmed`]: CommitmentLevel::Confirmed
|
||||
///
|
||||
/// # RPC Reference
|
||||
///
|
||||
@@ -2584,7 +2504,7 @@ impl RpcClient {
|
||||
///
|
||||
/// This method uses the [`Finalized`] [commitment level][cl].
|
||||
///
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
/// [`Finalized`]: CommitmentLevel::Finalized
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
///
|
||||
/// # RPC Reference
|
||||
@@ -2639,7 +2559,7 @@ impl RpcClient {
|
||||
/// [`Confirmed`].
|
||||
///
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
|
||||
/// [`Confirmed`]: CommitmentLevel::Confirmed
|
||||
///
|
||||
/// # RPC Reference
|
||||
///
|
||||
@@ -2676,7 +2596,6 @@ impl RpcClient {
|
||||
/// let config = RpcTransactionConfig {
|
||||
/// encoding: Some(UiTransactionEncoding::Json),
|
||||
/// commitment: Some(CommitmentConfig::confirmed()),
|
||||
/// max_supported_transaction_version: Some(0),
|
||||
/// };
|
||||
/// let transaction = rpc_client.get_transaction_with_config(
|
||||
/// &signature,
|
||||
@@ -3005,7 +2924,7 @@ impl RpcClient {
|
||||
///
|
||||
/// This method uses the [`Finalized`] [commitment level][cl].
|
||||
///
|
||||
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
|
||||
/// [`Finalized`]: CommitmentLevel::Finalized
|
||||
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
|
||||
///
|
||||
/// # RPC Reference
|
||||
@@ -3163,7 +3082,6 @@ impl RpcClient {
|
||||
/// [`RpcError::ForUser`]. This is unlike [`get_account_with_commitment`],
|
||||
/// which returns `Ok(None)` if the account does not exist.
|
||||
///
|
||||
/// [`RpcError::ForUser`]: crate::rpc_request::RpcError::ForUser
|
||||
/// [`get_account_with_commitment`]: RpcClient::get_account_with_commitment
|
||||
///
|
||||
/// # RPC Reference
|
||||
|
@@ -197,7 +197,6 @@ pub struct RpcBlockSubscribeConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub transaction_details: Option<TransactionDetails>,
|
||||
pub show_rewards: Option<bool>,
|
||||
pub max_supported_transaction_version: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
@@ -249,7 +248,6 @@ pub struct RpcBlockConfig {
|
||||
pub rewards: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub max_supported_transaction_version: Option<u8>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcBlockConfig {
|
||||
@@ -290,7 +288,6 @@ pub struct RpcTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub max_supported_transaction_version: Option<u8>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcTransactionConfig {
|
||||
|
@@ -3,7 +3,6 @@ use {
|
||||
crate::rpc_response::RpcSimulateTransactionResult,
|
||||
jsonrpc_core::{Error, ErrorCode},
|
||||
solana_sdk::clock::Slot,
|
||||
solana_transaction_status::EncodeError,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
@@ -60,7 +59,7 @@ pub enum RpcCustomError {
|
||||
#[error("BlockStatusNotAvailableYet")]
|
||||
BlockStatusNotAvailableYet { slot: Slot },
|
||||
#[error("UnsupportedTransactionVersion")]
|
||||
UnsupportedTransactionVersion(u8),
|
||||
UnsupportedTransactionVersion,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -69,16 +68,6 @@ pub struct NodeUnhealthyErrorData {
|
||||
pub num_slots_behind: Option<Slot>,
|
||||
}
|
||||
|
||||
impl From<EncodeError> for RpcCustomError {
|
||||
fn from(err: EncodeError) -> Self {
|
||||
match err {
|
||||
EncodeError::UnsupportedTransactionVersion(version) => {
|
||||
Self::UnsupportedTransactionVersion(version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcCustomError> for Error {
|
||||
fn from(e: RpcCustomError) -> Self {
|
||||
match e {
|
||||
@@ -183,9 +172,9 @@ impl From<RpcCustomError> for Error {
|
||||
message: format!("Block status not yet available for slot {}", slot),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::UnsupportedTransactionVersion(version) => Self {
|
||||
RpcCustomError::UnsupportedTransactionVersion => Self {
|
||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION),
|
||||
message: format!("Transaction version ({}) is not supported", version),
|
||||
message: "Versioned transactions are not supported".to_string(),
|
||||
data: None,
|
||||
},
|
||||
}
|
||||
|
@@ -71,7 +71,6 @@ impl From<RpcConfirmedBlockConfig> for RpcBlockConfig {
|
||||
transaction_details: config.transaction_details,
|
||||
rewards: config.rewards,
|
||||
commitment: config.commitment,
|
||||
max_supported_transaction_version: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +98,6 @@ impl From<RpcConfirmedTransactionConfig> for RpcTransactionConfig {
|
||||
Self {
|
||||
encoding: config.encoding,
|
||||
commitment: config.commitment,
|
||||
max_supported_transaction_version: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -117,7 +117,7 @@ pub struct RpcInflationRate {
|
||||
pub epoch: Epoch,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcKeyedAccount {
|
||||
pub pubkey: String,
|
||||
@@ -246,7 +246,7 @@ pub struct RpcBlockProductionRange {
|
||||
pub last_slot: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProduction {
|
||||
/// Map of leader base58 identity pubkeys to a tuple of `(number of leader slots, number of blocks produced)`
|
||||
@@ -290,8 +290,6 @@ pub struct RpcIdentity {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcVote {
|
||||
/// Vote account address, as base-58 encoded string
|
||||
pub vote_pubkey: String,
|
||||
pub slots: Vec<Slot>,
|
||||
pub hash: String,
|
||||
pub timestamp: Option<UnixTimestamp>,
|
||||
@@ -363,7 +361,7 @@ pub struct RpcAccountBalance {
|
||||
pub lamports: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSupply {
|
||||
pub total: u64,
|
||||
@@ -432,8 +430,8 @@ pub enum RpcBlockUpdateError {
|
||||
#[error("block store error")]
|
||||
BlockStoreError,
|
||||
|
||||
#[error("unsupported transaction version ({0})")]
|
||||
UnsupportedTransactionVersion(u8),
|
||||
#[error("unsupported transaction version")]
|
||||
UnsupportedTransactionVersion,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
@@ -23,9 +23,13 @@ pub struct RpcTransportStats {
|
||||
/// `RpcSender` implements the underlying transport of requests to, and
|
||||
/// responses from, a Solana node, and is used primarily by [`RpcClient`].
|
||||
///
|
||||
/// [`RpcClient`]: crate::rpc_client::RpcClient
|
||||
/// It is typically implemented by [`HttpSender`] in production, and
|
||||
/// [`MockSender`] in unit tests.
|
||||
///
|
||||
/// [`HttpSender`]: crate::http_sender::HttpSender
|
||||
/// [`MockSender`]: crate::mock_sender::MockSender
|
||||
#[async_trait]
|
||||
pub trait RpcSender {
|
||||
pub(crate) trait RpcSender {
|
||||
async fn send(
|
||||
&self,
|
||||
request: RpcRequest,
|
||||
|
@@ -4,10 +4,8 @@
|
||||
//! unstable and may change in future releases.
|
||||
|
||||
use {
|
||||
crate::{
|
||||
rpc_client::RpcClient, rpc_config::RpcProgramAccountsConfig, rpc_response::Response,
|
||||
tpu_connection::TpuConnection, udp_client::UdpTpuConnection,
|
||||
},
|
||||
crate::{rpc_client::RpcClient, rpc_config::RpcProgramAccountsConfig, rpc_response::Response},
|
||||
bincode::{serialize_into, serialized_size},
|
||||
log::*,
|
||||
solana_sdk::{
|
||||
account::Account,
|
||||
@@ -19,12 +17,13 @@ use {
|
||||
hash::Hash,
|
||||
instruction::Instruction,
|
||||
message::Message,
|
||||
packet::PACKET_DATA_SIZE,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signature, Signer},
|
||||
signers::Signers,
|
||||
system_instruction,
|
||||
timing::duration_as_ms,
|
||||
transaction::{self, Transaction, VersionedTransaction},
|
||||
transaction::{self, Transaction},
|
||||
transport::Result as TransportResult,
|
||||
},
|
||||
std::{
|
||||
@@ -118,20 +117,22 @@ impl ClientOptimizer {
|
||||
}
|
||||
|
||||
/// An object for querying and sending transactions to the network.
|
||||
pub struct ThinClient<C: 'static + TpuConnection> {
|
||||
pub struct ThinClient {
|
||||
transactions_socket: UdpSocket,
|
||||
tpu_addrs: Vec<SocketAddr>,
|
||||
rpc_clients: Vec<RpcClient>,
|
||||
tpu_connections: Vec<C>,
|
||||
optimizer: ClientOptimizer,
|
||||
}
|
||||
|
||||
impl<C: 'static + TpuConnection> ThinClient<C> {
|
||||
impl ThinClient {
|
||||
/// Create a new ThinClient that will interface with the Rpc at `rpc_addr` using TCP
|
||||
/// and the Tpu at `tpu_addr` over `transactions_socket` using Quic or UDP
|
||||
/// (currently hardcoded to UDP)
|
||||
/// and the Tpu at `tpu_addr` over `transactions_socket` using UDP.
|
||||
pub fn new(rpc_addr: SocketAddr, tpu_addr: SocketAddr, transactions_socket: UdpSocket) -> Self {
|
||||
let tpu_connection = C::new(transactions_socket, tpu_addr);
|
||||
|
||||
Self::new_from_client(RpcClient::new_socket(rpc_addr), tpu_connection)
|
||||
Self::new_from_client(
|
||||
tpu_addr,
|
||||
transactions_socket,
|
||||
RpcClient::new_socket(rpc_addr),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_socket_with_timeout(
|
||||
@@ -141,14 +142,18 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
|
||||
timeout: Duration,
|
||||
) -> Self {
|
||||
let rpc_client = RpcClient::new_socket_with_timeout(rpc_addr, timeout);
|
||||
let tpu_connection = C::new(transactions_socket, tpu_addr);
|
||||
Self::new_from_client(rpc_client, tpu_connection)
|
||||
Self::new_from_client(tpu_addr, transactions_socket, rpc_client)
|
||||
}
|
||||
|
||||
fn new_from_client(rpc_client: RpcClient, tpu_connection: C) -> Self {
|
||||
fn new_from_client(
|
||||
tpu_addr: SocketAddr,
|
||||
transactions_socket: UdpSocket,
|
||||
rpc_client: RpcClient,
|
||||
) -> Self {
|
||||
Self {
|
||||
transactions_socket,
|
||||
tpu_addrs: vec![tpu_addr],
|
||||
rpc_clients: vec![rpc_client],
|
||||
tpu_connections: vec![tpu_connection],
|
||||
optimizer: ClientOptimizer::new(0),
|
||||
}
|
||||
}
|
||||
@@ -163,19 +168,16 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
|
||||
|
||||
let rpc_clients: Vec<_> = rpc_addrs.into_iter().map(RpcClient::new_socket).collect();
|
||||
let optimizer = ClientOptimizer::new(rpc_clients.len());
|
||||
let tpu_connections: Vec<_> = tpu_addrs
|
||||
.into_iter()
|
||||
.map(|tpu_addr| C::new(transactions_socket.try_clone().unwrap(), tpu_addr))
|
||||
.collect();
|
||||
Self {
|
||||
transactions_socket,
|
||||
tpu_addrs,
|
||||
rpc_clients,
|
||||
tpu_connections,
|
||||
optimizer,
|
||||
}
|
||||
}
|
||||
|
||||
fn tpu_connection(&self) -> &C {
|
||||
&self.tpu_connections[self.optimizer.best()]
|
||||
fn tpu_addr(&self) -> &SocketAddr {
|
||||
&self.tpu_addrs[self.optimizer.best()]
|
||||
}
|
||||
|
||||
fn rpc_client(&self) -> &RpcClient {
|
||||
@@ -203,6 +205,7 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
|
||||
self.send_and_confirm_transaction(&[keypair], transaction, tries, 0)
|
||||
}
|
||||
|
||||
/// Retry sending a signed Transaction to the server for processing
|
||||
pub fn send_and_confirm_transaction<T: Signers>(
|
||||
&self,
|
||||
keypairs: &T,
|
||||
@@ -212,16 +215,18 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
|
||||
) -> TransportResult<Signature> {
|
||||
for x in 0..tries {
|
||||
let now = Instant::now();
|
||||
let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize];
|
||||
let mut wr = std::io::Cursor::new(&mut buf[..]);
|
||||
let mut num_confirmed = 0;
|
||||
let mut wait_time = MAX_PROCESSING_AGE;
|
||||
serialize_into(&mut wr, &transaction)
|
||||
.expect("serialize Transaction in pub fn transfer_signed");
|
||||
// resend the same transaction until the transaction has no chance of succeeding
|
||||
let wire_transaction =
|
||||
bincode::serialize(&transaction).expect("transaction serialization failed");
|
||||
while now.elapsed().as_secs() < wait_time as u64 {
|
||||
if num_confirmed == 0 {
|
||||
// Send the transaction if there has been no confirmation (e.g. the first time)
|
||||
self.tpu_connection()
|
||||
.send_wire_transaction(&wire_transaction)?;
|
||||
self.transactions_socket
|
||||
.send_to(&buf[..], &self.tpu_addr())?;
|
||||
}
|
||||
|
||||
if let Ok(confirmed_blocks) = self.poll_for_signature_confirmation(
|
||||
@@ -316,13 +321,13 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: 'static + TpuConnection> Client for ThinClient<C> {
|
||||
impl Client for ThinClient {
|
||||
fn tpu_addr(&self) -> String {
|
||||
self.tpu_connection().tpu_addr().to_string()
|
||||
self.tpu_addr().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: 'static + TpuConnection> SyncClient for ThinClient<C> {
|
||||
impl SyncClient for ThinClient {
|
||||
fn send_and_confirm_message<T: Signers>(
|
||||
&self,
|
||||
keypairs: &T,
|
||||
@@ -602,21 +607,17 @@ impl<C: 'static + TpuConnection> SyncClient for ThinClient<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: 'static + TpuConnection> AsyncClient for ThinClient<C> {
|
||||
impl AsyncClient for ThinClient {
|
||||
fn async_send_transaction(&self, transaction: Transaction) -> TransportResult<Signature> {
|
||||
let transaction = VersionedTransaction::from(transaction);
|
||||
self.tpu_connection()
|
||||
.serialize_and_send_transaction(&transaction)?;
|
||||
let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize];
|
||||
let mut wr = std::io::Cursor::new(&mut buf[..]);
|
||||
serialize_into(&mut wr, &transaction)
|
||||
.expect("serialize Transaction in pub fn transfer_signed");
|
||||
assert!(buf.len() < PACKET_DATA_SIZE);
|
||||
self.transactions_socket
|
||||
.send_to(&buf[..], &self.tpu_addr())?;
|
||||
Ok(transaction.signatures[0])
|
||||
}
|
||||
|
||||
fn async_send_batch(&self, transactions: Vec<Transaction>) -> TransportResult<()> {
|
||||
let batch: Vec<VersionedTransaction> = transactions.into_iter().map(Into::into).collect();
|
||||
self.tpu_connection()
|
||||
.par_serialize_and_send_transaction_batch(&batch[..])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn async_send_message<T: Signers>(
|
||||
&self,
|
||||
keypairs: &T,
|
||||
@@ -648,23 +649,20 @@ impl<C: 'static + TpuConnection> AsyncClient for ThinClient<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_client(
|
||||
(rpc, tpu): (SocketAddr, SocketAddr),
|
||||
range: (u16, u16),
|
||||
) -> ThinClient<UdpTpuConnection> {
|
||||
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient {
|
||||
let (_, transactions_socket) =
|
||||
solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap();
|
||||
ThinClient::<UdpTpuConnection>::new(rpc, tpu, transactions_socket)
|
||||
ThinClient::new(rpc, tpu, transactions_socket)
|
||||
}
|
||||
|
||||
pub fn create_client_with_timeout(
|
||||
(rpc, tpu): (SocketAddr, SocketAddr),
|
||||
range: (u16, u16),
|
||||
timeout: Duration,
|
||||
) -> ThinClient<UdpTpuConnection> {
|
||||
) -> ThinClient {
|
||||
let (_, transactions_socket) =
|
||||
solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap();
|
||||
ThinClient::<UdpTpuConnection>::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
|
||||
ThinClient::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@@ -1,40 +0,0 @@
|
||||
use {
|
||||
rayon::iter::{IntoParallelIterator, ParallelIterator},
|
||||
solana_sdk::{transaction::VersionedTransaction, transport::Result as TransportResult},
|
||||
std::net::{SocketAddr, UdpSocket},
|
||||
};
|
||||
|
||||
pub trait TpuConnection {
|
||||
fn new(client_socket: UdpSocket, tpu_addr: SocketAddr) -> Self;
|
||||
|
||||
fn tpu_addr(&self) -> &SocketAddr;
|
||||
|
||||
fn serialize_and_send_transaction(
|
||||
&self,
|
||||
transaction: &VersionedTransaction,
|
||||
) -> TransportResult<()> {
|
||||
let wire_transaction =
|
||||
bincode::serialize(transaction).expect("serialize Transaction in send_batch");
|
||||
self.send_wire_transaction(&wire_transaction)
|
||||
}
|
||||
|
||||
fn send_wire_transaction<T>(&self, wire_transaction: T) -> TransportResult<()>
|
||||
where
|
||||
T: AsRef<[u8]>;
|
||||
|
||||
fn par_serialize_and_send_transaction_batch(
|
||||
&self,
|
||||
transactions: &[VersionedTransaction],
|
||||
) -> TransportResult<()> {
|
||||
let buffers = transactions
|
||||
.into_par_iter()
|
||||
.map(|tx| bincode::serialize(&tx).expect("serialize Transaction in send_batch"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.send_wire_transaction_batch(&buffers)
|
||||
}
|
||||
|
||||
fn send_wire_transaction_batch<T>(&self, buffers: &[T]) -> TransportResult<()>
|
||||
where
|
||||
T: AsRef<[u8]>;
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
//! Simple TPU client that communicates with the given UDP port with UDP and provides
|
||||
//! an interface for sending transactions
|
||||
|
||||
use {
|
||||
crate::tpu_connection::TpuConnection,
|
||||
core::iter::repeat,
|
||||
solana_sdk::transport::Result as TransportResult,
|
||||
solana_streamer::sendmmsg::batch_send,
|
||||
std::net::{SocketAddr, UdpSocket},
|
||||
};
|
||||
|
||||
pub struct UdpTpuConnection {
|
||||
socket: UdpSocket,
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl TpuConnection for UdpTpuConnection {
|
||||
fn new(client_socket: UdpSocket, tpu_addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
socket: client_socket,
|
||||
addr: tpu_addr,
|
||||
}
|
||||
}
|
||||
|
||||
fn tpu_addr(&self) -> &SocketAddr {
|
||||
&self.addr
|
||||
}
|
||||
|
||||
fn send_wire_transaction<T>(&self, wire_transaction: T) -> TransportResult<()>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
self.socket.send_to(wire_transaction.as_ref(), self.addr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_wire_transaction_batch<T>(&self, buffers: &[T]) -> TransportResult<()>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
let pkts: Vec<_> = buffers.iter().zip(repeat(self.tpu_addr())).collect();
|
||||
batch_send(&self.socket, &pkts)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.10.5"
|
||||
version = "1.10.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-core"
|
||||
readme = "../README.md"
|
||||
@@ -15,69 +15,73 @@ codecov = { repository = "solana-labs/solana", branch = "master", service = "git
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.7.6"
|
||||
base64 = "0.13.0"
|
||||
base64 = "0.12.3"
|
||||
bincode = "1.3.3"
|
||||
bs58 = "0.4.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
crossbeam-channel = "0.5"
|
||||
dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] }
|
||||
etcd-client = { version = "0.8.4", features = ["tls"] }
|
||||
etcd-client = { version = "0.8.3", features = ["tls"]}
|
||||
fs_extra = "1.2.0"
|
||||
histogram = "0.6.9"
|
||||
itertools = "0.10.3"
|
||||
log = "0.4.14"
|
||||
lru = "0.7.3"
|
||||
lru = "0.7.2"
|
||||
rand = "0.7.0"
|
||||
rand_chacha = "0.2.2"
|
||||
rayon = "1.5.1"
|
||||
retain_mut = "0.1.7"
|
||||
serde = "1.0.136"
|
||||
retain_mut = "0.1.5"
|
||||
serde = "1.0.134"
|
||||
serde_derive = "1.0.103"
|
||||
solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.10.5" }
|
||||
solana-bloom = { path = "../bloom", version = "=1.10.5" }
|
||||
solana-client = { path = "../client", version = "=1.10.5" }
|
||||
solana-entry = { path = "../entry", version = "=1.10.5" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.5" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.5" }
|
||||
solana-geyser-plugin-manager = { path = "../geyser-plugin-manager", version = "=1.10.5" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.5" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.10.5" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.5" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.5" }
|
||||
solana-perf = { path = "../perf", version = "=1.10.5" }
|
||||
solana-poh = { path = "../poh", version = "=1.10.5" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.10.5" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.5" }
|
||||
solana-replica-lib = { path = "../replica-lib", version = "=1.10.5" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.10.5" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.5" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.5" }
|
||||
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.5" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.5" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.5" }
|
||||
sys-info = "0.9.1"
|
||||
solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.10.0" }
|
||||
solana-bloom = { path = "../bloom", version = "=1.10.0" }
|
||||
solana-accountsdb-plugin-manager = { path = "../accountsdb-plugin-manager", version = "=1.10.0" }
|
||||
solana-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-entry = { path = "../entry", version = "=1.10.0" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.10.0" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.10.0" }
|
||||
solana-measure = { path = "../measure", version = "=1.10.0" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.10.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
|
||||
solana-perf = { path = "../perf", version = "=1.10.0" }
|
||||
solana-poh = { path = "../poh", version = "=1.10.0" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.10.0" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.10.0" }
|
||||
solana-replica-lib = { path = "../replica-lib", version = "=1.10.0" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.10.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.10.0" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.0" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.0" }
|
||||
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.0" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.10.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
|
||||
tempfile = "3.3.0"
|
||||
thiserror = "1.0"
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.0" }
|
||||
sys-info = "0.9.1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
trees = "0.4.2"
|
||||
|
||||
[dev-dependencies]
|
||||
jsonrpc-core = "18.0.0"
|
||||
jsonrpc-core-client = { version = "18.0.0", features = ["ipc", "ws"] }
|
||||
jsonrpc-derive = "18.0.0"
|
||||
jsonrpc-pubsub = "18.0.0"
|
||||
matches = "0.1.9"
|
||||
raptorq = "1.6.5"
|
||||
reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde_json = "1.0.79"
|
||||
serial_test = "0.6.0"
|
||||
solana-logger = { path = "../logger", version = "=1.10.5" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.10.5" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.10.5" }
|
||||
solana-version = { path = "../version", version = "=1.10.5" }
|
||||
raptorq = "1.6.4"
|
||||
reqwest = { version = "0.11.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde_json = "1.0.78"
|
||||
serial_test = "0.5.1"
|
||||
solana-logger = { path = "../logger", version = "=1.10.0" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.10.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.10.0" }
|
||||
solana-version = { path = "../version", version = "=1.10.0" }
|
||||
static_assertions = "1.1.0"
|
||||
systemstat = "0.1.10"
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
sysctl = "0.4.4"
|
||||
sysctl = "0.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.4"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user