diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 00000000..520c4ab1 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +large-error-threshold = 1_000_000 diff --git a/.github/actions/setup-solana/action.yaml b/.github/actions/setup-solana/action.yaml new file mode 100644 index 00000000..1f4fb6cd --- /dev/null +++ b/.github/actions/setup-solana/action.yaml @@ -0,0 +1,21 @@ +name: "Setup Solana" +description: "Setup Solana" +runs: + using: "composite" + steps: + - uses: actions/cache@v2 + name: Cache Solana Tool Suite + id: cache-solana + with: + path: | + ~/.cache/solana/ + ~/.local/share/solana/ + key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }} + - run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)" + shell: bash + - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + shell: bash + - run: solana-keygen new --no-bip39-passphrase + shell: bash + - run: solana config set --url localhost + shell: bash diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml new file mode 100644 index 00000000..adcc0082 --- /dev/null +++ b/.github/actions/setup/action.yaml @@ -0,0 +1,7 @@ +name: "Setup" +description: "Setup" +runs: + using: "composite" + steps: + - run: sudo apt-get update && sudo apt-get install -y pkg-config build-essential libudev-dev + shell: bash diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 00000000..c8399676 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,99 @@ +name: Build +on: + push: + branches: + - master + pull_request: + branches: + - master +env: + SOLANA_CLI_VERSION: 1.16.17 +jobs: + build-and-test: + name: Build and test programs + runs-on: big-runner-1 + + steps: + # Setup + - uses: actions/checkout@v3 + with: + submodules: recursive + - uses: ./.github/actions/setup/ + - uses: ./.github/actions/setup-solana/ + - uses: actions/cache@v2 + name: Cache Cargo registry + index + id: cache-anchor + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }} + - run: cargo install --git https://github.com/coral-xyz/anchor --tag v0.28.0 anchor-cli --locked --force + working-directory: / + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2023-10-05-x86_64-unknown-linux-gnu + components: rustfmt, clippy + + # --force is hack around cargo caching, figure out later + - name: Setup Rust tools + run: | + cargo +nightly-2023-10-05 install cargo-sort --force + cargo +nightly-2023-10-05 install cargo-udeps --force + shell: bash + + - name: cargo sort + run: cargo +nightly-2023-10-05 sort --workspace --check + shell: bash + + - name: cargo fmt + run: cargo +nightly-2023-10-05 fmt --all --check + shell: bash + + - name: clippy + run: cargo +nightly-2023-10-05 clippy --all-features --all-targets --tests -- -D warnings + shell: bash + + - name: cargo udeps + run: cargo +nightly-2023-10-05 udeps --all-features --all-targets --tests + shell: bash + + # Build IDLs + - name: Build validator-history + run: anchor build --idl idl --program-name validator_history + + # Make sure no uncommitted changes + - name: Check for diff + run: git diff --exit-code + + - name: cargo test + run: RUST_LOG=debug cargo test --all-targets --all-features --color auto + shell: bash + + verified-build: + name: Build Verifiable Artifact + runs-on: big-runner-1 + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + # --force because the cargo cache has it saved + - name: Install Solana Verify + run: | + cargo install solana-verify --force + solana-verify --version + + - name: Verifiable Build + run: | + solana-verify build --library-name validator_history + + - name: Upload Artifact + uses: actions/upload-artifact@v3 + with: + name: build + path: | + target/deploy/validator_history.so diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..402f7f8b --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,66 @@ +name: Release +on: + push: + tags: + - "v*" + +env: + SOLANA_CLI_VERSION: 1.16.17 +jobs: + release: + name: Create the jito programs release + runs-on: big-runner-1 + + steps: + # Setup + - uses: actions/checkout@v3 + with: + submodules: recursive + - uses: ./.github/actions/setup/ + - uses: ./.github/actions/setup-solana/ + - uses: actions/cache@v2 + name: Cache Cargo registry + index + id: cache-anchor + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }} + - run: cargo install --git https://github.com/coral-xyz/anchor --tag v0.28.0 anchor-cli --locked --force + + # Build IDLs + programs + - name: Build program + run: anchor build --idl idl --program-name validator_history + + # Verified build + # --force because the cargo cache has it saved + - name: Install Solana Verify + run: | + cargo install solana-verify --force + solana-verify --version + + - name: Verifiable Build + run: | + solana-verify build --library-name validator_history + + # Make sure no uncommitted changes + - name: Check for diff + run: git diff --exit-code + + # Upload artifacts + - name: Upload Artifact + uses: actions/upload-artifact@v3 + with: + name: build + path: | + target/deploy/validator_history.so + programs/validator-history/idl/validator_history.json + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + target/deploy/validator_history.so + programs/validator-history/idl/validator_history.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b631d85e --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.anchor +.DS_Store +**/target +*/debug +*/node_modules +*/deps +*/dist +*/build +test-ledger +*/test-ledger +.idea/ +**/targets diff --git a/Anchor.toml b/Anchor.toml new file mode 100644 index 00000000..77068aa4 --- /dev/null +++ b/Anchor.toml @@ -0,0 +1,21 @@ +[features] +seeds = false +skip-lint = false +[programs.localnet] +validator_history = "HistoryJTGbKQD2mRgLZ3XhqHnN811Qpez8X9kCcGHoa" + +[workspace] +members = [ + "programs/validator-history", +] +exclude = ["keepers", "utils"] + +[registry] +url = "https://api.apr.dev" + +[provider] +cluster = "Localnet" +wallet = "~/.config/solana/id.json" + +[scripts] +test = "cargo +nightly test" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..3d63e0d3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7044 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589c637f0e68c877bbd59a4599bbe849cac8e5f3e4b5a3ebae8f528cd218dcdc" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom 0.2.11", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom 0.2.11", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa5be5b72abea167f87c868379ba3c2be356bfca9e6f474fd055fa0f7eeb4f2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.70", + "quote 1.0.33", + "regex", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f468970344c7c9f9d03b4da854fd7c54f21305059f53789d0045c1dd803f0018" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.5.0", + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59948e7f9ef8144c2aefb3f32a40c5fce2798baeec765ba038389e82301017ef" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.70", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc753c9d1c7981cb8948cf7e162fb0f64558999c0413058e2d43df1df5448086" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38b4e172ba1b52078f53fdc9f11e3dc0668ad27997838a0aad2d148afac8c97" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eebd21543606ab61e2d83d9da37d24d3886a49f390f9c43a1964735e8c0f0d5" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4720d899b3686396cced9508f23dab420f1308344456ec78ef76f98fda42af" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f495e85480bd96ddeb77b71d499247c7d4e8b501e75ecb234e9ef7ae7bd6552a" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "anchor-lang" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2d4b20100f1310a774aba3471ef268e5c4ba4d5c28c0bbe663c2658acbc414" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-program", + "anchor-derive-accounts", + "anchor-derive-space", + "arrayref", + "base64 0.13.1", + "bincode", + "borsh 0.10.3", + "bytemuck", + "getrandom 0.2.11", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a125e4b0cc046cfec58f5aa25038e34cf440151d58f0db3afc55308251fe936d" +dependencies = [ + "anyhow", + "bs58 0.5.0", + "heck 0.3.3", + "proc-macro2 1.0.70", + "quote 1.0.33", + "serde", + "serde_json", + "sha2 0.10.8", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint 0.4.4", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.4", + "num-traits", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint 0.4.4", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "array-bytes" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad284aeb45c13f2fb4f084de4a420ebf447423bdf9386c0540ce33cb3ef4b8c" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "autotools" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8da1805e028a172334c3b680f93e71126f2327622faef2ec3d893c0a4ad77" +dependencies = [ + "cc", +] + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom 0.2.11", + "instant", + "pin-project-lite", + "rand 0.8.5", + "tokio", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease 0.2.15", + "proc-macro2 1.0.70", + "quote 1.0.33", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.41", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive 0.9.3", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive 0.10.3", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal 0.9.3", + "borsh-schema-derive-internal 0.9.3", + "proc-macro-crate 0.1.5", + "proc-macro2 1.0.70", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal 0.10.3", + "borsh-schema-derive-internal 0.10.3", + "proc-macro-crate 0.1.5", + "proc-macro2 1.0.70", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "caps" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex 0.2.4", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.16.0", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex 0.6.0", + "strsim 0.10.0", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.70", + "quote 1.0.33", + "strsim 0.10.0", + "syn 2.0.41", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", + "rayon", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.4", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "dialoguer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dir-diff" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2860407d7d7e2e004bb2128510ad9e8d669e76fa005ccf567977b5d71b8b4a0b" +dependencies = [ + "walkdir", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "dlopen" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +dependencies = [ + "dlopen_derive", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "dlopen_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" +dependencies = [ + "libc", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.8", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7add3873b5dd076766ee79c8e406ad1a472c385476b9e38849f8eec24f1be689" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint 0.4.4", + "num-traits", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fast-math" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2465292146cdfc2011350fe3b1c616ac83cf0faeedb33463ba1c332ed8948d66" +dependencies = [ + "ieee754", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "serde", + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "goauth" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8af59a261bcf42f45d1b261232847b9b850ba0a1419d6100698246fb66e9240" +dependencies = [ + "arc-swap", + "futures", + "log", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "simpl", + "smpl_jwt", + "time", + "tokio", +] + +[[package]] +name = "goblin" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util 0.7.2", + "tracing", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.6", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "headers" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +dependencies = [ + "base64 0.21.5", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-proxy" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "hyper-tls", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ieee754" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "rayon", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "index_list" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70891286cb8e844fdfcf1178b47569699f9e20b5ecc4b45a6240a64771444638" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "rayon", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "indicatif" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.3", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jito-programs-vote-state" +version = "0.1.3" +source = "git+https://github.com/jito-foundation/jito-programs?rev=b8f8d315306d6ac32450bd1aa23b7c1322a83bc7#b8f8d315306d6ac32450bd1aa23b7c1322a83bc7" +dependencies = [ + "anchor-lang", + "bincode", + "serde", + "serde_derive", + "solana-program", +] + +[[package]] +name = "jito-tip-distribution" +version = "0.1.2" +source = "git+https://github.com/jito-foundation/jito-programs?rev=b8f8d315306d6ac32450bd1aa23b7c1322a83bc7#b8f8d315306d6ac32450bd1aa23b7c1322a83bc7" +dependencies = [ + "anchor-lang", + "jito-programs-vote-state", + "solana-program", +] + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keeper-core" +version = "0.1.0" +dependencies = [ + "bincode", + "clap 4.4.11", + "futures", + "log", + "solana-client", + "solana-metrics", + "solana-program", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "lz4-sys", +] + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libz-sys" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lz4" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "num-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.3", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +dependencies = [ + "num_enum_derive 0.7.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +dependencies = [ + "proc-macro-crate 2.0.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.2.1+3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "js-sys", + "lazy_static", + "percent-encoding", + "pin-project", + "rand 0.8.5", + "thiserror", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "percentage" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" +dependencies = [ + "num", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.1.0", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der", + "spki", + "zeroize", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2 1.0.70", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2 1.0.70", + "syn 2.0.41", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck 0.4.1", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease 0.1.25", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + +[[package]] +name = "protobuf-src" +version = "1.1.0+21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" +dependencies = [ + "autotools", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quinn" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", + "webpki", +] + +[[package]] +name = "quinn-proto" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring 0.16.20", + "rustc-hash", + "rustls", + "rustls-native-certs", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki", +] + +[[package]] +name = "quinn-udp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" +dependencies = [ + "libc", + "quinn-proto", + "socket2", + "tracing", + "windows-sys 0.42.0", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2 1.0.70", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "reed-solomon-erasure" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7263373d500d4d4f505d43a2a662d475a894aa94503a1ee28e9188b5f3960d4f" +dependencies = [ + "cc", + "libc", + "libm", + "lru", + "parking_lot 0.11.2", + "smallvec", + "spin 0.9.8", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +dependencies = [ + "async-compression", + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util 0.7.2", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom 0.2.11", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.5", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.7", + "untrusted 0.9.0", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "simpl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a30f10c911c0355f80f1c2faa8096efc4a58cdf8590b954d5b395efa071c711" + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "smpl_jwt" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b6ff8c21c74ce7744643a7cddbb02579a44f1f77e4316bff1ddb741aca8ac9" +dependencies = [ + "base64 0.13.1", + "log", + "openssl", + "serde", + "serde_derive", + "serde_json", + "simpl", + "time", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "solana-account-decoder" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "815b7eeb8cfc0cc27c3500658845bc0adbfb51a9212814af522f4912e1bdab2e" +dependencies = [ + "Inflector", + "base64 0.21.5", + "bincode", + "bs58 0.4.0", + "bv", + "lazy_static", + "serde", + "serde_derive", + "serde_json", + "solana-address-lookup-table-program", + "solana-config-program", + "solana-sdk", + "spl-token", + "spl-token-2022", + "spl-token-metadata-interface", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-address-lookup-table-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9137f2199f70e082f15f91076f31fa6e67d98d40168de759feab12c6b60542a8" +dependencies = [ + "bincode", + "bytemuck", + "log", + "num-derive 0.3.3", + "num-traits", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-banks-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "785bca4464357f3d7aad38b3f57b1dcec5fc47f7eb3e0c1ffba907ce15aae66c" +dependencies = [ + "borsh 0.10.3", + "futures", + "solana-banks-interface", + "solana-program", + "solana-sdk", + "tarpc", + "thiserror", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-banks-interface" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb13f6fff944f56df1c5b4bb708676efb856991bc368c969c436cc444e6f5e83" +dependencies = [ + "serde", + "solana-sdk", + "tarpc", +] + +[[package]] +name = "solana-banks-server" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "893926dda1a8d1594b55b753298df54fb9ed9ed1e802cac69ad3a225b7cdc63e" +dependencies = [ + "bincode", + "crossbeam-channel", + "futures", + "solana-banks-interface", + "solana-client", + "solana-runtime", + "solana-sdk", + "solana-send-transaction-service", + "tarpc", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-bloom" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7509b1d9823ed07deab1cfd86ee50a2b2a7775c02bc663eff7f48c6dbc0b221" +dependencies = [ + "bv", + "fnv", + "log", + "rand 0.7.3", + "rayon", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", +] + +[[package]] +name = "solana-bpf-loader-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb0ed3691420f8ca6c7d58eb1335ecb0b73b1b6188039b7b631bcd0253b24377" +dependencies = [ + "bincode", + "byteorder", + "libsecp256k1", + "log", + "rand 0.7.3", + "solana-measure", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-bucket-map" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b64e2a282391cffdd2985971abf571922fd58db5f2743014f8a0428f719af2" +dependencies = [ + "bv", + "log", + "memmap2", + "modular-bitfield", + "num_enum 0.6.1", + "rand 0.7.3", + "solana-measure", + "solana-sdk", + "tempfile", +] + +[[package]] +name = "solana-clap-utils" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a99a1aa397ec62a9b10f2f072fb95d78c6f4303bbca11d6af5f3f198e3d62e8" +dependencies = [ + "chrono", + "clap 2.34.0", + "rpassword", + "solana-perf", + "solana-remote-wallet", + "solana-sdk", + "thiserror", + "tiny-bip39", + "uriparse", + "url", +] + +[[package]] +name = "solana-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd1d1b50f6937ec5b7b05faa171956dc052ad593d058de5046e325cc0ec4c23" +dependencies = [ + "async-trait", + "bincode", + "futures", + "futures-util", + "indexmap 1.9.3", + "indicatif", + "log", + "quinn", + "rand 0.7.3", + "rayon", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-pubsub-client", + "solana-quic-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-rpc-client-nonce-utils", + "solana-sdk", + "solana-streamer", + "solana-thin-client", + "solana-tpu-client", + "solana-udp-client", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-compute-budget-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "674cbca707a6a38dc860cef40b988debb24e0347a04cd123bd2b05cb6f75eed4" +dependencies = [ + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-config-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7448528e2fd237e7d7ca93d4d8541a8a9f346b9f947405799d9a6dd5c22aa41c" +dependencies = [ + "bincode", + "chrono", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-connection-cache" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51fe3a80fc59a93392a103e6ab492305a6ac614abee70cde6e34fe74fc55dd" +dependencies = [ + "async-trait", + "bincode", + "futures-util", + "indexmap 1.9.3", + "log", + "rand 0.7.3", + "rayon", + "rcgen", + "solana-measure", + "solana-metrics", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-entry" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934c896302603683df402479d9c84478d23530bd0010dffb3020319348a824a0" +dependencies = [ + "bincode", + "crossbeam-channel", + "dlopen", + "dlopen_derive", + "lazy_static", + "log", + "rand 0.7.3", + "rayon", + "serde", + "solana-measure", + "solana-merkle-tree", + "solana-metrics", + "solana-perf", + "solana-rayon-threadlimit", + "solana-sdk", +] + +[[package]] +name = "solana-frozen-abi" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8e68a37635d475c40f026bfbc39df3298ce91ec0f4db848979b1dbcd9bc675" +dependencies = [ + "ahash 0.8.6", + "blake3", + "block-buffer 0.10.4", + "bs58 0.4.0", + "bv", + "byteorder", + "cc", + "either", + "generic-array", + "getrandom 0.1.16", + "im", + "lazy_static", + "log", + "memmap2", + "once_cell", + "rand_core 0.6.4", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ea45edfe53a4d95f18bd627f1b60e200611a436afd0c58c9c529c085af8965" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustc_version", + "syn 2.0.41", +] + +[[package]] +name = "solana-gossip" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd9169d2b7d8eaa7e46c414503bb54e65add0aa78497a59ebe1923743aee883" +dependencies = [ + "bincode", + "bv", + "clap 2.34.0", + "crossbeam-channel", + "flate2", + "indexmap 1.9.3", + "itertools", + "log", + "lru", + "matches", + "num-traits", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rayon", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "solana-bloom", + "solana-clap-utils", + "solana-client", + "solana-entry", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-ledger", + "solana-logger", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-perf", + "solana-rayon-threadlimit", + "solana-runtime", + "solana-sdk", + "solana-streamer", + "solana-thin-client", + "solana-tpu-client", + "solana-version", + "solana-vote-program", + "static_assertions", + "thiserror", +] + +[[package]] +name = "solana-ledger" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd8afdb4e35c57eb2d0b2a51e295f92fb60e9e16bf1f4f16d7313b52207aab1" +dependencies = [ + "assert_matches", + "bincode", + "bitflags 1.3.2", + "byteorder", + "chrono", + "chrono-humanize", + "crossbeam-channel", + "dashmap", + "fs_extra", + "futures", + "itertools", + "lazy_static", + "libc", + "log", + "lru", + "num_cpus", + "num_enum 0.6.1", + "prost", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rayon", + "reed-solomon-erasure", + "rocksdb", + "rustc_version", + "scopeguard", + "serde", + "serde_bytes", + "sha2 0.10.8", + "solana-account-decoder", + "solana-bpf-loader-program", + "solana-entry", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-perf", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-runtime", + "solana-sdk", + "solana-stake-program", + "solana-storage-bigtable", + "solana-storage-proto", + "solana-transaction-status", + "solana-vote-program", + "spl-token", + "spl-token-2022", + "static_assertions", + "tempfile", + "thiserror", + "tokio", + "tokio-stream", + "trees", +] + +[[package]] +name = "solana-loader-v4-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4091f5bc56ecd65473ad5ae4f0bae43a5ea26b916a824eaf74909ed0b0154a7d" +dependencies = [ + "log", + "rand 0.7.3", + "solana-measure", + "solana-program-runtime", + "solana-sdk", + "solana_rbpf", +] + +[[package]] +name = "solana-logger" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db83d89279b0620958ae1278fd52f340c68be79980a5f6ebfb3d4e4623d7241" +dependencies = [ + "env_logger 0.9.3", + "lazy_static", + "log", +] + +[[package]] +name = "solana-measure" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6745b818b9d2d88b0011ac5532e3dcd4cde0bd1613464ee1bcb98db423ab97" +dependencies = [ + "log", + "solana-sdk", +] + +[[package]] +name = "solana-merkle-tree" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa3c7b0efa48c10d3bb93bd26e7f14f091683e71121f78ce2b01abc6b517e5d" +dependencies = [ + "fast-math", + "matches", + "solana-program", +] + +[[package]] +name = "solana-metrics" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b3782e709a4546a77354e6b0fbc176a34f19b420e65c0d9c9c48f93459fbab" +dependencies = [ + "crossbeam-channel", + "gethostname", + "lazy_static", + "log", + "reqwest", + "solana-sdk", +] + +[[package]] +name = "solana-net-utils" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea302ba1a7186826fecace83da4adce9b288e97ea370999a9aee2bfc71129b" +dependencies = [ + "bincode", + "clap 3.2.25", + "crossbeam-channel", + "log", + "nix", + "rand 0.7.3", + "serde", + "serde_derive", + "socket2", + "solana-logger", + "solana-sdk", + "solana-version", + "tokio", + "url", +] + +[[package]] +name = "solana-perf" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a5d5de014354c112349667c51f80ce01bca0c6b0bfa027cbc069e972c1c0c7" +dependencies = [ + "ahash 0.8.6", + "bincode", + "bv", + "caps", + "curve25519-dalek", + "dlopen", + "dlopen_derive", + "fnv", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.7.3", + "rayon", + "serde", + "solana-metrics", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2560d24192b60301c1219c054a34bcd9d9723bb64ec9b5b987882d86c32868e6" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "array-bytes", + "base64 0.21.5", + "bincode", + "bitflags 1.3.2", + "blake3", + "borsh 0.10.3", + "borsh 0.9.3", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.11", + "itertools", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1", + "log", + "memoffset 0.9.0", + "num-bigint 0.4.4", + "num-derive 0.3.3", + "num-traits", + "parking_lot 0.12.1", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-program-runtime" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1726697292d3f551898537f921749352e965510a9cfe7e7b2ff7f1a0fcc6e1db" +dependencies = [ + "base64 0.21.5", + "bincode", + "eager", + "enum-iterator", + "itertools", + "libc", + "log", + "num-derive 0.3.3", + "num-traits", + "percentage", + "rand 0.7.3", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-program-test" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66d071392c72f4e12527fa5e13e9ab9bd23a785eda1331a597277ee8f8c0800" +dependencies = [ + "assert_matches", + "async-trait", + "base64 0.21.5", + "bincode", + "chrono-humanize", + "crossbeam-channel", + "log", + "serde", + "solana-banks-client", + "solana-banks-interface", + "solana-banks-server", + "solana-bpf-loader-program", + "solana-logger", + "solana-program-runtime", + "solana-runtime", + "solana-sdk", + "solana-vote-program", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-pubsub-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f134152897fe6d3fad3da9945ae452dfc6c2d71465ddce1ad8a423d54ad38bee" +dependencies = [ + "crossbeam-channel", + "futures-util", + "log", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "solana-sdk", + "thiserror", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "solana-quic-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409c0182a32bb11acdf84c96361cff4628e93e7e8b293a8cc43e5ef354ffa46a" +dependencies = [ + "async-mutex", + "async-trait", + "futures", + "itertools", + "lazy_static", + "log", + "quinn", + "quinn-proto", + "quinn-udp", + "rcgen", + "rustls", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-rpc-client-api", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-rayon-threadlimit" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce5c2d7f4e92580e6dd18877f0cd5f152e662dbda9c2eed69d29ae9a6f6e5d0" +dependencies = [ + "lazy_static", + "num_cpus", +] + +[[package]] +name = "solana-remote-wallet" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a9e49486e3f31009cfd24869de318e0fac261257f0e87e6f692e0bbf6a053b6" +dependencies = [ + "console", + "dialoguer", + "log", + "num-derive 0.3.3", + "num-traits", + "parking_lot 0.12.1", + "qstring", + "semver", + "solana-sdk", + "thiserror", + "uriparse", +] + +[[package]] +name = "solana-rpc-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336cdd2dbb4dcfdb7c905eb45fdd32de30f594b12f00d894160a8e4d12fc76a3" +dependencies = [ + "async-trait", + "base64 0.21.5", + "bincode", + "bs58 0.4.0", + "indicatif", + "log", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "solana-vote-program", + "tokio", +] + +[[package]] +name = "solana-rpc-client-api" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0cc64f5092d9c3e0bbfbd459689ffc17617b9f52773ffb7e26a2483a33d5ace" +dependencies = [ + "base64 0.21.5", + "bs58 0.4.0", + "jsonrpc-core", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "solana-rpc-client-nonce-utils" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c54440695e2a3b14749b52f2021172aeb2387f8bd95f4e0cc2f97e5d27b5ea4" +dependencies = [ + "clap 2.34.0", + "solana-clap-utils", + "solana-rpc-client", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-runtime" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63578440eb0526fc3b3155be56c33dec115d8739e0964ec563a8ae8c80b4ffd2" +dependencies = [ + "arrayref", + "base64 0.21.5", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "dir-diff", + "flate2", + "fnv", + "im", + "index_list", + "itertools", + "lazy_static", + "log", + "lru", + "lz4", + "memmap2", + "modular-bitfield", + "num-derive 0.3.3", + "num-traits", + "num_cpus", + "num_enum 0.6.1", + "once_cell", + "ouroboros", + "percentage", + "rand 0.7.3", + "rayon", + "regex", + "rustc_version", + "serde", + "serde_derive", + "serde_json", + "solana-address-lookup-table-program", + "solana-bpf-loader-program", + "solana-bucket-map", + "solana-compute-budget-program", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-loader-v4-program", + "solana-measure", + "solana-metrics", + "solana-perf", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-version", + "solana-vote-program", + "solana-zk-token-proof-program", + "solana-zk-token-sdk", + "static_assertions", + "strum", + "strum_macros", + "symlink", + "tar", + "tempfile", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-sdk" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fc9581f8345a67da71386274084d9a2e35f25689871ad644f5992c786df7c7" +dependencies = [ + "assert_matches", + "base64 0.21.5", + "bincode", + "bitflags 1.3.2", + "borsh 0.10.3", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.7", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array", + "hmac 0.12.1", + "itertools", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memmap2", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.6.1", + "pbkdf2 0.11.0", + "qstring", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "serde_with", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d749979b74d6ca1d8b0f1da1d0333332cfac425a34d71ed1149cccc322e0533" +dependencies = [ + "bs58 0.4.0", + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustversion", + "syn 2.0.41", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + +[[package]] +name = "solana-send-transaction-service" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dad2cb6f9462e7e1c9900df76b9ad601b199aab3d26855d28bf8494698def3" +dependencies = [ + "crossbeam-channel", + "log", + "solana-client", + "solana-measure", + "solana-metrics", + "solana-runtime", + "solana-sdk", + "solana-tpu-client", +] + +[[package]] +name = "solana-stake-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec54610fafa934717e90e8ab0774867b054d4c3852b5ca24d5947edf14a61e1" +dependencies = [ + "bincode", + "log", + "rustc_version", + "solana-config-program", + "solana-program-runtime", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-storage-bigtable" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b6b00589ff2ef15d76202a2d4b022c86527b64f96e127d0735784aacb15be5" +dependencies = [ + "backoff", + "bincode", + "bytes", + "bzip2", + "enum-iterator", + "flate2", + "futures", + "goauth", + "http", + "hyper", + "hyper-proxy", + "log", + "openssl", + "prost", + "prost-types", + "serde", + "serde_derive", + "smpl_jwt", + "solana-metrics", + "solana-sdk", + "solana-storage-proto", + "solana-transaction-status", + "thiserror", + "tokio", + "tonic", + "zstd", +] + +[[package]] +name = "solana-storage-proto" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5188f8e47570059ff3754e6bcd2203a911db973464587151efc06327922e454b" +dependencies = [ + "bincode", + "bs58 0.4.0", + "prost", + "protobuf-src", + "serde", + "solana-account-decoder", + "solana-sdk", + "solana-transaction-status", + "tonic-build", +] + +[[package]] +name = "solana-streamer" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a848b9b56af99988e6273ccf79f2bd816633dc3da9ea0eb4488a5b0f8ec820" +dependencies = [ + "async-channel", + "bytes", + "crossbeam-channel", + "futures-util", + "histogram", + "indexmap 1.9.3", + "itertools", + "libc", + "log", + "nix", + "pem", + "percentage", + "pkcs8", + "quinn", + "quinn-proto", + "quinn-udp", + "rand 0.7.3", + "rcgen", + "rustls", + "solana-metrics", + "solana-perf", + "solana-sdk", + "thiserror", + "tokio", + "x509-parser", +] + +[[package]] +name = "solana-system-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a427a441f95ee0b9d8e2065a2978d86142c6fa40cbdccd1de724f77b6cc885af" +dependencies = [ + "bincode", + "log", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-thin-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db5ba7eddcc0cefc3c5c116387097cb81bb13d7598fbdb3b40c5a964105e879" +dependencies = [ + "bincode", + "log", + "rayon", + "solana-connection-cache", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", +] + +[[package]] +name = "solana-tpu-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bdf56494fd1b509c5428f969a10e4e0865b3eaf40aac1640c8f72dac3112b89" +dependencies = [ + "async-trait", + "bincode", + "futures-util", + "indexmap 1.9.3", + "indicatif", + "log", + "rand 0.7.3", + "rayon", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-pubsub-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-transaction-status" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3c52eaa1977b0121a099243de4b5b44de936e67869d3298400fb6e974a2f7b" +dependencies = [ + "Inflector", + "base64 0.21.5", + "bincode", + "borsh 0.10.3", + "bs58 0.4.0", + "lazy_static", + "log", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-address-lookup-table-program", + "solana-sdk", + "spl-associated-token-account", + "spl-memo", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "solana-udp-client" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1225fb057b8b5e5aa5b0ee01b974e6ef2c6f01727dfd217c23b89b6547a8b17b" +dependencies = [ + "async-trait", + "solana-connection-cache", + "solana-net-utils", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-version" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f7b09ffc8f5446bee6ee1ab4ce4c98504d23222313de1d0ed762f736a3ffe3" +dependencies = [ + "log", + "rustc_version", + "semver", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", +] + +[[package]] +name = "solana-vote-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2be239ebe1d73af268ce9ba5111ce9595a430aa98576105e87b00e92a5ef2a0b" +dependencies = [ + "bincode", + "log", + "num-derive 0.3.3", + "num-traits", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-zk-token-proof-program" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a813c2c577b9eb24b62d7149d1e5340425f55650f3ff6a01eb2ded828a95774" +dependencies = [ + "bytemuck", + "getrandom 0.1.16", + "num-derive 0.3.3", + "num-traits", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", +] + +[[package]] +name = "solana-zk-token-sdk" +version = "1.16.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4b0547480462cfec9dddaa8adcf2fa7c8b022021738bf71c790c0c7be54a34" +dependencies = [ + "aes-gcm-siv", + "base64 0.21.5", + "bincode", + "bytemuck", + "byteorder", + "curve25519-dalek", + "getrandom 0.1.16", + "itertools", + "lazy_static", + "merlin", + "num-derive 0.3.3", + "num-traits", + "rand 0.7.3", + "serde", + "serde_json", + "sha3 0.9.1", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "solana_rbpf" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d4ba1e58947346e360fabde0697029d36ba83c42f669199b16a8931313cf29" +dependencies = [ + "byteorder", + "combine", + "goblin", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "scroll", + "thiserror", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "spl-associated-token-account" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3" +dependencies = [ + "assert_matches", + "borsh 0.10.3", + "num-derive 0.4.1", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "spl-discriminator" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" +dependencies = [ + "quote 1.0.33", + "spl-discriminator-syn", + "syn 2.0.41", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "sha2 0.10.8", + "syn 2.0.41", + "thiserror", +] + +[[package]] +name = "spl-memo" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-pod" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079" +dependencies = [ + "borsh 0.10.3", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error", +] + +[[package]] +name = "spl-program-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c" +dependencies = [ + "num-derive 0.4.1", + "num-traits", + "solana-program", + "spl-program-error-derive", + "thiserror", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "sha2 0.10.8", + "syn 2.0.41", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", +] + +[[package]] +name = "spl-token" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.6.1", + "solana-program", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.1", + "num-traits", + "num_enum 0.7.1", + "solana-program", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod", + "spl-token", + "spl-token-metadata-interface", + "spl-transfer-hook-interface", + "spl-type-length-value", + "thiserror", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" +dependencies = [ + "borsh 0.10.3", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051d31803f873cabe71aec3c1b849f35248beae5d19a347d93a5c9cccc5d5a9b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-tlv-account-resolution", + "spl-type-length-value", +] + +[[package]] +name = "spl-type-length-value" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", + "unicode-xid 0.2.4", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tarpc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38a012bed6fb9681d3bf71ffaa4f88f3b4b9ed3198cda6e4c8462d24d4bb80" +dependencies = [ + "anyhow", + "fnv", + "futures", + "humantime", + "opentelemetry", + "pin-project", + "rand 0.8.5", + "serde", + "static_assertions", + "tarpc-plugins", + "thiserror", + "tokio", + "tokio-serde", + "tokio-util 0.6.10", + "tracing", + "tracing-opentelemetry", +] + +[[package]] +name = "tarpc-plugins" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.4.1", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tests" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "bincode", + "bytemuck", + "cfg-if", + "ed25519-dalek", + "futures", + "jito-tip-distribution", + "matches", + "rand 0.7.3", + "semver", + "serde", + "serde_derive", + "solana-gossip", + "solana-program-test", + "solana-sdk", + "solana-version", + "validator-history", + "validator-history-vote-state", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d0183f6f6001549ab68f8c7585093bb732beefbcf6d23a10b9b95c73a1dd49" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot 0.11.2", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki", + "webpki-roots", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.13.1", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "rustls-pemfile", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-util 0.7.2", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +dependencies = [ + "prettyplease 0.1.25", + "proc-macro2 1.0.70", + "prost-build", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util 0.7.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "trees" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de5f738ceab88e2491a94ddc33c3feeadfa95fedc60363ef110845df12f3878" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "sha-1", + "thiserror", + "url", + "utf-8", + "webpki", + "webpki-roots", +] + +[[package]] +name = "type-layout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074069282ba5be5078f1bdb34112b963516d50f262bf4c1082fee1f6ada11d74" +dependencies = [ + "memoffset 0.5.6", + "type-layout-derive", +] + +[[package]] +name = "type-layout-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4a1cf66ce820973c4b31c5ef47a8e930a53ffbcec191212c33f5a3ad75c6cd" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "validator-history" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "bincode", + "bytemuck", + "cfg-if", + "jito-tip-distribution", + "semver", + "serde", + "serde_derive", + "solana-security-txt", + "static_assertions", + "thiserror", + "type-layout", + "validator-history-vote-state", +] + +[[package]] +name = "validator-history-cli" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "clap 4.4.11", + "futures", + "futures-util", + "jito-tip-distribution", + "log", + "solana-account-decoder", + "solana-clap-utils", + "solana-client", + "solana-metrics", + "solana-program", + "solana-sdk", + "thiserror", + "tokio", + "validator-history", +] + +[[package]] +name = "validator-history-vote-state" +version = "0.1.2" +dependencies = [ + "anchor-lang", + "bincode", + "serde", + "solana-program", +] + +[[package]] +name = "validator-keeper" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "bytemuck", + "clap 4.4.11", + "env_logger 0.10.1", + "futures", + "futures-util", + "jito-tip-distribution", + "keeper-core", + "log", + "solana-account-decoder", + "solana-clap-utils", + "solana-client", + "solana-gossip", + "solana-metrics", + "solana-net-utils", + "solana-program", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", + "validator-history", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote 1.0.33", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.7", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee40e41f93be5d0057f40462588b988db1354f2c1b1442be186b6df88e8d151" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "xattr" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..0b351b7f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "keepers/*", + "programs/*", + "tests", + "utils/*" +] diff --git a/README.md b/README.md new file mode 100644 index 00000000..3fd4195d --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# Validator History Program + +## About + +The Validator History Program, a component of Jito StakeNet, is an on-chain record of verified Solana validator data, storing up to 512 epochs of history per validator. It takes fields accessible to the solana runtime like validator performance history, validator commission, MEV commission, as well as Gossip data like validator IP, version, and client type, and stores them all in a single account. It also contains some fields that currently require permissioned upload but are easily verifiable with a getVoteAccounts call, like total active stake per validator, stake rank, and superminority status. All these fields are stored in a single account per validator, the ValidatorHistory account. This enables all these disparate fields to be easily composed with in on chain programs, with a long lookback period and ease of access through the single account. + +## Structure + +The main Anchor program is in `programs/validator-history`. + +### Important files + +- `src/lib.rs` - entrypoint for instructions +- `src/state.rs` - containing the account definitions as well as logic for appending all the fields to the main circular buffer +- `src/instructions/*.rs` - individual instructions + +### Accounts + +`ValidatorHistory`: Tracks historical metadata on chain for a single validator. Contains a `CircBuf`, a data structure that acts as a wrap-around array. The CircBuf contains entries of `ValidatorHistoryEntry`, which stores validator metadata for an epoch. The default/null value for each field is the max value for the field's type. + +Note that this is a `zero_copy` account, which allows us to initialize a lot of space without hitting runtime stack or heap size liimits. This has the constraint of requiring the struct to implement `bytemuck::{Pod, Zeroable}` and following C-style struct alignment. + +`Config`: Tracks admin authorities as well as global program metadata. + +## Test + +Tests are in `tests/` written with solana-program-test, and can be run with `cargo test`. + +## Build + +`anchor build --program-name validator_history` (regular anchor build) +`solana-verify build --library-name validator_history` (solana verified build) + +## Verify + +Verify with [solana-verifiable-build](https://github.com/Ellipsis-Labs/solana-verifiable-build): + +`solana-verify verify-from-repo -um --program-id HistoryJTGbKQD2mRgLZ3XhqHnN811Qpez8X9kCcGHoa https://github.com/jito-foundation/stakenet` + +## Running Keeper + +Run as binary: + +Build: `cargo b -r --package validator-keeper` + +Run: `./target/release/validator-keeper --json-rpc-url --cluster mainnet --tip-distribution-program-id F2Zu7QZiTYUhPd7u9ukRVwxh7B71oA3NMJcHuCHc29P2 --program-id HistoryJTGbKQD2mRgLZ3XhqHnN811Qpez8X9kCcGHoa --interval 600 --keypair --gossip-entrypoint ` + +Run as docker container: + +`docker compose --env-file config/.env up -d --build validator-keeper` + +Metrics for running can be sent to your influx server if you set the SOLANA_METRICS_CONFIG env var. + +## CLI + +The CLI can be used to see the status of on-chain validator history data. + +Build: `cargo b -r --package validator-history-cli` + +To see the current epoch state of all validator history accounts: + +`./target/release/validator-history-cli --json-rpc-url cranker-status` + +To see the historical state of a single validator history account: + +`./target/release/validator-history-cli --json-rpc-url history ` diff --git a/keepers/keeper-core/Cargo.toml b/keepers/keeper-core/Cargo.toml new file mode 100644 index 00000000..e6732713 --- /dev/null +++ b/keepers/keeper-core/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "keeper-core" +version = "0.1.0" +edition = "2021" +description = "Core utilities for DSP keeper bots." +license = "Apache-2.0" +authors = ["Jito Foundation "] + +[dependencies] +bincode = "1.3.3" +clap = { version = "4.3.0", features = ["derive"] } +futures = "0.3.21" +log = "0.4.18" +solana-client = "1.16" +solana-metrics = "1.16" +solana-program = "1.16" +solana-sdk = "1.16" +thiserror = "1.0.37" +tokio = { version = "~1.14.1", features = ["full"] } diff --git a/keepers/keeper-core/src/lib.rs b/keepers/keeper-core/src/lib.rs new file mode 100644 index 00000000..a8d2a147 --- /dev/null +++ b/keepers/keeper-core/src/lib.rs @@ -0,0 +1,529 @@ +use std::fmt::{Display, Formatter}; +use std::mem::size_of; +use std::{collections::HashMap, sync::Arc, time::Duration}; + +use clap::ValueEnum; +use log::*; +use solana_client::rpc_response::RpcVoteAccountInfo; +use solana_client::{client_error::ClientError, nonblocking::rpc_client::RpcClient}; +use solana_program::hash::Hash; +use solana_sdk::packet::PACKET_DATA_SIZE; +use solana_sdk::{ + account::Account, commitment_config::CommitmentConfig, instruction::AccountMeta, + instruction::Instruction, packet::Packet, pubkey::Pubkey, signature::Keypair, + signature::Signature, signer::Signer, transaction::Transaction, +}; +use thiserror::Error as ThisError; +use tokio::task::{self, JoinError}; + +#[derive(Debug, Default, Clone, Copy)] +pub struct SubmitStats { + pub successes: u64, + pub errors: u64, +} +#[derive(Debug, Default, Clone, Copy)] +pub struct CreateUpdateStats { + pub creates: SubmitStats, + pub updates: SubmitStats, +} + +pub type Error = Box; +#[derive(ThisError, Debug, Clone)] +pub enum TransactionExecutionError { + #[error("Transactions failed to execute after multiple retries")] + RetryError(Vec), + #[error("Transactions failed to execute after multiple retries")] + TransactionRetryError(Vec>), + #[error("RPC Client error: {0:?}")] + ClientError(String, Vec), + #[error("RPC Client error: {0:?}")] + TransactionClientError(String, Vec>), +} + +#[derive(ThisError, Debug)] +pub enum MultipleAccountsError { + #[error(transparent)] + ClientError(#[from] ClientError), + #[error(transparent)] + JoinError(#[from] JoinError), +} + +#[derive(ValueEnum, Debug, Clone)] +pub enum Cluster { + Mainnet, + Testnet, + Localnet, +} + +impl Display for Cluster { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Cluster::Mainnet => write!(f, "mainnet"), + Cluster::Testnet => write!(f, "testnet"), + Cluster::Localnet => write!(f, "localnet"), + } + } +} + +pub trait CreateTransaction { + fn create_transaction(&self) -> Vec; +} + +pub trait UpdateInstruction { + fn update_instruction(&self) -> Instruction; +} + +pub trait Address { + fn address(&self) -> Pubkey; +} + +pub async fn get_multiple_accounts_batched( + accounts: &[Pubkey], + rpc_client: &Arc, +) -> Result>, MultipleAccountsError> { + let tasks = accounts.chunks(100).map(|chunk| { + let client = Arc::clone(rpc_client); + let chunk = chunk.to_owned(); + task::spawn( + async move { get_multiple_accounts_with_retry(&client, chunk.as_slice()).await }, + ) + }); + + let mut accounts_result = Vec::new(); + for result in futures::future::join_all(tasks).await.into_iter() { + match result { + Ok(Ok(accounts)) => accounts_result.extend(accounts), + Ok(Err(e)) => { + return Err(MultipleAccountsError::ClientError(e)); + } + Err(e) => return Err(MultipleAccountsError::JoinError(e)), + } + } + Ok(accounts_result) +} + +async fn get_latest_blockhash_with_retry(client: &RpcClient) -> Result { + for _ in 1..4 { + let result = client + .get_latest_blockhash_with_commitment(CommitmentConfig::finalized()) + .await; + if result.is_ok() { + return Ok(result?.0); + } + } + Ok(client + .get_latest_blockhash_with_commitment(CommitmentConfig::finalized()) + .await? + .0) +} + +pub async fn get_multiple_accounts_with_retry( + client: &RpcClient, + pubkeys: &[Pubkey], +) -> Result>, ClientError> { + for _ in 1..4 { + let result = client.get_multiple_accounts(pubkeys).await; + if result.is_ok() { + return result; + } + } + client.get_multiple_accounts(pubkeys).await +} + +pub async fn get_vote_accounts_with_retry( + client: &RpcClient, + min_vote_epochs: usize, + commitment: Option, +) -> Result, ClientError> { + for _ in 1..4 { + let result = client + .get_vote_accounts_with_commitment(commitment.unwrap_or(CommitmentConfig::finalized())) + .await; + if let Ok(response) = result { + return Ok(response + .current + .into_iter() + .chain(response.delinquent.into_iter()) + .filter(|vote_account| vote_account.epoch_credits.len() >= min_vote_epochs) + .collect::>()); + } + } + let result = client + .get_vote_accounts_with_commitment(commitment.unwrap_or(CommitmentConfig::finalized())) + .await; + match result { + Ok(response) => Ok(response + .current + .into_iter() + .chain(response.delinquent.into_iter()) + .filter(|vote_account| vote_account.epoch_credits.len() >= min_vote_epochs) + .collect::>()), + Err(e) => Err(e), + } +} + +const DEFAULT_COMPUTE_LIMIT: usize = 200_000; + +async fn calculate_instructions_per_tx( + client: &RpcClient, + instructions: &[Instruction], + signer: &Keypair, +) -> Result { + let blockhash = get_latest_blockhash_with_retry(client).await?; + let test_tx = Transaction::new_signed_with_payer( + &[instructions[0].to_owned()], + Some(&signer.pubkey()), + &[signer], + blockhash, + ); + let response = client.simulate_transaction(&test_tx).await?; + if let Some(err) = response.value.clone().err { + debug!("Simulation error: {:?}", response.value); + return Err(err.into()); + } + let compute = response + .value + .units_consumed + .unwrap_or(DEFAULT_COMPUTE_LIMIT as u64); + + let serialized_size = Packet::from_data(None, &test_tx).unwrap().meta().size; + // additional size per ix + let size_per_ix = + instructions[0].accounts.len() * size_of::() + instructions[0].data.len(); + let size_max = (PACKET_DATA_SIZE - serialized_size + size_per_ix) / size_per_ix; + + let compute_max = DEFAULT_COMPUTE_LIMIT / compute as usize; + + Ok(size_max.min(compute_max)) +} + +async fn parallel_submit_transactions( + client: &RpcClient, + signer: &Arc, + // Each &[Instruction] represents a transaction + transactions: &[&[Instruction]], + // Map of signature to index of tx in `transactions` + executed_signatures: &mut HashMap, +) -> Result<(), TransactionExecutionError> { + // Converts arrays of instructions into transactions and submits them in parallel, in batches of 50 (arbitrary, to avoid spamming RPC) + // Saves signatures associated with the indexes of instructions it contains + + const TX_BATCH_SIZE: usize = 50; + for transaction_batch in transactions.chunks(TX_BATCH_SIZE) { + let recent_blockhash = get_latest_blockhash_with_retry(client).await.map_err(|e| { + TransactionExecutionError::TransactionClientError( + e.to_string(), + transactions.iter().map(|&tx| tx.to_vec()).collect(), + ) + })?; + // Convert instructions to transactions in batches and send them all, saving their signatures + let transactions: Vec = transaction_batch + .iter() + .map(|batch| { + Transaction::new_signed_with_payer( + batch, + Some(&signer.pubkey()), + &[signer.as_ref()], + recent_blockhash, + ) + }) + .collect(); + + let tx_futures = transactions + .iter() + .enumerate() + .map(|(i, tx)| async move { + let res = client.send_transaction(tx).await; + match res { + Ok(signature) => Some((signature, i)), + Err(e) => { + warn!("Send transaction failed: {:?}", e); + None + } + } + }) + .collect::>(); + + let results = futures::future::join_all(tx_futures).await; + for (signature, ix_index) in results.into_iter().flatten() { + executed_signatures.insert(signature, ix_index); + } + debug!( + "Transactions sent: {}, executed_signatures: {}", + transactions.len(), + executed_signatures.len() + ); + } + Ok(()) +} + +async fn parallel_confirm_transactions( + client: &RpcClient, + executed_signatures: &mut HashMap, +) { + // Confirmes TXs in batches of 256 (max allowed by RPC method) + const SIG_STATUS_BATCH_SIZE: usize = 256; + let signatures_to_confirm = executed_signatures.clone().into_keys().collect::>(); + + // Imperfect logic here: if a transaction is slow to confirm on first submission, and it can only be called once succesfully, + // it will be resubmitted and fail. Ideally on the next loop it will not be included in the instructions list + let confirmation_futures: Vec<_> = signatures_to_confirm + .chunks(SIG_STATUS_BATCH_SIZE) + .map(|sig_batch| async move { + match client.get_signature_statuses(sig_batch).await { + Ok(sig_batch_response) => sig_batch_response + .value + .iter() + .enumerate() + .map(|(i, sig_status)| (sig_batch[i], sig_status.clone())) + .collect::>(), + Err(_) => vec![], + } + }) + .collect(); + + let results = futures::future::join_all(confirmation_futures).await; + + let num_transactions_submitted = executed_signatures.len(); + for result_batch in results.iter() { + for (sig, result) in result_batch { + if let Some(status) = result { + if status.satisfies_commitment(client.commitment()) && status.err.is_none() { + executed_signatures.remove(sig); + } + } + } + } + + info!( + "{} transactions submitted, {} confirmed", + num_transactions_submitted, + num_transactions_submitted - executed_signatures.len() + ); +} + +pub async fn parallel_execute_instructions( + client: &RpcClient, + mut instructions: Vec, + signer: &Arc, + retry_count: u16, + confirmation_time: u64, +) -> Result<(), TransactionExecutionError> { + /* + Note: Assumes all instructions are equivalent in compute, equivalent in size, and can be executed in any order + + 1) Submits all instructions in parallel + 2) Waits a bit for them to confirm + 3) Checks which ones have confirmed, and keeps the ones that haven't + 4) Repeats retry_count number of times until all have confirmed + + Returns all remaining instructions that haven't executed so application can handle + */ + + if instructions.is_empty() { + return Ok(()); + } + + let instructions_per_tx = calculate_instructions_per_tx(client, &instructions, signer) + .await + .map_err(|e| { + TransactionExecutionError::ClientError(e.to_string(), instructions.to_vec()) + })?; + + for _ in 0..retry_count { + let mut executed_signatures: HashMap = HashMap::new(); + let instruction_batches: Vec<&[Instruction]> = + instructions.chunks(instructions_per_tx).collect(); + parallel_submit_transactions( + client, + signer, + &instruction_batches, + &mut executed_signatures, + ) + .await?; + + tokio::time::sleep(Duration::from_secs(confirmation_time)).await; + + parallel_confirm_transactions(client, &mut executed_signatures).await; + + // All have been executed + if executed_signatures.is_empty() { + return Ok(()); + } + + // Update instructions to the ones remaining + instructions = executed_signatures + .into_values() + .flat_map(|i| instruction_batches[i]) + .cloned() + .collect::>(); + } + Err(TransactionExecutionError::RetryError(instructions)) +} + +pub async fn parallel_execute_transactions( + client: &Arc, + mut transactions: Vec<&[Instruction]>, + signer: &Arc, + retry_count: u16, + confirmation_time: u64, +) -> Result<(), TransactionExecutionError> { + // Accepts a list of transactions (each represented as &[Instruction]) + // Executes them in parallel, returns the ones that failed to execute + // And repeats up to retry_count number of times until all have executed + if transactions.is_empty() { + return Ok(()); + } + + for _ in 0..retry_count { + let mut executed_signatures: HashMap = HashMap::new(); + parallel_submit_transactions(client, signer, &transactions, &mut executed_signatures) + .await?; + + tokio::time::sleep(Duration::from_secs(confirmation_time)).await; + + parallel_confirm_transactions(client, &mut executed_signatures).await; + + // All have been executed + if executed_signatures.is_empty() { + return Ok(()); + } + + // Update transactions to the ones remaining + transactions = executed_signatures + .into_values() + .map(|i| transactions[i]) + .collect::>(); + } + + Err(TransactionExecutionError::TransactionRetryError( + transactions.iter().map(|t| t.to_vec()).collect(), + )) +} + +pub async fn build_create_and_update_instructions< + T: Address + CreateTransaction + UpdateInstruction, +>( + client: &Arc, + account_entries: &[T], +) -> Result<(Vec>, Vec), MultipleAccountsError> { + let addresses = account_entries + .iter() + .map(|a| a.address()) + .collect::>(); + let existing_accounts_response: Vec> = + get_multiple_accounts_batched(&addresses, client).await?; + + let create_transactions = existing_accounts_response + .iter() + .zip(account_entries.iter()) + .filter_map(|(existing_account, entry)| { + if existing_account.is_none() { + Some(entry.create_transaction()) + } else { + None + } + }) + .collect::>(); + + Ok(( + create_transactions, + account_entries + .iter() + .map(|entry| entry.update_instruction()) + .collect(), + )) +} + +pub async fn submit_transactions( + client: &Arc, + transactions: Vec>, + keypair: &Arc, +) -> Result { + let mut stats = SubmitStats::default(); + let num_transactions = transactions.len(); + match parallel_execute_transactions( + client, + transactions.iter().map(AsRef::as_ref).collect(), + keypair, + 10, + 60, + ) + .await + { + Ok(_) => { + stats.successes = num_transactions as u64; + } + Err(e) => { + let transactions_len = match e.clone() { + TransactionExecutionError::TransactionRetryError(transactions) => { + transactions.len() + } + TransactionExecutionError::TransactionClientError(_, transactions) => { + transactions.len() + } + _ => { + error!("Hit unreachable statement in submit_transactions"); + unreachable!(); + } + }; + stats.successes = num_transactions as u64 - transactions_len as u64; + stats.errors = transactions_len as u64; + return Err((e, stats)); + } + } + Ok(stats) +} + +pub async fn submit_instructions( + client: &Arc, + instructions: Vec, + keypair: &Arc, +) -> Result { + let mut stats = SubmitStats::default(); + let num_instructions = instructions.len(); + match parallel_execute_instructions(client, instructions, keypair, 10, 30).await { + Ok(_) => { + stats.successes = num_instructions as u64; + } + Err(e) => { + let instructions_len = match e.clone() { + TransactionExecutionError::RetryError(instructions) => instructions.len(), + TransactionExecutionError::ClientError(_, instructions) => instructions.len(), + TransactionExecutionError::TransactionClientError(_, instructions) => { + instructions.concat().len() + } + _ => { + error!("Hit unreachable statement in submit_instructions"); + unreachable!(); + } + }; + stats.successes = num_instructions as u64 - instructions_len as u64; + stats.errors = instructions_len as u64; + return Err((e, stats)); + } + } + Ok(stats) +} + +pub async fn submit_create_and_update( + client: &Arc, + create_transactions: Vec>, + update_instructions: Vec, + keypair: &Arc, +) -> Result { + let mut stats = CreateUpdateStats::default(); + stats.creates = submit_transactions(client, create_transactions, keypair) + .await + .map_err(|(e, submit_stats)| { + stats.creates = submit_stats; + (e, stats) + })?; + stats.updates = submit_instructions(client, update_instructions, keypair) + .await + .map_err(|(e, submit_stats)| { + stats.updates = submit_stats; + (e, stats) + })?; + Ok(stats) +} diff --git a/keepers/validator-keeper/Cargo.toml b/keepers/validator-keeper/Cargo.toml new file mode 100644 index 00000000..c54003f3 --- /dev/null +++ b/keepers/validator-keeper/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "validator-keeper" +version = "0.1.0" +edition = "2021" +description = "Script to keep validator history accounts up to date" + +[dependencies] +anchor-lang = "0.28.0" +bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"] } +clap = { version = "4.3.0", features = ["derive", "env"] } +env_logger = "0.10.0" +futures = "0.3.21" +futures-util = "0.3.21" +jito-tip-distribution = { git = "https://github.com/jito-foundation/jito-programs", rev = "b8f8d315306d6ac32450bd1aa23b7c1322a83bc7" } +keeper-core = { path = "../keeper-core" } +log = "0.4.18" +solana-account-decoder = "1.16" +solana-clap-utils = "1.16" +solana-client = "1.16" +solana-gossip = "1.16" +solana-metrics = "1.16" +solana-net-utils = "1.16" +solana-program = "1.16" +solana-sdk = "1.16" +solana-streamer = "1.16" +thiserror = "1.0.37" +tokio = { version = "~1.14.1", features = ["full"] } +validator-history = { path = "../../programs/validator-history" } diff --git a/keepers/validator-keeper/src/gossip.rs b/keepers/validator-keeper/src/gossip.rs new file mode 100644 index 00000000..3be3cc43 --- /dev/null +++ b/keepers/validator-keeper/src/gossip.rs @@ -0,0 +1,409 @@ +use std::{ + net::{IpAddr, SocketAddr}, + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, RwLockReadGuard, + }, + time::Duration, +}; + +use anchor_lang::{InstructionData, ToAccountMetas}; +use bytemuck::{bytes_of, Pod, Zeroable}; +use keeper_core::{ + get_multiple_accounts_batched, get_vote_accounts_with_retry, submit_transactions, Address, + CreateTransaction, CreateUpdateStats, +}; +use log::error; +use solana_client::{nonblocking::rpc_client::RpcClient, rpc_response::RpcVoteAccountInfo}; +use solana_gossip::{ + crds::Crds, + crds_value::{CrdsData, CrdsValue, CrdsValueLabel}, +}; +use solana_metrics::datapoint_info; +use solana_sdk::{ + instruction::Instruction, + pubkey::Pubkey, + signature::{Keypair, Signable, Signature}, + signer::Signer, +}; +use tokio::time::sleep; +use validator_history::{ + self, + constants::{MAX_ALLOC_BYTES, MIN_VOTE_EPOCHS}, + Config, ValidatorHistory, +}; + +use crate::start_spy_server; + +#[derive(Clone, Debug)] +pub struct GossipEntry { + pub vote_account: Pubkey, + pub validator_history_account: Pubkey, + pub config: Pubkey, + pub signature: Signature, + pub message: Vec, + pub program_id: Pubkey, + pub identity: Pubkey, + pub signer: Pubkey, +} + +impl GossipEntry { + pub fn new( + vote_account: &Pubkey, + signature: &Signature, + message: &[u8], + program_id: &Pubkey, + identity: &Pubkey, + signer: &Pubkey, + ) -> Self { + let (validator_history_account, _) = Pubkey::find_program_address( + &[ValidatorHistory::SEED, &vote_account.to_bytes()], + program_id, + ); + let (config, _) = Pubkey::find_program_address(&[Config::SEED], program_id); + Self { + vote_account: *vote_account, + validator_history_account, + config, + signature: *signature, + message: message.to_vec(), + program_id: *program_id, + identity: *identity, + signer: *signer, + } + } +} + +impl Address for GossipEntry { + fn address(&self) -> Pubkey { + self.validator_history_account + } +} + +impl CreateTransaction for GossipEntry { + fn create_transaction(&self) -> Vec { + let mut ixs = vec![Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }]; + let num_reallocs = (ValidatorHistory::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1; + ixs.extend(vec![ + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + config: self.config, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + num_reallocs + ]); + ixs + } +} + +impl GossipEntry { + pub fn build_update_tx(&self) -> Vec { + let mut ixs = vec![build_verify_signature_ix( + self.signature.as_ref(), + self.identity.to_bytes(), + &self.message, + )]; + + ixs.push(Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::CopyGossipContactInfo { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + instructions: solana_program::sysvar::instructions::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::CopyGossipContactInfo {}.data(), + }); + ixs + } +} + +pub fn emit_gossip_datapoint(stats: CreateUpdateStats, runs_for_epoch: i64) { + datapoint_info!( + "gossip-upload-stats", + ("num_creates_success", stats.creates.successes, i64), + ("num_creates_error", stats.creates.errors, i64), + ("num_updates_success", stats.updates.successes, i64), + ("num_updates_error", stats.updates.errors, i64), + ("runs_for_epoch", runs_for_epoch, i64), + ); +} + +fn check_entry_valid(entry: &CrdsValue, validator_identity: Pubkey) -> bool { + match &entry.data { + CrdsData::LegacyContactInfo(_) => {} + CrdsData::LegacyVersion(_) => {} + CrdsData::Version(_) => {} + CrdsData::ContactInfo(_) => {} + _ => { + return false; + } + }; + + let signer = entry.pubkey(); + + if signer != validator_identity { + error!( + "Invalid gossip value retrieved for validator {}", + validator_identity + ); + return false; + } + true +} + +fn build_gossip_entry( + vote_account: &RpcVoteAccountInfo, + crds: &RwLockReadGuard<'_, Crds>, + program_id: Pubkey, + keypair: &Arc, +) -> Option> { + let validator_identity = Pubkey::from_str(&vote_account.node_pubkey).ok()?; + let validator_vote_pubkey = Pubkey::from_str(&vote_account.vote_pubkey).ok()?; + + let contact_info_key: CrdsValueLabel = CrdsValueLabel::ContactInfo(validator_identity); + let legacy_contact_info_key: CrdsValueLabel = + CrdsValueLabel::LegacyContactInfo(validator_identity); + let version_key: CrdsValueLabel = CrdsValueLabel::Version(validator_identity); + let legacy_version_key: CrdsValueLabel = CrdsValueLabel::LegacyVersion(validator_identity); + + // Current ContactInfo has both IP and Version, but LegacyContactInfo has only IP. + // So if there is not ContactInfo, we need to submit tx for LegacyContactInfo + one of (Version, LegacyVersion) + if let Some(entry) = crds.get::<&CrdsValue>(&contact_info_key) { + if !check_entry_valid(entry, validator_identity) { + return None; + } + Some(vec![GossipEntry::new( + &validator_vote_pubkey, + &entry.get_signature(), + &entry.signable_data(), + &program_id, + &entry.pubkey(), + &keypair.pubkey(), + )]) + } else { + let mut entries = vec![]; + if let Some(entry) = crds.get::<&CrdsValue>(&legacy_contact_info_key) { + if !check_entry_valid(entry, validator_identity) { + return None; + } + entries.push(GossipEntry::new( + &validator_vote_pubkey, + &entry.get_signature(), + &entry.signable_data(), + &program_id, + &entry.pubkey(), + &keypair.pubkey(), + )) + } + + if let Some(entry) = crds.get::<&CrdsValue>(&version_key) { + if !check_entry_valid(entry, validator_identity) { + return None; + } + entries.push(GossipEntry::new( + &validator_vote_pubkey, + &entry.get_signature(), + &entry.signable_data(), + &program_id, + &entry.pubkey(), + &keypair.pubkey(), + )) + } else if let Some(entry) = crds.get::<&CrdsValue>(&legacy_version_key) { + if !check_entry_valid(entry, validator_identity) { + return None; + } + entries.push(GossipEntry::new( + &validator_vote_pubkey, + &entry.get_signature(), + &entry.signable_data(), + &program_id, + &entry.pubkey(), + &keypair.pubkey(), + )) + } + Some(entries) + } +} + +pub async fn upload_gossip_values( + client: Arc, + keypair: Arc, + entrypoint: SocketAddr, + program_id: &Pubkey, +) -> Result, CreateUpdateStats)> { + let gossip_port = 0; + + let spy_socket_addr = SocketAddr::new( + IpAddr::from_str("0.0.0.0").expect("Invalid IP"), + gossip_port, + ); + let exit: Arc = Arc::new(AtomicBool::new(false)); + let (_gossip_service, cluster_info) = + start_spy_server(entrypoint, gossip_port, spy_socket_addr, &keypair, &exit); + + let vote_accounts = get_vote_accounts_with_retry(&client, MIN_VOTE_EPOCHS, None) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + // Wait for all active validators to be received + sleep(Duration::from_secs(30)).await; + + let gossip_entries = { + let crds = cluster_info + .gossip + .crds + .read() + .map_err(|e| (e.to_string().into(), CreateUpdateStats::default()))?; + + vote_accounts + .iter() + .filter_map(|vote_account| { + build_gossip_entry(vote_account, &crds, *program_id, &keypair) + }) + .flatten() + .collect::>() + }; + + exit.store(true, Ordering::Relaxed); + + let addresses = gossip_entries + .iter() + .map(|a| a.address()) + .collect::>(); + let existing_accounts_response = get_multiple_accounts_batched(&addresses, &client) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let create_transactions = existing_accounts_response + .iter() + .zip(gossip_entries.iter()) + .filter_map(|(existing_account, entry)| { + if existing_account.is_none() { + Some(entry.create_transaction()) + } else { + None + } + }) + .collect::>(); + + let update_transactions = gossip_entries + .iter() + .map(|entry| entry.build_update_tx()) + .collect::>(); + + let mut stats = CreateUpdateStats::default(); + stats.creates = submit_transactions(&client, create_transactions, &keypair) + .await + .map_err(|(e, submit_stats)| { + stats.creates = submit_stats; + (e.into(), stats) + })?; + + stats.updates = submit_transactions(&client, update_transactions, &keypair) + .await + .map_err(|(e, submit_stats)| { + stats.updates = submit_stats; + (e.into(), stats) + })?; + + Ok(stats) +} + +// CODE BELOW SLIGHTLY MODIFIED FROM +// solana_sdk/src/ed25519_instruction.rs + +pub const PUBKEY_SERIALIZED_SIZE: usize = 32; +pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; +pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14; +// bytemuck requires structures to be aligned +pub const SIGNATURE_OFFSETS_START: usize = 2; +pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START; + +#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)] +#[repr(C)] +pub struct Ed25519SignatureOffsets { + signature_offset: u16, // offset to ed25519 signature of 64 bytes + signature_instruction_index: u16, // instruction index to find signature + public_key_offset: u16, // offset to public key of 32 bytes + public_key_instruction_index: u16, // instruction index to find public key + message_data_offset: u16, // offset to start of message data + message_data_size: u16, // size of message data + message_instruction_index: u16, // index of instruction data to get message data +} + +// This code is modified from solana_sdk/src/ed25519_instruction.rs +// due to that function requiring a keypair, and generating the signature within the function. +// In our case we don't have the keypair, we just have the signature and pubkey. +pub fn build_verify_signature_ix( + signature: &[u8], + pubkey: [u8; 32], + message: &[u8], +) -> Instruction { + assert_eq!(pubkey.len(), PUBKEY_SERIALIZED_SIZE); + assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE); + + let mut instruction_data = Vec::with_capacity( + DATA_START + .saturating_add(SIGNATURE_SERIALIZED_SIZE) + .saturating_add(PUBKEY_SERIALIZED_SIZE) + .saturating_add(message.len()), + ); + + let num_signatures: u8 = 1; + let public_key_offset = DATA_START; + let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE); + let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE); + + // add padding byte so that offset structure is aligned + instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0])); + + let offsets = Ed25519SignatureOffsets { + signature_offset: signature_offset as u16, + signature_instruction_index: u16::MAX, + public_key_offset: public_key_offset as u16, + public_key_instruction_index: u16::MAX, + message_data_offset: message_data_offset as u16, + message_data_size: message.len() as u16, + message_instruction_index: u16::MAX, + }; + + instruction_data.extend_from_slice(bytes_of(&offsets)); + + debug_assert_eq!(instruction_data.len(), public_key_offset); + + instruction_data.extend_from_slice(&pubkey); + + debug_assert_eq!(instruction_data.len(), signature_offset); + + instruction_data.extend_from_slice(signature); + + debug_assert_eq!(instruction_data.len(), message_data_offset); + + instruction_data.extend_from_slice(message); + + Instruction { + program_id: solana_program::ed25519_program::id(), + accounts: vec![], + data: instruction_data, + } +} diff --git a/keepers/validator-keeper/src/lib.rs b/keepers/validator-keeper/src/lib.rs new file mode 100644 index 00000000..b2d0b489 --- /dev/null +++ b/keepers/validator-keeper/src/lib.rs @@ -0,0 +1,211 @@ +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::{atomic::AtomicBool, Arc}, +}; + +use anchor_lang::{AccountDeserialize, Discriminator}; +use keeper_core::CreateUpdateStats; +use log::error; +use solana_account_decoder::UiDataSliceConfig; +use solana_client::{ + client_error::ClientError, + nonblocking::rpc_client::RpcClient, + rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, + rpc_filter::{Memcmp, RpcFilterType}, +}; +use solana_gossip::{ + cluster_info::ClusterInfo, gossip_service::GossipService, + legacy_contact_info::LegacyContactInfo, +}; +use solana_metrics::datapoint_info; +use solana_net_utils::bind_in_range; +use solana_sdk::{ + pubkey::Pubkey, + signature::{Keypair, Signer}, +}; +use solana_streamer::socket::SocketAddrSpace; + +use jito_tip_distribution::state::TipDistributionAccount; +use validator_history::{ValidatorHistory, ValidatorHistoryEntry}; + +pub mod gossip; +pub mod mev_commission; +pub mod stake; +pub mod vote_account; + +pub type Error = Box; + +pub async fn get_tip_distribution_accounts( + rpc_client: &RpcClient, + tip_distribution_program: &Pubkey, + epoch: u64, +) -> Result, Error> { + const EPOCH_OFFSET: usize = 8 + 32 + 32 + 1; // Discriminator + Pubkey + Pubkey + size of "None" Option + let config = RpcProgramAccountsConfig { + filters: Some(vec![ + RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 0, + TipDistributionAccount::discriminator().into(), + )), + RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + EPOCH_OFFSET, + epoch.to_le_bytes().to_vec(), + )), + ]), + account_config: RpcAccountInfoConfig { + encoding: Some(solana_account_decoder::UiAccountEncoding::Base64), + data_slice: Some(UiDataSliceConfig { + offset: EPOCH_OFFSET, + length: 8, + }), + ..RpcAccountInfoConfig::default() + }, + ..RpcProgramAccountsConfig::default() + }; + let res = rpc_client + .get_program_accounts_with_config(tip_distribution_program, config) + .await?; + + // we actually don't care about the data slice, we just want the pubkey + Ok(res.into_iter().map(|x| x.0).collect::>()) +} + +pub fn emit_mev_commission_datapoint(stats: CreateUpdateStats) { + datapoint_info!( + "mev-commission-stats", + ("num_creates_success", stats.creates.successes, i64), + ("num_creates_error", stats.creates.errors, i64), + ("num_updates_success", stats.updates.successes, i64), + ("num_updates_error", stats.updates.errors, i64), + ); +} + +pub fn emit_validator_commission_datapoint(stats: CreateUpdateStats, runs_for_epoch: i64) { + datapoint_info!( + "vote-account-stats", + ("num_creates_success", stats.creates.successes, i64), + ("num_creates_error", stats.creates.errors, i64), + ("num_updates_success", stats.updates.successes, i64), + ("num_updates_error", stats.updates.errors, i64), + ("runs_for_epoch", runs_for_epoch, i64), + ); +} + +pub async fn emit_validator_history_metrics( + client: &Arc, + program_id: Pubkey, +) -> Result<(), ClientError> { + let epoch = client.get_epoch_info().await?; + + // Fetch every ValidatorHistory account + let gpa_config = RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 0, + ValidatorHistory::discriminator().into(), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(solana_account_decoder::UiAccountEncoding::Base64), + ..RpcAccountInfoConfig::default() + }, + ..RpcProgramAccountsConfig::default() + }; + let mut validator_history_accounts = client + .get_program_accounts_with_config(&program_id, gpa_config) + .await?; + + let validator_histories = validator_history_accounts + .iter_mut() + .filter_map(|(_, account)| { + ValidatorHistory::try_deserialize(&mut account.data.as_slice()).ok() + }) + .collect::>(); + + let mut ips = 0; + let mut versions = 0; + let mut types = 0; + let mut mev_comms = 0; + let mut comms = 0; + let mut epoch_credits = 0; + let mut stakes = 0; + let num_validators = validator_histories.len(); + let default = ValidatorHistoryEntry::default(); + for validator_history in validator_histories { + if let Some(entry) = validator_history.history.last() { + if entry.epoch as u64 != epoch.epoch { + continue; + } + if entry.ip != default.ip { + ips += 1; + } + if !(entry.version.major == default.version.major + && entry.version.minor == default.version.minor + && entry.version.patch == default.version.patch) + { + versions += 1; + } + if entry.client_type != default.client_type { + types += 1; + } + if entry.mev_commission != default.mev_commission { + mev_comms += 1; + } + if entry.commission != default.commission { + comms += 1; + } + if entry.epoch_credits != default.epoch_credits { + epoch_credits += 1; + } + if entry.activated_stake_lamports != default.activated_stake_lamports { + stakes += 1; + } + } + } + + datapoint_info!( + "validator-history-stats", + ("num_validator_histories", num_validators, i64), + ("num_ips", ips, i64), + ("num_versions", versions, i64), + ("num_client_types", types, i64), + ("num_mev_commissions", mev_comms, i64), + ("num_commissions", comms, i64), + ("num_epoch_credits", epoch_credits, i64), + ("num_stakes", stakes, i64), + ("slot_index", epoch.slot_index, i64), + ); + Ok(()) +} + +pub fn start_spy_server( + cluster_entrypoint: SocketAddr, + gossip_port: u16, + spy_socket_addr: SocketAddr, + keypair: &Arc, + exit: &Arc, +) -> (GossipService, Arc) { + // bind socket to expected port + let (_, gossip_socket) = bind_in_range( + IpAddr::V4(Ipv4Addr::UNSPECIFIED), + (gossip_port, gossip_port + 1), + ) + .map_err(|e| { + error!("Failed to bind to expected port"); + e + }) + .expect("Failed to bind to expected gossip port"); + + // connect to entrypoint and start spying on gossip + let node = ClusterInfo::gossip_contact_info(keypair.pubkey(), spy_socket_addr, 0); + let cluster_info = Arc::new(ClusterInfo::new( + node, + keypair.clone(), + SocketAddrSpace::Unspecified, + )); + + cluster_info.set_entrypoint(LegacyContactInfo::new_gossip_entry_point( + &cluster_entrypoint, + )); + let gossip_service = + GossipService::new(&cluster_info, None, gossip_socket, None, true, None, exit); + (gossip_service, cluster_info) +} diff --git a/keepers/validator-keeper/src/main.rs b/keepers/validator-keeper/src/main.rs new file mode 100644 index 00000000..ee749cde --- /dev/null +++ b/keepers/validator-keeper/src/main.rs @@ -0,0 +1,315 @@ +/* +This program starts several threads to manage the creation of validator history accounts, +and the updating of the various data feeds within the accounts. +It will emits metrics for each data feed, if env var SOLANA_METRICS_CONFIG is set to a valid influx server. +*/ + +use std::{collections::HashMap, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; + +use clap::{arg, command, Parser}; +use keeper_core::{Cluster, CreateUpdateStats}; +use log::*; +use solana_client::nonblocking::rpc_client::RpcClient; +use solana_metrics::{datapoint_error, set_host_id}; +use solana_sdk::{ + pubkey::Pubkey, + signature::{read_keypair_file, Keypair}, +}; +use tokio::time::sleep; +use validator_keeper::{ + emit_mev_commission_datapoint, emit_validator_commission_datapoint, + emit_validator_history_metrics, + gossip::{emit_gossip_datapoint, upload_gossip_values}, + mev_commission::update_mev_commission, + stake::{emit_stake_history_datapoint, update_stake_history}, + vote_account::update_vote_accounts, +}; + +#[derive(Parser, Debug)] +#[command(about = "Keeps commission history accounts up to date")] +struct Args { + /// RPC URL for the cluster + #[arg( + short, + long, + env, + default_value = "https://api.mainnet-beta.solana.com" + )] + json_rpc_url: String, + + /// Gossip entrypoint in the form of URL:PORT + #[arg(short, long, env)] + gossip_entrypoint: String, + + /// Path to keypair used to pay for account creation and execute transactions + #[arg(short, long, env, default_value = "~/.config/solana/id.json")] + keypair: PathBuf, + + /// Path to keypair used specifically for submitting stake upload transactions + #[arg(short, long, env)] + stake_upload_keypair: Option, + + /// Validator history program ID (Pubkey as base58 string) + #[arg(short, long, env)] + program_id: Pubkey, + + /// Tip distribution program ID (Pubkey as base58 string) + #[arg(short, long, env)] + tip_distribution_program_id: Pubkey, + + // Loop interval time (default 300 sec) + #[arg(short, long, env, default_value = "300")] + interval: u64, + + #[arg(short, long, env, default_value_t = Cluster::Mainnet)] + cluster: Cluster, +} + +async fn monitoring_loop(client: Arc, program_id: Pubkey, interval: u64) { + loop { + match emit_validator_history_metrics(&client, program_id).await { + Ok(_) => {} + Err(e) => { + error!("Failed to emit validator history metrics: {}", e); + } + } + sleep(Duration::from_secs(interval)).await; + } +} + +async fn mev_commission_loop( + client: Arc, + keypair: Arc, + commission_history_program_id: Pubkey, + tip_distribution_program_id: Pubkey, + interval: u64, +) { + let mut prev_epoch = 0; + // {TipDistributionAccount : VoteAccount} + let mut validators_updated: HashMap = HashMap::new(); + + loop { + // Continuously runs throughout an epoch, polling for new tip distribution accounts + // and submitting update txs when new accounts are detected + match update_mev_commission( + client.clone(), + keypair.clone(), + &commission_history_program_id, + &tip_distribution_program_id, + &mut validators_updated, + &mut prev_epoch, + ) + .await + { + Ok(stats) => { + emit_mev_commission_datapoint(stats); + sleep(Duration::from_secs(interval)).await; + } + Err((e, stats)) => { + emit_mev_commission_datapoint(stats); + datapoint_error!("mev-commission-error", ("error", e.to_string(), String),); + sleep(Duration::from_secs(5)).await; + } + }; + } +} + +async fn vote_account_loop( + rpc_client: Arc, + keypair: Arc, + program_id: Pubkey, + interval: u64, +) { + let mut runs_for_epoch = 0; + let mut current_epoch = 0; + let mut stats = CreateUpdateStats::default(); + loop { + let epoch_info = match rpc_client.get_epoch_info().await { + Ok(epoch_info) => epoch_info, + Err(e) => { + error!("Failed to get epoch info: {}", e); + sleep(Duration::from_secs(5)).await; + continue; + } + }; + if current_epoch != epoch_info.epoch { + runs_for_epoch = 0; + } + // Run at 10%, 50% and 90% completion of epoch + let should_run = (epoch_info.slot_index > epoch_info.slots_in_epoch / 10 + && runs_for_epoch < 1) + || (epoch_info.slot_index > epoch_info.slots_in_epoch / 2 && runs_for_epoch < 2) + || (epoch_info.slot_index > epoch_info.slots_in_epoch * 9 / 10 && runs_for_epoch < 3); + + if should_run { + stats = + match update_vote_accounts(rpc_client.clone(), keypair.clone(), program_id).await { + Ok(stats) => { + runs_for_epoch += 1; + sleep(Duration::from_secs(interval)).await; + stats + } + Err((e, stats)) => { + datapoint_error!("vote-account-error", ("error", e.to_string(), String),); + stats + } + }; + } + current_epoch = epoch_info.epoch; + emit_validator_commission_datapoint(stats, runs_for_epoch); + sleep(Duration::from_secs(interval)).await; + } +} + +async fn stake_upload_loop( + client: Arc, + keypair: Arc, + program_id: Pubkey, + interval: u64, +) { + let mut runs_for_epoch = 0; + let mut current_epoch = 0; + + loop { + let epoch_info = match client.get_epoch_info().await { + Ok(epoch_info) => epoch_info, + Err(e) => { + error!("Failed to get epoch info: {}", e); + sleep(Duration::from_secs(5)).await; + continue; + } + }; + let epoch = epoch_info.epoch; + let mut stats = CreateUpdateStats::default(); + + if current_epoch != epoch { + runs_for_epoch = 0; + } + // Run at 0.1%, 50% and 90% completion of epoch + let should_run = (epoch_info.slot_index > epoch_info.slots_in_epoch / 1000 + && runs_for_epoch < 1) + || (epoch_info.slot_index > epoch_info.slots_in_epoch / 2 && runs_for_epoch < 2) + || (epoch_info.slot_index > epoch_info.slots_in_epoch * 9 / 10 && runs_for_epoch < 3); + if should_run { + stats = match update_stake_history(client.clone(), keypair.clone(), &program_id).await { + Ok(run_stats) => { + runs_for_epoch += 1; + run_stats + } + Err((e, run_stats)) => { + datapoint_error!("stake-history-error", ("error", e.to_string(), String),); + run_stats + } + }; + } + + current_epoch = epoch; + emit_stake_history_datapoint(stats, runs_for_epoch); + sleep(Duration::from_secs(interval)).await; + } +} + +async fn gossip_upload_loop( + client: Arc, + keypair: Arc, + program_id: Pubkey, + entrypoint: SocketAddr, + interval: u64, +) { + let mut runs_for_epoch = 0; + let mut current_epoch = 0; + loop { + let epoch_info = match client.get_epoch_info().await { + Ok(epoch_info) => epoch_info, + Err(e) => { + error!("Failed to get epoch info: {}", e); + sleep(Duration::from_secs(5)).await; + continue; + } + }; + let epoch = epoch_info.epoch; + if current_epoch != epoch { + runs_for_epoch = 0; + } + // Run at 0%, 50% and 90% completion of epoch + let should_run = runs_for_epoch < 1 + || (epoch_info.slot_index > epoch_info.slots_in_epoch / 2 && runs_for_epoch < 2) + || (epoch_info.slot_index > epoch_info.slots_in_epoch * 9 / 10 && runs_for_epoch < 3); + + let mut stats = CreateUpdateStats::default(); + if should_run { + stats = match upload_gossip_values( + client.clone(), + keypair.clone(), + entrypoint, + &program_id, + ) + .await + { + Ok(stats) => { + runs_for_epoch += 1; + stats + } + Err((e, stats)) => { + datapoint_error!("gossip-upload-error", ("error", e.to_string(), String),); + stats + } + }; + } + current_epoch = epoch; + emit_gossip_datapoint(stats, runs_for_epoch); + sleep(Duration::from_secs(interval)).await; + } +} + +#[tokio::main] +async fn main() { + env_logger::init(); + let args = Args::parse(); + set_host_id(format!("{}", args.cluster)); + + let client = Arc::new(RpcClient::new_with_timeout( + args.json_rpc_url.clone(), + Duration::from_secs(60), + )); + let keypair = Arc::new(read_keypair_file(args.keypair).expect("Failed reading keypair file")); + let entrypoint = solana_net_utils::parse_host_port(&args.gossip_entrypoint) + .expect("Failed to parse host and port from gossip entrypoint"); + + info!("Starting validator history keeper..."); + + tokio::spawn(monitoring_loop( + Arc::clone(&client), + args.program_id, + args.interval, + )); + + tokio::spawn(vote_account_loop( + Arc::clone(&client), + Arc::clone(&keypair), + args.program_id, + args.interval, + )); + + if let Some(stake_upload_keypair) = args.stake_upload_keypair { + let stake_upload_keypair = Arc::new( + read_keypair_file(stake_upload_keypair).expect("Failed reading stake keypair file"), + ); + tokio::spawn(stake_upload_loop( + Arc::clone(&client), + Arc::clone(&stake_upload_keypair), + args.program_id, + args.interval, + )); + } + + tokio::spawn(mev_commission_loop( + Arc::clone(&client), + Arc::clone(&keypair), + args.program_id, + args.tip_distribution_program_id, + args.interval, + )); + + gossip_upload_loop(client, keypair, args.program_id, entrypoint, args.interval).await; +} diff --git a/keepers/validator-keeper/src/mev_commission.rs b/keepers/validator-keeper/src/mev_commission.rs new file mode 100644 index 00000000..490854d4 --- /dev/null +++ b/keepers/validator-keeper/src/mev_commission.rs @@ -0,0 +1,217 @@ +use std::{collections::HashMap, str::FromStr, sync::Arc}; + +use anchor_lang::{InstructionData, ToAccountMetas}; +use jito_tip_distribution::sdk::derive_tip_distribution_account_address; +use keeper_core::{ + build_create_and_update_instructions, get_multiple_accounts_batched, + get_vote_accounts_with_retry, submit_create_and_update, Address, CreateTransaction, + CreateUpdateStats, MultipleAccountsError, TransactionExecutionError, UpdateInstruction, +}; +use log::error; +use solana_client::rpc_response::RpcVoteAccountInfo; +use solana_client::{client_error::ClientError, nonblocking::rpc_client::RpcClient}; +use solana_program::{instruction::Instruction, pubkey::Pubkey}; +use solana_sdk::{signature::Keypair, signer::Signer}; +use thiserror::Error as ThisError; +use validator_history::constants::MIN_VOTE_EPOCHS; +use validator_history::{constants::MAX_ALLOC_BYTES, Config, ValidatorHistory}; + +#[derive(ThisError, Debug)] +pub enum MevCommissionError { + #[error(transparent)] + ClientError(#[from] ClientError), + #[error(transparent)] + TransactionExecutionError(#[from] TransactionExecutionError), + #[error(transparent)] + MultipleAccountsError(#[from] MultipleAccountsError), +} + +#[derive(Clone)] +pub struct ValidatorMevCommissionEntry { + pub vote_account: Pubkey, + pub tip_distribution_account: Pubkey, + pub validator_history_account: Pubkey, + pub config: Pubkey, + pub program_id: Pubkey, + pub signer: Pubkey, +} + +impl ValidatorMevCommissionEntry { + pub fn new( + vote_account: &RpcVoteAccountInfo, + epoch: u64, + program_id: &Pubkey, + tip_distribution_program_id: &Pubkey, + signer: &Pubkey, + ) -> Self { + let vote_account = Pubkey::from_str(&vote_account.vote_pubkey) + .map_err(|e| { + error!("Invalid vote account pubkey"); + e + }) + .expect("Invalid vote account pubkey"); + let (validator_history_account, _) = Pubkey::find_program_address( + &[ValidatorHistory::SEED, &vote_account.to_bytes()], + program_id, + ); + let (tip_distribution_account, _) = derive_tip_distribution_account_address( + tip_distribution_program_id, + &vote_account, + epoch, + ); + let (config, _) = Pubkey::find_program_address(&[Config::SEED], program_id); + Self { + vote_account, + tip_distribution_account, + validator_history_account, + config, + program_id: *program_id, + signer: *signer, + } + } +} + +impl Address for ValidatorMevCommissionEntry { + fn address(&self) -> Pubkey { + self.validator_history_account + } +} + +impl CreateTransaction for ValidatorMevCommissionEntry { + fn create_transaction(&self) -> Vec { + let mut ixs = vec![Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }]; + let num_reallocs = (ValidatorHistory::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1; + ixs.extend(vec![ + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + config: self.config, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + num_reallocs + ]); + ixs + } +} + +impl UpdateInstruction for ValidatorMevCommissionEntry { + fn update_instruction(&self) -> Instruction { + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::UpdateMevCommission { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + tip_distribution_account: self.tip_distribution_account, + config: self.config, + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::UpdateMevCommission {}.data(), + } + } +} + +pub async fn update_mev_commission( + client: Arc, + keypair: Arc, + validator_history_program_id: &Pubkey, + tip_distribution_program_id: &Pubkey, + validators_updated: &mut HashMap, + prev_epoch: &mut u64, +) -> Result { + let epoch = client + .get_epoch_info() + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))? + .epoch; + if epoch > *prev_epoch { + validators_updated.clear(); + } + *prev_epoch = epoch; + + let vote_accounts = get_vote_accounts_with_retry(&client, MIN_VOTE_EPOCHS, None) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let entries = vote_accounts + .iter() + .map(|vote_account| { + ValidatorMevCommissionEntry::new( + vote_account, + epoch, + validator_history_program_id, + tip_distribution_program_id, + &keypair.pubkey(), + ) + }) + .collect::>(); + + let existing_entries = get_existing_entries(client.clone(), &entries) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let entries_to_update = existing_entries + .into_iter() + .filter(|entry| !validators_updated.contains_key(&entry.tip_distribution_account)) + .collect::>(); + let (create_transactions, update_instructions) = + build_create_and_update_instructions(&client, &entries_to_update) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let submit_result = + submit_create_and_update(&client, create_transactions, update_instructions, &keypair).await; + if submit_result.is_ok() { + for ValidatorMevCommissionEntry { + vote_account, + tip_distribution_account, + .. + } in entries_to_update + { + validators_updated.insert(tip_distribution_account, vote_account); + } + } + submit_result.map_err(|(e, stats)| (e.into(), stats)) +} + +async fn get_existing_entries( + client: Arc, + entries: &[ValidatorMevCommissionEntry], +) -> Result, MultipleAccountsError> { + /* Filters tip distribution tuples to the addresses, then fetches accounts to see which ones exist */ + let tip_distribution_addresses = entries + .iter() + .map(|entry| entry.tip_distribution_account) + .collect::>(); + + let accounts = get_multiple_accounts_batched(&tip_distribution_addresses, &client).await?; + let result = accounts + .iter() + .enumerate() + .filter_map(|(i, account_data)| { + if account_data.is_some() { + Some(entries[i].clone()) + } else { + None + } + }) + .collect::>(); + // Fetch existing tip distribution accounts for this epoch + Ok(result) +} diff --git a/keepers/validator-keeper/src/stake.rs b/keepers/validator-keeper/src/stake.rs new file mode 100644 index 00000000..a3bb5a86 --- /dev/null +++ b/keepers/validator-keeper/src/stake.rs @@ -0,0 +1,352 @@ +use std::{collections::HashMap, str::FromStr, sync::Arc}; + +use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas}; +use keeper_core::{ + build_create_and_update_instructions, get_vote_accounts_with_retry, submit_create_and_update, + submit_instructions, Address, CreateTransaction, CreateUpdateStats, MultipleAccountsError, + SubmitStats, TransactionExecutionError, UpdateInstruction, +}; +use log::error; +use solana_client::{ + client_error::ClientError, + nonblocking::rpc_client::RpcClient, + rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, + rpc_filter::{Memcmp, RpcFilterType}, + rpc_response::RpcVoteAccountInfo, +}; +use solana_metrics::datapoint_info; +use solana_sdk::{ + commitment_config::CommitmentConfig, instruction::Instruction, pubkey::Pubkey, + signature::Keypair, signer::Signer, +}; +use thiserror::Error as ThisError; +use validator_history::{ + constants::{MAX_ALLOC_BYTES, MIN_VOTE_EPOCHS}, + state::{Config, ValidatorHistory}, +}; + +#[derive(ThisError, Debug)] +pub enum StakeHistoryError { + #[error(transparent)] + ClientError(#[from] ClientError), + #[error(transparent)] + TransactionExecutionError(#[from] TransactionExecutionError), + #[error(transparent)] + MultipleAccountsError(#[from] MultipleAccountsError), + #[error("Epoch mismatch")] + EpochMismatch, +} + +pub struct StakeHistoryEntry { + pub stake: u64, + pub rank: u32, + pub is_superminority: bool, + pub vote_account: Pubkey, + pub address: Pubkey, + pub config_address: Pubkey, + pub signer: Pubkey, + pub program_id: Pubkey, + pub epoch: u64, +} + +impl StakeHistoryEntry { + pub fn new( + vote_account: &RpcVoteAccountInfo, + program_id: &Pubkey, + signer: &Pubkey, + epoch: u64, + rank: u32, + is_superminority: bool, + ) -> StakeHistoryEntry { + let vote_pubkey = Pubkey::from_str(&vote_account.vote_pubkey) + .map_err(|e| { + error!("Invalid vote account pubkey"); + e + }) + .expect("Invalid vote account pubkey"); + let (address, _) = Pubkey::find_program_address( + &[ValidatorHistory::SEED, &vote_pubkey.to_bytes()], + program_id, + ); + let (config_address, _) = Pubkey::find_program_address(&[Config::SEED], program_id); + + StakeHistoryEntry { + stake: vote_account.activated_stake, + rank, + is_superminority, + vote_account: vote_pubkey, + address, + config_address, + signer: *signer, + program_id: *program_id, + epoch, + } + } +} + +impl CreateTransaction for StakeHistoryEntry { + fn create_transaction(&self) -> Vec { + let mut ixs = vec![Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: self.address, + vote_account: self.vote_account, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }]; + let num_reallocs = (ValidatorHistory::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1; + ixs.extend(vec![ + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: self.address, + vote_account: self.vote_account, + config: self.config_address, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + num_reallocs + ]); + ixs + } +} + +impl Address for StakeHistoryEntry { + fn address(&self) -> Pubkey { + self.address + } +} + +impl UpdateInstruction for StakeHistoryEntry { + fn update_instruction(&self) -> Instruction { + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::UpdateStakeHistory { + validator_history_account: self.address, + vote_account: self.vote_account, + config: self.config_address, + stake_authority: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::UpdateStakeHistory { + lamports: self.stake, + epoch: self.epoch, + rank: self.rank, + is_superminority: self.is_superminority, + } + .data(), + } + } +} + +/* +Calculates ordering of validators by stake, assigning a 0..N rank (validator 0 has the most stake), +and returns the index at which all validators before are in the superminority. 0-indexed. +*/ +fn get_stake_rank_map_and_superminority_count( + vote_accounts: &[RpcVoteAccountInfo], +) -> (HashMap, u32) { + let mut stake_vec = vote_accounts + .iter() + .map(|va| (va.vote_pubkey.clone(), va.activated_stake)) + .collect::>(); + + let total_stake = stake_vec.iter().map(|(_, stake)| *stake).sum::(); + stake_vec.sort_by(|a, b| b.1.cmp(&a.1)); + + let mut cumulative_stake = 0; + let mut superminority_threshold = 0; + for (i, (_, stake)) in stake_vec.iter().enumerate() { + cumulative_stake += stake; + if cumulative_stake > total_stake / 3 { + superminority_threshold = i as u32; + break; + } + } + let stake_rank_map = HashMap::from_iter( + stake_vec + .into_iter() + .enumerate() + .map(|(i, (vote_pubkey, _))| (vote_pubkey, i as u32)), + ); + + (stake_rank_map, superminority_threshold) +} + +pub async fn update_stake_history( + client: Arc, + keypair: Arc, + program_id: &Pubkey, +) -> Result { + let vote_accounts = get_vote_accounts_with_retry( + &client, + MIN_VOTE_EPOCHS, + Some(CommitmentConfig::finalized()), + ) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + // Need to ensure that the response contains update stake amounts for the current epoch, + // so we find the largest epoch a validator has voted on to confirm the data is fresh + let max_vote_account_epoch = vote_accounts + .iter() + .flat_map(|va| va.epoch_credits.clone()) + .map(|(epoch, _, _)| epoch) + .max() + .unwrap_or(0); + + let (stake_rank_map, superminority_threshold) = + get_stake_rank_map_and_superminority_count(&vote_accounts); + + let epoch = client + .get_epoch_info_with_commitment(CommitmentConfig::finalized()) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))? + .epoch; + + if max_vote_account_epoch != epoch { + return Err(( + StakeHistoryError::EpochMismatch, + CreateUpdateStats::default(), + )); + } + + let stake_history_entries = vote_accounts + .iter() + .map(|va| { + let rank = stake_rank_map[&va.vote_pubkey.clone()]; + let is_superminority = rank <= superminority_threshold; + StakeHistoryEntry::new( + va, + program_id, + &keypair.pubkey(), + epoch, + rank, + is_superminority, + ) + }) + .collect::>(); + + let (create_transactions, update_instructions) = + build_create_and_update_instructions(&client, &stake_history_entries) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + submit_create_and_update(&client, create_transactions, update_instructions, &keypair) + .await + .map_err(|(e, stats)| (e.into(), stats)) +} + +/* + Utility to recompute the superminority and rank fields for all validators from start_epoch to end_epoch. + Will over-write the on-chain data, so should only be used when the on-chain data is corrupted. +*/ +pub async fn _recompute_superminority_and_rank( + client: Arc, + keypair: Arc, + program_id: &Pubkey, + start_epoch: u64, + end_epoch: u64, +) -> Result<(), (StakeHistoryError, SubmitStats)> { + // Fetch every ValidatorHistory account + let gpa_config = RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 0, + ValidatorHistory::discriminator().into(), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(solana_account_decoder::UiAccountEncoding::Base64), + ..RpcAccountInfoConfig::default() + }, + ..RpcProgramAccountsConfig::default() + }; + let validator_history_accounts = client + .get_program_accounts_with_config(&validator_history::id(), gpa_config) + .await + .expect("Failed to get validator history accounts"); + + let validator_histories = validator_history_accounts + .iter() + .map(|(_, account)| { + let validator_history = ValidatorHistory::try_deserialize(&mut account.data.as_slice()) + .expect("Failed to deserialize validator history account"); + validator_history + }) + .collect::>(); + + for epoch in start_epoch..=end_epoch { + // Get entry for each validator for this epoch + let vote_accounts: Vec = validator_histories + .iter() + .filter_map(|validator| { + validator + .history + .arr + .iter() + .find(|entry| { + entry.epoch == epoch as u16 && entry.activated_stake_lamports != u64::MAX + }) + .map(|entry| { + // All values except vote_pubkey and activated_stake are unused + RpcVoteAccountInfo { + vote_pubkey: validator.vote_account.to_string(), + activated_stake: entry.activated_stake_lamports, + epoch_credits: vec![], + commission: 0, + root_slot: 0, + node_pubkey: "".to_string(), + epoch_vote_account: false, + last_vote: 0, + } + }) + }) + .collect(); + let (stake_rank_map, superminority_threshold) = + get_stake_rank_map_and_superminority_count(&vote_accounts); + + let stake_history_entries = vote_accounts + .iter() + .map(|va| { + let rank = stake_rank_map[&va.vote_pubkey.clone()]; + let is_superminority = rank <= superminority_threshold; + StakeHistoryEntry::new( + va, + program_id, + &keypair.pubkey(), + epoch, + rank, + is_superminority, + ) + }) + .collect::>(); + + let update_instructions = stake_history_entries + .iter() + .map(|entry| entry.update_instruction()) + .collect::>(); + + match submit_instructions(&client, update_instructions, &keypair).await { + Ok(_) => println!("completed epoch {}", epoch), + Err((e, stats)) => return Err((e.into(), stats)), + }; + } + + Ok(()) +} + +pub fn emit_stake_history_datapoint(stats: CreateUpdateStats, runs_for_epoch: i64) { + datapoint_info!( + "stake-history-stats", + ("num_creates_success", stats.creates.successes, i64), + ("num_creates_error", stats.creates.errors, i64), + ("num_updates_success", stats.updates.successes, i64), + ("num_updates_error", stats.updates.errors, i64), + ("runs_for_epoch", runs_for_epoch, i64), + ); +} diff --git a/keepers/validator-keeper/src/vote_account.rs b/keepers/validator-keeper/src/vote_account.rs new file mode 100644 index 00000000..49ba0c46 --- /dev/null +++ b/keepers/validator-keeper/src/vote_account.rs @@ -0,0 +1,145 @@ +use std::str::FromStr; +use std::sync::Arc; + +use anchor_lang::{InstructionData, ToAccountMetas}; +use keeper_core::{ + build_create_and_update_instructions, get_vote_accounts_with_retry, submit_create_and_update, + Address, CreateTransaction, CreateUpdateStats, MultipleAccountsError, + TransactionExecutionError, UpdateInstruction, +}; +use log::error; +use solana_client::rpc_response::RpcVoteAccountInfo; +use solana_client::{client_error::ClientError, nonblocking::rpc_client::RpcClient}; +use solana_program::{instruction::Instruction, pubkey::Pubkey}; +use solana_sdk::{signature::Keypair, signer::Signer}; +use thiserror::Error as ThisError; +use validator_history::constants::{MAX_ALLOC_BYTES, MIN_VOTE_EPOCHS}; +use validator_history::state::ValidatorHistory; +use validator_history::Config; + +#[derive(ThisError, Debug)] +pub enum UpdateCommissionError { + #[error(transparent)] + ClientError(#[from] ClientError), + #[error(transparent)] + TransactionExecutionError(#[from] TransactionExecutionError), + #[error(transparent)] + MultipleAccountsError(#[from] MultipleAccountsError), +} + +pub struct CopyVoteAccountEntry { + pub vote_account: Pubkey, + pub validator_history_account: Pubkey, + pub config_address: Pubkey, + pub program_id: Pubkey, + pub signer: Pubkey, +} + +impl CopyVoteAccountEntry { + pub fn new(vote_account: &RpcVoteAccountInfo, program_id: &Pubkey, signer: &Pubkey) -> Self { + let vote_account = Pubkey::from_str(&vote_account.vote_pubkey) + .map_err(|e| { + error!("Invalid vote account pubkey"); + e + }) + .expect("Invalid vote account pubkey"); + let (validator_history_account, _) = Pubkey::find_program_address( + &[ValidatorHistory::SEED, &vote_account.to_bytes()], + program_id, + ); + let (config_address, _) = Pubkey::find_program_address(&[Config::SEED], program_id); + Self { + vote_account, + validator_history_account, + config_address, + program_id: *program_id, + signer: *signer, + } + } +} + +impl Address for CopyVoteAccountEntry { + fn address(&self) -> Pubkey { + self.validator_history_account + } +} + +impl CreateTransaction for CopyVoteAccountEntry { + fn create_transaction(&self) -> Vec { + let mut ixs = vec![Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }]; + let num_reallocs = (ValidatorHistory::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1; + ixs.extend(vec![ + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + config: self.config_address, + system_program: solana_program::system_program::id(), + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + num_reallocs + ]); + ixs + } +} + +impl UpdateInstruction for CopyVoteAccountEntry { + fn update_instruction(&self) -> Instruction { + Instruction { + program_id: self.program_id, + accounts: validator_history::accounts::CopyVoteAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + signer: self.signer, + } + .to_account_metas(None), + data: validator_history::instruction::CopyVoteAccount {}.data(), + } + } +} + +pub async fn update_vote_accounts( + rpc_client: Arc, + keypair: Arc, + validator_history_program_id: Pubkey, +) -> Result { + let stats = CreateUpdateStats::default(); + + let vote_accounts = get_vote_accounts_with_retry(&rpc_client, MIN_VOTE_EPOCHS, None) + .await + .map_err(|e| (e.into(), stats))?; + + let entries = vote_accounts + .iter() + .map(|va| CopyVoteAccountEntry::new(va, &validator_history_program_id, &keypair.pubkey())) + .collect::>(); + + let (create_transactions, update_instructions) = + build_create_and_update_instructions(&rpc_client, &entries) + .await + .map_err(|e| (e.into(), stats))?; + + let submit_result = submit_create_and_update( + &rpc_client, + create_transactions, + update_instructions, + &keypair, + ) + .await; + + submit_result.map_err(|(e, stats)| (e.into(), stats)) +} diff --git a/programs/validator-history/Cargo.toml b/programs/validator-history/Cargo.toml new file mode 100644 index 00000000..4a54cf02 --- /dev/null +++ b/programs/validator-history/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "validator-history" +version = "0.1.0" +description = "Program for tracking validator metrics on chain" +edition = "2021" +license = "Apache-2.0" +authors = ["Jito Foundation "] + +[package.metadata.cargo-udeps.ignore] +normal = ["solana-security-txt"] + +[lib] +crate-type = ["cdylib", "lib"] +name = "validator_history" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.28.0" +bincode = "1.3.3" +bytemuck = { version = "1.13.1", features = ["derive", "min_const_generics"] } +cfg-if = "1.0.0" +jito-tip-distribution = { git = "https://github.com/jito-foundation/jito-programs", rev = "b8f8d315306d6ac32450bd1aa23b7c1322a83bc7", features = ["no-entrypoint"] } +semver = "1.0.17" +serde = "1.0.183" +serde_derive = "1.0.183" +solana-security-txt = "1.1.0" +static_assertions = "1.1.0" +thiserror = "1.0.37" +type-layout = "0.2" +validator-history-vote-state = { path = "../../utils/vote-state" } diff --git a/programs/validator-history/Xargo.toml b/programs/validator-history/Xargo.toml new file mode 100644 index 00000000..475fb71e --- /dev/null +++ b/programs/validator-history/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/programs/validator-history/src/constants.rs b/programs/validator-history/src/constants.rs new file mode 100644 index 00000000..990e9342 --- /dev/null +++ b/programs/validator-history/src/constants.rs @@ -0,0 +1,2 @@ +pub const MAX_ALLOC_BYTES: usize = 10240; +pub const MIN_VOTE_EPOCHS: usize = 5; diff --git a/programs/validator-history/src/crds_value.rs b/programs/validator-history/src/crds_value.rs new file mode 100644 index 00000000..8f238511 --- /dev/null +++ b/programs/validator-history/src/crds_value.rs @@ -0,0 +1,311 @@ +use { + crate::serde_varint, + anchor_lang::solana_program::{ + pubkey::Pubkey, + sanitize::{Sanitize, SanitizeError}, + short_vec, + }, + serde::{Deserialize, Deserializer, Serialize}, + static_assertions::const_assert_eq, + std::net::{IpAddr, Ipv4Addr, SocketAddr}, + thiserror::Error, +}; + +/////// From solana/gossip/src/crds_value.rs + +pub const MAX_WALLCLOCK: u64 = 1_000_000_000_000_000; + +/// CrdsData that defines the different types of items CrdsValues can hold +/// * Merge Strategy - Latest wallclock is picked +/// * LowestSlot index is deprecated +#[allow(clippy::large_enum_variant)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub enum CrdsData { + LegacyContactInfo(LegacyContactInfo), + Vote, + LowestSlot, + LegacySnapshotHashes, + AccountsHashes, + EpochSlots, + LegacyVersion(LegacyVersion), + Version(Version2), + NodeInstance, + DuplicateShred, + SnapshotHashes, + ContactInfo(ContactInfo), +} + +/// Copied from solana/version/src/lib.rs + +#[derive(Debug, Eq, PartialEq)] +enum ClientId { + SolanaLabs, + JitoLabs, + Firedancer, + // If new variants are added, update From and TryFrom. + Unknown(u16), +} + +#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct Version { + #[serde(with = "serde_varint")] + pub major: u16, + #[serde(with = "serde_varint")] + pub minor: u16, + #[serde(with = "serde_varint")] + pub patch: u16, + pub commit: u32, // first 4 bytes of the sha1 commit hash + pub feature_set: u32, // first 4 bytes of the FeatureSet identifier + #[serde(with = "serde_varint")] + pub client: u16, +} + +impl Version { + pub fn as_semver_version(&self) -> semver::Version { + semver::Version::new(self.major as u64, self.minor as u64, self.patch as u64) + } + + #[allow(dead_code)] + fn client(&self) -> ClientId { + ClientId::from(self.client) + } +} + +impl From for ClientId { + fn from(client: u16) -> Self { + match client { + 0u16 => Self::SolanaLabs, + 1u16 => Self::JitoLabs, + 2u16 => Self::Firedancer, + _ => Self::Unknown(client), + } + } +} + +//////// Copied from solana/gossip/src/contact_info.rs + +const SOCKET_TAG_TVU_QUIC: u8 = 12; +const_assert_eq!(SOCKET_CACHE_SIZE, 13); +const SOCKET_CACHE_SIZE: usize = SOCKET_TAG_TVU_QUIC as usize + 1usize; + +#[derive(Debug, Error)] +pub enum Error { + #[error("Duplicate IP address: {0}")] + DuplicateIpAddr(IpAddr), + #[error("Duplicate socket: {0}")] + DuplicateSocket(/*key:*/ u8), + #[error("Invalid IP address index: {index}, num addrs: {num_addrs}")] + InvalidIpAddrIndex { index: u8, num_addrs: usize }, + #[error("Invalid port: {0}")] + InvalidPort(/*port:*/ u16), + #[error("Invalid {0:?} (udp) and {1:?} (quic) sockets")] + InvalidQuicSocket(Option, Option), + #[error("IP addresses saturated")] + IpAddrsSaturated, + #[error("Multicast IP address: {0}")] + MulticastIpAddr(IpAddr), + #[error("Port offsets overflow")] + PortOffsetsOverflow, + #[error("Socket not found: {0}")] + SocketNotFound(/*key:*/ u8), + #[error("Unspecified IP address: {0}")] + UnspecifiedIpAddr(IpAddr), + #[error("Unused IP address: {0}")] + UnusedIpAddr(IpAddr), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ContactInfo { + pubkey: Pubkey, + #[serde(with = "serde_varint")] + wallclock: u64, + // When the node instance was first created. + // Identifies duplicate running instances. + outset: u64, + shred_version: u16, + pub version: Version, + // All IP addresses are unique and referenced at least once in sockets. + #[serde(with = "short_vec")] + pub addrs: Vec, + // All sockets have a unique key and a valid IP address index. + #[serde(with = "short_vec")] + sockets: Vec, + #[serde(skip_serializing)] + cache: [SocketAddr; SOCKET_CACHE_SIZE], +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +struct SocketEntry { + key: u8, // Protocol identifier, e.g. tvu, tpu, etc + index: u8, // IpAddr index in the accompanying addrs vector. + #[serde(with = "serde_varint")] + offset: u16, // Port offset with respect to the previous entry. +} + +// As part of deserialization, self.addrs and self.sockets should be cross +// verified and self.cache needs to be populated. This type serves as a +// workaround since serde does not have an initializer. +// https://github.com/serde-rs/serde/issues/642 +#[derive(Deserialize)] +struct ContactInfoLite { + pubkey: Pubkey, + #[serde(with = "serde_varint")] + wallclock: u64, + outset: u64, + shred_version: u16, + version: Version, + #[serde(with = "short_vec")] + addrs: Vec, + #[serde(with = "short_vec")] + sockets: Vec, +} + +impl ContactInfo { + #[inline] + pub fn pubkey(&self) -> &Pubkey { + &self.pubkey + } + + #[inline] + pub fn wallclock(&self) -> u64 { + self.wallclock + } +} + +impl<'de> Deserialize<'de> for ContactInfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let node = ContactInfoLite::deserialize(deserializer)?; + ContactInfo::try_from(node).map_err(serde::de::Error::custom) + } +} + +impl TryFrom for ContactInfo { + type Error = Error; + + fn try_from(node: ContactInfoLite) -> Result { + let socket_addr_unspecified: SocketAddr = + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), /*port:*/ 0u16); + let ContactInfoLite { + pubkey, + wallclock, + outset, + shred_version, + version, + addrs, + sockets, + } = node; + let node = ContactInfo { + pubkey, + wallclock, + outset, + shred_version, + version, + addrs, + sockets, + cache: [socket_addr_unspecified; SOCKET_CACHE_SIZE], + }; + + Ok(node) + } +} + +///////// from legacy_contact_info.rs + +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] +pub struct LegacyContactInfo { + id: Pubkey, + /// gossip address + pub gossip: SocketAddr, + /// address to connect to for replication + tvu: SocketAddr, + /// address to forward shreds to + tvu_forwards: SocketAddr, + /// address to send repair responses to + repair: SocketAddr, + /// transactions address + tpu: SocketAddr, + /// address to forward unprocessed transactions to + tpu_forwards: SocketAddr, + /// address to which to send bank state requests + tpu_vote: SocketAddr, + /// address to which to send JSON-RPC requests + rpc: SocketAddr, + /// websocket for JSON-RPC push notifications + rpc_pubsub: SocketAddr, + /// address to send repair requests to + serve_repair: SocketAddr, + /// latest wallclock picked + wallclock: u64, + /// node shred version + shred_version: u16, +} + +impl Sanitize for LegacyContactInfo { + fn sanitize(&self) -> std::result::Result<(), SanitizeError> { + if self.wallclock >= MAX_WALLCLOCK { + return Err(SanitizeError::ValueOutOfBounds); + } + Ok(()) + } +} +impl LegacyContactInfo { + #[inline] + pub fn pubkey(&self) -> &Pubkey { + &self.id + } + + #[inline] + pub fn wallclock(&self) -> u64 { + self.wallclock + } +} + +///////// from crds_value.rs + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct LegacyVersion { + pub from: Pubkey, + pub wallclock: u64, + pub version: LegacyVersion1, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct Version2 { + pub from: Pubkey, + pub wallclock: u64, + pub version: LegacyVersion2, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct LegacyVersion1 { + pub major: u16, + pub minor: u16, + pub patch: u16, + pub commit: Option, // first 4 bytes of the sha1 commit hash +} + +impl Sanitize for LegacyVersion1 {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct LegacyVersion2 { + pub major: u16, + pub minor: u16, + pub patch: u16, + pub commit: Option, // first 4 bytes of the sha1 commit hash + pub feature_set: u32, // first 4 bytes of the FeatureSet identifier +} + +impl From for LegacyVersion2 { + fn from(legacy_version: LegacyVersion1) -> Self { + Self { + major: legacy_version.major, + minor: legacy_version.minor, + patch: legacy_version.patch, + commit: legacy_version.commit, + feature_set: 0, + } + } +} diff --git a/programs/validator-history/src/errors.rs b/programs/validator-history/src/errors.rs new file mode 100644 index 00000000..03239c13 --- /dev/null +++ b/programs/validator-history/src/errors.rs @@ -0,0 +1,27 @@ +use anchor_lang::prelude::*; + +#[error_code] +pub enum ValidatorHistoryError { + #[msg("Account already reached proper size, no more allocations allowed")] + AccountFullySized, + #[msg("Invalid epoch credits, credits must be greater than previous credits")] + InvalidEpochCredits, + #[msg("Epoch is out of range of history")] + EpochOutOfRange, + #[msg("Gossip Signature Verification not performed")] + NotSigVerified, + #[msg("Gossip Data Invalid")] + GossipDataInvalid, + #[msg("Unsupported IP Format, only IpAddr::V4 is supported")] + UnsupportedIpFormat, + #[msg("Not enough voting history to create account. Minimum 10 epochs required")] + NotEnoughVotingHistory, + #[msg( + "Gossip data too old. Data cannot be older than the last recorded timestamp for a field" + )] + GossipDataTooOld, + #[msg("Gossip timestamp too far in the future")] + GossipDataInFuture, + #[msg("Arithmetic Error (overflow/underflow)")] + ArithmeticError, +} diff --git a/programs/validator-history/src/instructions/copy_gossip_contact_info.rs b/programs/validator-history/src/instructions/copy_gossip_contact_info.rs new file mode 100644 index 00000000..9b1d2b4b --- /dev/null +++ b/programs/validator-history/src/instructions/copy_gossip_contact_info.rs @@ -0,0 +1,102 @@ +use anchor_lang::{ + prelude::*, + solana_program::{self, clock::Clock, pubkey::Pubkey, sysvar}, +}; + +use crate::{ + crds_value::CrdsData, errors::ValidatorHistoryError, state::ValidatorHistory, utils::cast_epoch, +}; +use validator_history_vote_state::VoteStateVersions; + +#[derive(Accounts)] +pub struct CopyGossipContactInfo<'info> { + #[account( + mut, + seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], + bump, + )] + pub validator_history_account: AccountLoader<'info, ValidatorHistory>, + /// CHECK: Safe because we check the vote program is the owner. + #[account(owner = solana_program::vote::program::ID.key())] + pub vote_account: AccountInfo<'info>, + /// CHECK: Safe because it's a sysvar account + #[account(address = sysvar::instructions::ID)] + pub instructions: UncheckedAccount<'info>, + #[account(mut)] + pub signer: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + let mut validator_history_account = ctx.accounts.validator_history_account.load_mut()?; + let instructions = ctx.accounts.instructions.to_account_info(); + let clock = Clock::get()?; + let epoch = cast_epoch(clock.epoch); + + let verify_instruction = sysvar::instructions::get_instruction_relative(-1, &instructions)?; + + // Check that the instruction is a ed25519 instruction + if verify_instruction.program_id != solana_program::ed25519_program::ID { + return Err(ValidatorHistoryError::NotSigVerified.into()); + } + + let message_signer = Pubkey::try_from(&verify_instruction.data[16..48]) + .map_err(|_| ValidatorHistoryError::GossipDataInvalid)?; + let message_data = &verify_instruction.data[112..]; + + let crds_data: CrdsData = + bincode::deserialize(message_data).map_err(|_| ValidatorHistoryError::GossipDataInvalid)?; + + let (crds_data_pubkey, last_signed_ts) = match &crds_data { + CrdsData::LegacyContactInfo(contact_info) => { + (*contact_info.pubkey(), contact_info.wallclock()) + } + CrdsData::ContactInfo(contact_info) => (*contact_info.pubkey(), contact_info.wallclock()), + CrdsData::Version(version) => (version.from, version.wallclock), + CrdsData::LegacyVersion(version) => (version.from, version.wallclock), + _ => { + return Err(ValidatorHistoryError::GossipDataInvalid.into()); + } + }; + + let node_pubkey = VoteStateVersions::deserialize_node_pubkey(&ctx.accounts.vote_account)?; + // The gossip signature signer, the ContactInfo struct, and the vote account identity + // must all reference the same address + if crds_data_pubkey != node_pubkey || message_signer != node_pubkey { + return Err(ValidatorHistoryError::GossipDataInvalid.into()); + } + + // Timestamp can't be too far in the future or this upload will be stuck. Allows 10 minutes of buffer + if last_signed_ts + > clock + .unix_timestamp + .checked_add(600) + .ok_or(ValidatorHistoryError::ArithmeticError)? as u64 + { + return Err(ValidatorHistoryError::GossipDataInFuture.into()); + } + + // Set gossip values + match crds_data { + CrdsData::LegacyContactInfo(legacy_contact_info) => { + validator_history_account.set_legacy_contact_info( + epoch, + &legacy_contact_info, + last_signed_ts, + )?; + } + CrdsData::ContactInfo(contact_info) => { + validator_history_account.set_contact_info(epoch, &contact_info, last_signed_ts)?; + } + CrdsData::Version(version) => { + validator_history_account.set_version(epoch, &version, last_signed_ts)?; + } + CrdsData::LegacyVersion(legacy_version) => { + validator_history_account.set_legacy_version(epoch, &legacy_version, last_signed_ts)?; + } + _ => { + return Err(ValidatorHistoryError::GossipDataInvalid.into()); + } + } + + Ok(()) +} diff --git a/programs/validator-history/src/instructions/copy_vote_account.rs b/programs/validator-history/src/instructions/copy_vote_account.rs new file mode 100644 index 00000000..ce892de1 --- /dev/null +++ b/programs/validator-history/src/instructions/copy_vote_account.rs @@ -0,0 +1,37 @@ +use anchor_lang::{ + prelude::*, + solana_program::{clock::Clock, vote}, +}; +use validator_history_vote_state::VoteStateVersions; + +use crate::{state::ValidatorHistory, utils::cast_epoch}; + +#[derive(Accounts)] +pub struct CopyVoteAccount<'info> { + #[account( + mut, + seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], + bump, + has_one = vote_account + )] + pub validator_history_account: AccountLoader<'info, ValidatorHistory>, + /// CHECK: Safe because we check the vote program is the owner before reading bytes. + #[account(owner = vote::program::ID.key())] + pub vote_account: AccountInfo<'info>, + #[account(mut)] + pub signer: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + let mut validator_history_account = ctx.accounts.validator_history_account.load_mut()?; + let clock = Clock::get()?; + let epoch = cast_epoch(clock.epoch); + + let commission = VoteStateVersions::deserialize_commission(&ctx.accounts.vote_account)?; + validator_history_account.set_commission_and_slot(epoch, commission, clock.slot)?; + + let epoch_credits = VoteStateVersions::deserialize_epoch_credits(&ctx.accounts.vote_account)?; + validator_history_account.set_epoch_credits(&epoch_credits)?; + + Ok(()) +} diff --git a/programs/validator-history/src/instructions/initialize_config.rs b/programs/validator-history/src/instructions/initialize_config.rs new file mode 100644 index 00000000..d763c9aa --- /dev/null +++ b/programs/validator-history/src/instructions/initialize_config.rs @@ -0,0 +1,26 @@ +use anchor_lang::prelude::*; + +use crate::Config; + +#[derive(Accounts)] +pub struct InitializeConfig<'info> { + #[account( + init, + payer = signer, + space = Config::SIZE, + seeds = [Config::SEED], + bump, + )] + pub config: Account<'info, Config>, + pub system_program: Program<'info, System>, + #[account(mut)] + pub signer: Signer<'info>, +} + +pub fn handler(ctx: Context, authority: Pubkey) -> Result<()> { + ctx.accounts.config.stake_authority = authority; + ctx.accounts.config.tip_distribution_authority = authority; + ctx.accounts.config.bump = *ctx.bumps.get("config").unwrap(); + ctx.accounts.config.counter = 0; + Ok(()) +} diff --git a/programs/validator-history/src/instructions/initialize_validator_history_account.rs b/programs/validator-history/src/instructions/initialize_validator_history_account.rs new file mode 100644 index 00000000..3ca82e7f --- /dev/null +++ b/programs/validator-history/src/instructions/initialize_validator_history_account.rs @@ -0,0 +1,34 @@ +use crate::{ + constants::{MAX_ALLOC_BYTES, MIN_VOTE_EPOCHS}, + errors::ValidatorHistoryError, + state::ValidatorHistory, +}; +use anchor_lang::{prelude::*, solana_program::vote}; +use validator_history_vote_state::VoteStateVersions; + +#[derive(Accounts)] +pub struct InitializeValidatorHistoryAccount<'info> { + #[account( + init, + payer = signer, + space = MAX_ALLOC_BYTES, + seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], + bump + )] + pub validator_history_account: AccountLoader<'info, ValidatorHistory>, + /// CHECK: Safe because we check the vote program is the owner before deserialization. + #[account(owner = vote::program::ID.key())] + pub vote_account: AccountInfo<'info>, + pub system_program: Program<'info, System>, + #[account(mut)] + pub signer: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + // Need minimum 5 epochs of vote credits to be valid + let epoch_credits = VoteStateVersions::deserialize_epoch_credits(&ctx.accounts.vote_account)?; + if epoch_credits.len() < MIN_VOTE_EPOCHS { + return Err(ValidatorHistoryError::NotEnoughVotingHistory.into()); + } + Ok(()) +} diff --git a/programs/validator-history/src/instructions/mod.rs b/programs/validator-history/src/instructions/mod.rs new file mode 100644 index 00000000..5d932c4d --- /dev/null +++ b/programs/validator-history/src/instructions/mod.rs @@ -0,0 +1,22 @@ +#![allow(ambiguous_glob_reexports)] +pub mod copy_gossip_contact_info; +pub mod copy_vote_account; +pub mod initialize_config; +pub mod initialize_validator_history_account; +pub mod realloc_validator_history_account; +pub mod set_new_stake_authority; +pub mod set_new_tip_distribution_authority; +pub mod set_new_tip_distribution_program; +pub mod update_mev_commission; +pub mod update_stake_history; + +pub use copy_gossip_contact_info::*; +pub use copy_vote_account::*; +pub use initialize_config::*; +pub use initialize_validator_history_account::*; +pub use realloc_validator_history_account::*; +pub use set_new_stake_authority::*; +pub use set_new_tip_distribution_authority::*; +pub use set_new_tip_distribution_program::*; +pub use update_mev_commission::*; +pub use update_stake_history::*; diff --git a/programs/validator-history/src/instructions/realloc_validator_history_account.rs b/programs/validator-history/src/instructions/realloc_validator_history_account.rs new file mode 100644 index 00000000..1885b937 --- /dev/null +++ b/programs/validator-history/src/instructions/realloc_validator_history_account.rs @@ -0,0 +1,73 @@ +use crate::{ + constants::MAX_ALLOC_BYTES, + state::{Config, ValidatorHistory, ValidatorHistoryEntry, ValidatorHistoryVersion}, +}; +use anchor_lang::{prelude::*, solana_program::vote}; + +fn get_realloc_size(account_info: &AccountInfo) -> usize { + let account_size = account_info.data_len(); + + // If account is already over-allocated, don't try to shrink + if account_size < ValidatorHistory::SIZE { + ValidatorHistory::SIZE.min(account_size + MAX_ALLOC_BYTES) + } else { + account_size + } +} + +fn is_initialized(account_info: &AccountInfo) -> Result { + let account_data = account_info.as_ref().try_borrow_data()?; + // discriminator + version_number + let vote_account_pubkey_bytes = account_data[(8 + 4)..(8 + 4 + 32)].to_vec(); + + // If pubkey is all zeroes, then it's not initialized + Ok(vote_account_pubkey_bytes.iter().any(|&x| x != 0)) +} + +#[derive(Accounts)] +pub struct ReallocValidatorHistoryAccount<'info> { + #[account( + mut, + realloc = get_realloc_size(validator_history_account.as_ref()), + realloc::payer = signer, + realloc::zero = false, + seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], + bump + )] + pub validator_history_account: AccountLoader<'info, ValidatorHistory>, + #[account(mut, seeds = [Config::SEED], bump = config.bump)] + pub config: Account<'info, Config>, + /// CHECK: Safe because we check the vote program is the owner before deserialization. + /// Used to read validator commission. + #[account(owner = vote::program::ID.key())] + pub vote_account: AccountInfo<'info>, + pub system_program: Program<'info, System>, + #[account(mut)] + pub signer: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + let account_size = ctx.accounts.validator_history_account.as_ref().data_len(); + if account_size >= ValidatorHistory::SIZE + && !is_initialized(ctx.accounts.validator_history_account.as_ref())? + { + // Can actually initialize values now that the account is proper size + let mut validator_history_account = ctx.accounts.validator_history_account.load_mut()?; + + validator_history_account.index = ctx.accounts.config.counter; + ctx.accounts.config.counter += 1; + validator_history_account.bump = *ctx.bumps.get("validator_history_account").unwrap(); + validator_history_account.vote_account = *ctx.accounts.vote_account.key; + validator_history_account.struct_version = ValidatorHistoryVersion::V0 as u32; + validator_history_account.history.idx = + (validator_history_account.history.arr.len() - 1) as u64; + for _ in 0..validator_history_account.history.arr.len() { + validator_history_account + .history + .push(ValidatorHistoryEntry::default()); + } + validator_history_account.history.is_empty = 1; + } + + Ok(()) +} diff --git a/programs/validator-history/src/instructions/set_new_stake_authority.rs b/programs/validator-history/src/instructions/set_new_stake_authority.rs new file mode 100644 index 00000000..0e9f3066 --- /dev/null +++ b/programs/validator-history/src/instructions/set_new_stake_authority.rs @@ -0,0 +1,22 @@ +use anchor_lang::prelude::*; + +use crate::state::Config; + +#[derive(Accounts)] +pub struct SetNewStakeAuthority<'info> { + #[account( + mut, + seeds = [Config::SEED], + bump = config.bump, + has_one = stake_authority, + )] + pub config: Account<'info, Config>, + /// CHECK: fine since we are not deserializing account + pub new_authority: AccountInfo<'info>, + pub stake_authority: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + ctx.accounts.config.stake_authority = ctx.accounts.new_authority.key(); + Ok(()) +} diff --git a/programs/validator-history/src/instructions/set_new_tip_distribution_authority.rs b/programs/validator-history/src/instructions/set_new_tip_distribution_authority.rs new file mode 100644 index 00000000..50ef8e53 --- /dev/null +++ b/programs/validator-history/src/instructions/set_new_tip_distribution_authority.rs @@ -0,0 +1,22 @@ +use anchor_lang::prelude::*; + +use crate::state::Config; + +#[derive(Accounts)] +pub struct SetNewTipDistributionAuthority<'info> { + #[account( + mut, + seeds = [Config::SEED], + bump = config.bump, + has_one = tip_distribution_authority, + )] + pub config: Account<'info, Config>, + /// CHECK: fine since we are not deserializing account + pub new_authority: AccountInfo<'info>, + pub tip_distribution_authority: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + ctx.accounts.config.tip_distribution_authority = ctx.accounts.new_authority.key(); + Ok(()) +} diff --git a/programs/validator-history/src/instructions/set_new_tip_distribution_program.rs b/programs/validator-history/src/instructions/set_new_tip_distribution_program.rs new file mode 100644 index 00000000..e3288003 --- /dev/null +++ b/programs/validator-history/src/instructions/set_new_tip_distribution_program.rs @@ -0,0 +1,23 @@ +use anchor_lang::prelude::*; + +use crate::state::Config; + +#[derive(Accounts)] +pub struct SetNewTipDistributionProgram<'info> { + #[account( + mut, + seeds = [Config::SEED], + bump = config.bump, + has_one = tip_distribution_authority, + )] + pub config: Account<'info, Config>, + /// CHECK: fine since we are not deserializing account + #[account(executable)] + pub new_tip_distribution_program: AccountInfo<'info>, + pub tip_distribution_authority: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + ctx.accounts.config.tip_distribution_program = ctx.accounts.new_tip_distribution_program.key(); + Ok(()) +} diff --git a/programs/validator-history/src/instructions/update_mev_commission.rs b/programs/validator-history/src/instructions/update_mev_commission.rs new file mode 100644 index 00000000..80d5864b --- /dev/null +++ b/programs/validator-history/src/instructions/update_mev_commission.rs @@ -0,0 +1,63 @@ +use anchor_lang::{ + prelude::*, + solana_program::{clock::Clock, vote}, +}; + +use crate::{ + state::{Config, ValidatorHistory}, + utils::cast_epoch, +}; +use jito_tip_distribution::state::TipDistributionAccount; + +#[derive(Accounts)] +pub struct UpdateMevCommission<'info> { + #[account( + mut, + seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], + bump, + has_one = vote_account + )] + pub validator_history_account: AccountLoader<'info, ValidatorHistory>, + + /// CHECK: Safe because we check the vote program is the owner before deserialization. + /// Used to read validator commission. + #[account(owner = vote::program::ID.key())] + pub vote_account: AccountInfo<'info>, + + #[account( + seeds = [Config::SEED], + bump = config.bump, + )] + pub config: Account<'info, Config>, + + /// CHECK: Avoiding struct deserialization here to avoid default Owner trait check. + /// `owner = config.tip_distribution_program.key()` here is sufficient. + #[account( + seeds = [ + TipDistributionAccount::SEED, + vote_account.key().as_ref(), + Clock::get().unwrap().epoch.to_le_bytes().as_ref(), + ], + bump, + seeds::program = config.tip_distribution_program.key(), + owner = config.tip_distribution_program.key() + )] + pub tip_distribution_account: UncheckedAccount<'info>, + + #[account(mut)] + pub signer: Signer<'info>, +} + +pub fn handler(ctx: Context) -> Result<()> { + let mut validator_history_account = ctx.accounts.validator_history_account.load_mut()?; + + let mut tda_data: &[u8] = &ctx.accounts.tip_distribution_account.try_borrow_data()?; + + let tip_distribution_account = TipDistributionAccount::try_deserialize(&mut tda_data)?; + let mev_commission_bps = tip_distribution_account.validator_commission_bps; + let epoch = cast_epoch(Clock::get()?.epoch); + + validator_history_account.set_mev_commission(epoch, mev_commission_bps)?; + + Ok(()) +} diff --git a/programs/validator-history/src/instructions/update_stake_history.rs b/programs/validator-history/src/instructions/update_stake_history.rs new file mode 100644 index 00000000..0fa6dd5b --- /dev/null +++ b/programs/validator-history/src/instructions/update_stake_history.rs @@ -0,0 +1,50 @@ +use crate::{ + errors::ValidatorHistoryError, + state::{Config, ValidatorHistory}, + utils::cast_epoch, +}; +use anchor_lang::{prelude::*, solana_program::vote}; + +#[derive(Accounts)] +pub struct UpdateStakeHistory<'info> { + #[account( + mut, + seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], + bump + )] + pub validator_history_account: AccountLoader<'info, ValidatorHistory>, + + /// CHECK: fine since we are not deserializing account + #[account(owner = vote::program::ID.key())] + pub vote_account: AccountInfo<'info>, + + #[account( + seeds = [Config::SEED], + bump = config.bump, + has_one = stake_authority + )] + pub config: Account<'info, Config>, + + #[account(mut)] + pub stake_authority: Signer<'info>, +} + +pub fn handler( + ctx: Context, + epoch: u64, + lamports: u64, + rank: u32, + is_superminority: bool, +) -> Result<()> { + let mut validator_history_account = ctx.accounts.validator_history_account.load_mut()?; + + // Cannot set stake for future epochs + if epoch > Clock::get()?.epoch { + return Err(ValidatorHistoryError::EpochOutOfRange.into()); + } + let epoch = cast_epoch(epoch); + + validator_history_account.set_stake(epoch, lamports, rank, is_superminority)?; + + Ok(()) +} diff --git a/programs/validator-history/src/lib.rs b/programs/validator-history/src/lib.rs new file mode 100644 index 00000000..cf7c01fb --- /dev/null +++ b/programs/validator-history/src/lib.rs @@ -0,0 +1,97 @@ +use anchor_lang::prelude::*; + +pub mod constants; +pub mod crds_value; +pub mod errors; +pub mod instructions; +pub mod serde_varint; +pub mod state; +pub mod utils; + +pub use instructions::*; +pub use state::*; + +cfg_if::cfg_if! { + if #[cfg(feature = "mainnet-beta")] { + declare_id!("HistoryJTGbKQD2mRgLZ3XhqHnN811Qpez8X9kCcGHoa"); + } else if #[cfg(feature = "testnet")] { + declare_id!("HisTBTgDnsdxfMp3m63fgKxCx9xVQE17MhA9BWRdrAP"); + } else { + declare_id!("HistoryJTGbKQD2mRgLZ3XhqHnN811Qpez8X9kCcGHoa"); + } +} + +#[cfg(not(feature = "no-entrypoint"))] +use solana_security_txt::security_txt; + +#[cfg(not(feature = "no-entrypoint"))] +security_txt! { + // Required fields + name: "Jito Validator History V1", + project_url: "https://jito.network/", + contacts: "email:team@jito.network", + policy: "https://github.com/jito-foundation/stakenet/blob/master/README.md", + // Optional Fields + preferred_languages: "en", + source_code: "https://github.com/jito-foundation/stakenet" +} + +#[program] +pub mod validator_history { + + use super::*; + + pub fn initialize_validator_history_account( + ctx: Context, + ) -> Result<()> { + instructions::initialize_validator_history_account::handler(ctx) + } + + pub fn realloc_validator_history_account( + ctx: Context, + ) -> Result<()> { + instructions::realloc_validator_history_account::handler(ctx) + } + + pub fn copy_vote_account(ctx: Context) -> Result<()> { + instructions::copy_vote_account::handler(ctx) + } + + pub fn update_mev_commission(ctx: Context) -> Result<()> { + instructions::update_mev_commission::handler(ctx) + } + + pub fn initialize_config(ctx: Context, authority: Pubkey) -> Result<()> { + instructions::initialize_config::handler(ctx, authority) + } + + pub fn set_new_tip_distribution_program( + ctx: Context, + ) -> Result<()> { + instructions::set_new_tip_distribution_program::handler(ctx) + } + + pub fn set_new_tip_distribution_authority( + ctx: Context, + ) -> Result<()> { + instructions::set_new_tip_distribution_authority::handler(ctx) + } + + pub fn set_new_stake_authority(ctx: Context) -> Result<()> { + instructions::set_new_stake_authority::handler(ctx) + } + + pub fn update_stake_history( + ctx: Context, + epoch: u64, + lamports: u64, + rank: u32, + is_superminority: bool, + ) -> Result<()> { + instructions::update_stake_history::handler(ctx, epoch, lamports, rank, is_superminority) + } + + pub fn copy_gossip_contact_info(ctx: Context) -> Result<()> { + instructions::copy_gossip_contact_info::handler(ctx) + } +} diff --git a/programs/validator-history/src/serde_varint.rs b/programs/validator-history/src/serde_varint.rs new file mode 100644 index 00000000..ec8de02f --- /dev/null +++ b/programs/validator-history/src/serde_varint.rs @@ -0,0 +1,121 @@ +// Copied from solana/sdk/program/src/serde_varint.rs +//! Integers that serialize to variable size. + +#![allow(clippy::arithmetic_side_effects)] +use { + serde::{ + de::{Error as _, SeqAccess, Visitor}, + ser::SerializeTuple, + Deserializer, Serializer, + }, + std::{fmt, marker::PhantomData}, +}; + +pub trait VarInt: Sized { + fn visit_seq<'de, A>(seq: A) -> Result + where + A: SeqAccess<'de>; + + fn serialize(self, serializer: S) -> Result + where + S: Serializer; +} + +struct VarIntVisitor { + phantom: PhantomData, +} + +impl<'de, T> Visitor<'de> for VarIntVisitor +where + T: VarInt, +{ + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a VarInt") + } + + fn visit_seq(self, seq: A) -> Result + where + A: SeqAccess<'de>, + { + T::visit_seq(seq) + } +} + +pub fn serialize(value: &T, serializer: S) -> Result +where + T: Copy + VarInt, + S: Serializer, +{ + (*value).serialize(serializer) +} + +pub fn deserialize<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: VarInt, +{ + deserializer.deserialize_tuple( + (std::mem::size_of::() * 8 + 6) / 7, + VarIntVisitor { + phantom: PhantomData, + }, + ) +} + +macro_rules! impl_var_int { + ($type:ty) => { + impl VarInt for $type { + fn visit_seq<'de, A>(mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut out = 0; + let mut shift = 0u32; + while shift < <$type>::BITS { + let byte = match seq.next_element::()? { + None => return Err(A::Error::custom("Invalid Sequence")), + Some(byte) => byte, + }; + out |= ((byte & 0x7F) as Self) << shift; + if byte & 0x80 == 0 { + // Last byte should not have been truncated when it was + // shifted to the left above. + if (out >> shift) as u8 != byte { + return Err(A::Error::custom("Last Byte Truncated")); + } + // Last byte can be zero only if there was only one + // byte and the output is also zero. + if byte == 0u8 && (shift != 0 || out != 0) { + return Err(A::Error::custom("Invalid Trailing Zeros")); + } + return Ok(out); + } + shift += 7; + } + Err(A::Error::custom("Left Shift Overflows")) + } + + fn serialize(mut self, serializer: S) -> Result + where + S: Serializer, + { + let bits = <$type>::BITS - self.leading_zeros(); + let num_bytes = ((bits + 6) / 7).max(1) as usize; + let mut seq = serializer.serialize_tuple(num_bytes)?; + while self >= 0x80 { + let byte = ((self & 0x7F) | 0x80) as u8; + seq.serialize_element(&byte)?; + self >>= 7; + } + seq.serialize_element(&(self as u8))?; + seq.end() + } + } + }; +} + +impl_var_int!(u16); +impl_var_int!(u32); +impl_var_int!(u64); diff --git a/programs/validator-history/src/state.rs b/programs/validator-history/src/state.rs new file mode 100644 index 00000000..8ca6aa65 --- /dev/null +++ b/programs/validator-history/src/state.rs @@ -0,0 +1,457 @@ +use std::{cmp::Ordering, collections::HashMap, mem::size_of, net::IpAddr}; + +use anchor_lang::prelude::*; +use borsh::{BorshDeserialize, BorshSerialize}; +use type_layout::TypeLayout; + +use crate::{ + crds_value::{ContactInfo, LegacyContactInfo, LegacyVersion, Version2}, + errors::ValidatorHistoryError, + utils::cast_epoch, +}; + +static_assertions::const_assert_eq!(size_of::(), 104); + +#[account] +#[derive(Default)] +pub struct Config { + // This program is used to distribute MEV + track which validators are running jito-solana for a given epoch + pub tip_distribution_program: Pubkey, + + // Has the ability to upgrade the tip_distribution_program in case of a program upgrade + pub tip_distribution_authority: Pubkey, + + // Has the ability to publish stake amounts per validator + pub stake_authority: Pubkey, + + // Tracks number of initialized ValidatorHistory accounts + pub counter: u32, + + pub bump: u8, +} + +impl Config { + pub const SEED: &'static [u8] = b"config"; + pub const SIZE: usize = 8 + size_of::(); +} + +static_assertions::const_assert_eq!(size_of::(), 128); + +#[derive(AnchorSerialize, TypeLayout)] +#[zero_copy] +pub struct ValidatorHistoryEntry { + pub activated_stake_lamports: u64, + pub epoch: u16, + // MEV commission in basis points + pub mev_commission: u16, + // Number of successful votes in current epoch. Not finalized until subsequent epoch + pub epoch_credits: u32, + // Validator commission in points + pub commission: u8, + // 0 if Solana Labs client, 1 if Jito client, >1 if other + pub client_type: u8, + pub version: ClientVersion, + pub ip: [u8; 4], + // Required to keep 8-byte alignment + pub padding0: u8, + // 0 if not a superminority validator, 1 if superminority validator + pub is_superminority: u8, + // rank of validator by stake amount + pub rank: u32, + // Most recent updated slot for epoch credits and commission + pub vote_account_last_update_slot: u64, + pub padding1: [u8; 88], +} + +impl Default for ValidatorHistoryEntry { + fn default() -> Self { + Self { + activated_stake_lamports: u64::MAX, + epoch: u16::MAX, + mev_commission: u16::MAX, + epoch_credits: u32::MAX, + commission: u8::MAX, + client_type: u8::MAX, + version: ClientVersion { + major: u8::MAX, + minor: u8::MAX, + patch: u16::MAX, + }, + ip: [u8::MAX; 4], + padding0: u8::MAX, + is_superminority: u8::MAX, + rank: u32::MAX, + vote_account_last_update_slot: u64::MAX, + padding1: [u8::MAX; 88], + } + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +#[zero_copy] +pub struct ClientVersion { + pub major: u8, + pub minor: u8, + pub patch: u16, +} + +const MAX_ITEMS: usize = 512; + +#[derive(AnchorSerialize)] +#[zero_copy] +pub struct CircBuf { + pub idx: u64, + pub is_empty: u8, + pub padding: [u8; 7], + pub arr: [ValidatorHistoryEntry; MAX_ITEMS], +} + +#[cfg(test)] +impl Default for CircBuf { + fn default() -> Self { + Self { + arr: [ValidatorHistoryEntry::default(); MAX_ITEMS], + idx: 0, + is_empty: 1, + padding: [0; 7], + } + } +} + +impl CircBuf { + pub fn push(&mut self, item: ValidatorHistoryEntry) { + self.idx = (self.idx + 1) % self.arr.len() as u64; + self.arr[self.idx as usize] = item; + self.is_empty = 0; + } + + pub fn is_empty(&self) -> bool { + self.is_empty == 1 + } + + pub fn last(&self) -> Option<&ValidatorHistoryEntry> { + if self.is_empty() { + None + } else { + Some(&self.arr[self.idx as usize]) + } + } + + pub fn last_mut(&mut self) -> Option<&mut ValidatorHistoryEntry> { + if self.is_empty() { + None + } else { + Some(&mut self.arr[self.idx as usize]) + } + } + + pub fn arr_mut(&mut self) -> &mut [ValidatorHistoryEntry] { + &mut self.arr + } +} + +pub enum ValidatorHistoryVersion { + V0 = 0, +} + +static_assertions::const_assert_eq!(size_of::(), 65848); + +#[derive(AnchorSerialize)] +#[account(zero_copy)] +pub struct ValidatorHistory { + // Cannot be enum due to Pod and Zeroable trait limitations + pub struct_version: u32, + + pub vote_account: Pubkey, + // Index of validator of all ValidatorHistory accounts + pub index: u32, + + pub bump: u8, + + pub _padding0: [u8; 7], + + // These Crds gossip values are only signed and dated once upon startup and then never updated + // so we track latest time on-chain to make sure old messages aren't uploaded + pub last_ip_timestamp: u64, + pub last_version_timestamp: u64, + + pub _padding1: [u8; 232], + + pub history: CircBuf, +} + +impl ValidatorHistory { + pub const SIZE: usize = 8 + size_of::(); + pub const MAX_ITEMS: usize = MAX_ITEMS; + pub const SEED: &'static [u8] = b"validator-history"; + + pub fn set_mev_commission(&mut self, epoch: u16, commission: u16) -> Result<()> { + // check if entry exists for the epoch + if let Some(entry) = self.history.last_mut() { + if entry.epoch == epoch { + entry.mev_commission = commission; + return Ok(()); + } + } + let entry = ValidatorHistoryEntry { + epoch, + mev_commission: commission, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + + Ok(()) + } + + pub fn set_stake( + &mut self, + epoch: u16, + stake: u64, + rank: u32, + is_superminority: bool, + ) -> Result<()> { + // Only one authority for upload here, so any epoch can be updated in case of missed upload + if let Some(entry) = self.history.last_mut() { + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.activated_stake_lamports = stake; + entry.rank = rank; + entry.is_superminority = is_superminority as u8; + return Ok(()); + } + Ordering::Greater => { + for entry in self.history.arr_mut().iter_mut() { + if entry.epoch == epoch { + entry.activated_stake_lamports = stake; + entry.rank = rank; + entry.is_superminority = is_superminority as u8; + return Ok(()); + } + } + return Err(ValidatorHistoryError::EpochOutOfRange.into()); + } + Ordering::Less => {} + } + } + let entry = ValidatorHistoryEntry { + epoch, + activated_stake_lamports: stake, + rank, + is_superminority: is_superminority as u8, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + Ok(()) + } + + pub fn set_epoch_credits( + &mut self, + epoch_credits: &[( + u64, /* epoch */ + u64, /* epoch cumulative votes */ + u64, /* prev epoch cumulative votes */ + )], + ) -> Result<()> { + // Assumes `set_commission` has already been run in `copy_vote_account`, + // guaranteeing an entry exists for the current epoch + if epoch_credits.is_empty() { + return Ok(()); + } + let epoch_credits_map: HashMap = + HashMap::from_iter(epoch_credits.iter().map(|(epoch, cur, prev)| { + ( + cast_epoch(*epoch), + (cur.checked_sub(*prev) + .ok_or(ValidatorHistoryError::InvalidEpochCredits) + .unwrap() as u32), + ) + })); + + // Traverses entries in reverse order, breaking once we either: + // 1) Start seeing identical epoch credit values + // 2) See an epoch not in validator epoch credits (uninitialized or out of range) + let len = self.history.arr.len(); + for i in 0..len { + let position = (self.history.idx as usize + len - i) % len; + let entry = &mut self.history.arr[position]; + if let Some(&epoch_credits) = epoch_credits_map.get(&entry.epoch) { + if epoch_credits != entry.epoch_credits { + entry.epoch_credits = epoch_credits; + } else { + break; + } + } else { + break; + } + } + + Ok(()) + } + + pub fn set_commission_and_slot(&mut self, epoch: u16, commission: u8, slot: u64) -> Result<()> { + if let Some(entry) = self.history.last_mut() { + if entry.epoch == epoch { + entry.commission = commission; + entry.vote_account_last_update_slot = slot; + return Ok(()); + } + } + let entry = ValidatorHistoryEntry { + epoch, + commission, + vote_account_last_update_slot: slot, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + + Ok(()) + } + + pub fn set_contact_info( + &mut self, + epoch: u16, + contact_info: &ContactInfo, + contact_info_ts: u64, + ) -> Result<()> { + let ip = if let IpAddr::V4(address) = contact_info.addrs[0] { + address.octets() + } else { + return Err(ValidatorHistoryError::UnsupportedIpFormat.into()); + }; + + if self.last_ip_timestamp > contact_info_ts || self.last_version_timestamp > contact_info_ts + { + return Err(ValidatorHistoryError::GossipDataTooOld.into()); + } + self.last_ip_timestamp = contact_info_ts; + self.last_version_timestamp = contact_info_ts; + + if let Some(entry) = self.history.last_mut() { + if entry.epoch == epoch { + entry.ip = ip; + entry.client_type = contact_info.version.client as u8; + entry.version.major = contact_info.version.major as u8; + entry.version.minor = contact_info.version.minor as u8; + entry.version.patch = contact_info.version.patch; + return Ok(()); + } + } + + let entry = ValidatorHistoryEntry { + epoch, + ip, + client_type: contact_info.version.client as u8, + version: ClientVersion { + major: contact_info.version.major as u8, + minor: contact_info.version.minor as u8, + patch: contact_info.version.patch, + }, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + + Ok(()) + } + + pub fn set_legacy_contact_info( + &mut self, + epoch: u16, + legacy_contact_info: &LegacyContactInfo, + contact_info_ts: u64, + ) -> Result<()> { + let ip = if let IpAddr::V4(address) = legacy_contact_info.gossip.ip() { + address.octets() + } else { + return Err(ValidatorHistoryError::UnsupportedIpFormat.into()); + }; + if self.last_ip_timestamp > contact_info_ts { + return Err(ValidatorHistoryError::GossipDataTooOld.into()); + } + self.last_ip_timestamp = contact_info_ts; + + if let Some(entry) = self.history.last_mut() { + if entry.epoch == epoch { + entry.ip = ip; + return Ok(()); + } + } + + let entry = ValidatorHistoryEntry { + epoch, + ip, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + Ok(()) + } + + pub fn set_version(&mut self, epoch: u16, version: &Version2, version_ts: u64) -> Result<()> { + if self.last_version_timestamp > version_ts { + return Err(ValidatorHistoryError::GossipDataTooOld.into()); + } + self.last_version_timestamp = version_ts; + + if let Some(entry) = self.history.last_mut() { + if entry.epoch == epoch { + entry.version.major = version.version.major as u8; + entry.version.minor = version.version.minor as u8; + entry.version.patch = version.version.patch; + return Ok(()); + } + } + let entry = ValidatorHistoryEntry { + epoch, + version: ClientVersion { + major: version.version.major as u8, + minor: version.version.minor as u8, + patch: version.version.patch, + }, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + Ok(()) + } + + pub fn set_legacy_version( + &mut self, + epoch: u16, + legacy_version: &LegacyVersion, + version_ts: u64, + ) -> Result<()> { + if self.last_version_timestamp > version_ts { + return Err(ValidatorHistoryError::GossipDataTooOld.into()); + } + self.last_version_timestamp = version_ts; + + if let Some(entry) = self.history.last_mut() { + if entry.epoch == epoch { + entry.version.major = legacy_version.version.major as u8; + entry.version.minor = legacy_version.version.minor as u8; + entry.version.patch = legacy_version.version.patch; + return Ok(()); + } + } + let entry = ValidatorHistoryEntry { + epoch, + version: ClientVersion { + major: legacy_version.version.major as u8, + minor: legacy_version.version.minor as u8, + patch: legacy_version.version.patch, + }, + ..ValidatorHistoryEntry::default() + }; + self.history.push(entry); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Utility test to see struct layout + #[test] + fn test_validator_history_layout() { + println!("{}", ValidatorHistoryEntry::type_layout()); + } +} diff --git a/programs/validator-history/src/utils.rs b/programs/validator-history/src/utils.rs new file mode 100644 index 00000000..8fc9309c --- /dev/null +++ b/programs/validator-history/src/utils.rs @@ -0,0 +1,12 @@ +use anchor_lang::prelude::{AccountInfo, Pubkey}; + +pub fn cast_epoch(epoch: u64) -> u16 { + (epoch % u16::MAX as u64).try_into().unwrap() +} + +pub fn get_vote_account(validator_history_account_info: &AccountInfo) -> Pubkey { + let pubkey_bytes = &validator_history_account_info.data.borrow()[8..32 + 8]; + let mut data = [0; 32]; + data.copy_from_slice(pubkey_bytes); + Pubkey::from(data) +} diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 00000000..d12724da --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "tests" +version = "0.1.0" +description = "" +edition = "2021" +license = "Apache-2.0" +authors = ["Jito Foundation "] + +[lib] +crate-type = ["cdylib", "lib"] +name = "tests" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.28.0" +bincode = "1.3.3" +bytemuck = { version = "1.13.1", features = ["derive", "min_const_generics"] } +cfg-if = "1.0.0" +ed25519-dalek = "1.0.1" + +futures = "0.3.25" +jito-tip-distribution = { git = "https://github.com/jito-foundation/jito-programs", rev = "b8f8d315306d6ac32450bd1aa23b7c1322a83bc7", features = ["no-entrypoint"] } +matches = "0.1.10" +rand = "0.7.3" +semver = "1.0.17" +serde = "1.0.183" +serde_derive = "1.0.183" +solana-gossip = "1.16" +solana-program-test = "1.16" +solana-sdk = "1.16" +solana-version = "1.16" +validator-history = { path = "../programs/validator-history" } +validator-history-vote-state = { path = "../utils/vote-state" } diff --git a/tests/src/fixtures.rs b/tests/src/fixtures.rs new file mode 100644 index 00000000..3a3e3584 --- /dev/null +++ b/tests/src/fixtures.rs @@ -0,0 +1,300 @@ +#![allow(clippy::await_holding_refcell_ref)] +use anchor_lang::{ + solana_program::{ + clock::Clock, + pubkey::Pubkey, + vote::state::{VoteInit, VoteState, VoteStateVersions}, + }, + AccountSerialize, InstructionData, ToAccountMetas, +}; +use solana_program_test::*; +use solana_sdk::{ + account::Account, epoch_schedule::EpochSchedule, instruction::Instruction, signature::Keypair, + signer::Signer, transaction::Transaction, +}; +use std::{cell::RefCell, rc::Rc}; + +use jito_tip_distribution::{ + sdk::derive_tip_distribution_account_address, state::TipDistributionAccount, +}; +use validator_history::{self, constants::MAX_ALLOC_BYTES, ValidatorHistory}; + +pub struct TestFixture { + pub ctx: Rc>, + pub vote_account: Pubkey, + pub identity_keypair: Keypair, + pub validator_history_account: Pubkey, + pub validator_history_config: Pubkey, + pub tip_distribution_account: Pubkey, + pub keypair: Keypair, +} + +impl TestFixture { + pub async fn new() -> Self { + /* + Initializes test context with ValidatorHistory and TipDistribution programs loaded, as well as + a vote account and a system account for signing transactions. + + Returns a fixture with relevant account addresses and keypairs. + */ + let mut program = ProgramTest::new( + "validator-history", + validator_history::ID, + processor!(validator_history::entry), + ); + program.add_program( + "jito-tip-distribution", + jito_tip_distribution::id(), + processor!(jito_tip_distribution::entry), + ); + + let epoch = 0; + let vote_account = Pubkey::new_unique(); + let identity_keypair = Keypair::new(); + let identity_pubkey = identity_keypair.pubkey(); + let tip_distribution_account = derive_tip_distribution_account_address( + &jito_tip_distribution::id(), + &vote_account, + epoch, + ) + .0; + let validator_history_config = Pubkey::find_program_address( + &[validator_history::state::Config::SEED], + &validator_history::id(), + ) + .0; + let validator_history_account = Pubkey::find_program_address( + &[ + validator_history::state::ValidatorHistory::SEED, + vote_account.as_ref(), + ], + &validator_history::id(), + ) + .0; + let keypair = Keypair::new(); + + program.add_account( + vote_account, + new_vote_account(identity_pubkey, vote_account, 1, Some(vec![(0, 0, 0); 10])), + ); + program.add_account(keypair.pubkey(), system_account(100_000_000_000)); + + let ctx = Rc::new(RefCell::new(program.start_with_context().await)); + + Self { + ctx, + validator_history_config, + validator_history_account, + identity_keypair, + vote_account, + tip_distribution_account, + keypair, + } + } + + pub async fn load_and_deserialize( + &self, + address: &Pubkey, + ) -> T { + let ai = self + .ctx + .borrow_mut() + .banks_client + .get_account(*address) + .await + .unwrap() + .unwrap(); + + T::try_deserialize(&mut ai.data.as_slice()).unwrap() + } + + pub async fn initialize_config(&self) { + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::InitializeConfig { + config: self.validator_history_config, + system_program: anchor_lang::solana_program::system_program::id(), + signer: self.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::InitializeConfig { + authority: self.keypair.pubkey(), + } + .data(), + }; + let set_tip_distribution_instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::SetNewTipDistributionProgram { + config: self.validator_history_config, + new_tip_distribution_program: jito_tip_distribution::id(), + tip_distribution_authority: self.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewTipDistributionProgram {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction, set_tip_distribution_instruction], + Some(&self.keypair.pubkey()), + &[&self.keypair], + self.ctx.borrow().last_blockhash, + ); + if let Err(e) = self + .ctx + .borrow_mut() + .banks_client + .process_transaction_with_preflight(transaction) + .await + { + panic!("Error: {}", e); + } + } + + pub async fn initialize_validator_history_account(&self) { + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + system_program: anchor_lang::solana_program::system_program::id(), + signer: self.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }; + + let mut ixs = vec![instruction]; + + // Realloc validator history account + let num_reallocs = (ValidatorHistory::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1; + ixs.extend(vec![ + Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: self.validator_history_account, + vote_account: self.vote_account, + config: self.validator_history_config, + system_program: anchor_lang::solana_program::system_program::id(), + signer: self.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + num_reallocs + ]); + let transaction = Transaction::new_signed_with_payer( + &ixs, + Some(&self.keypair.pubkey()), + &[&self.keypair], + self.ctx.borrow().last_blockhash, + ); + self.submit_transaction_assert_success(transaction).await; + } + + pub async fn advance_num_epochs(&self, num_epochs: u64) { + let clock: Clock = self + .ctx + .borrow_mut() + .banks_client + .get_sysvar() + .await + .expect("Failed getting clock"); + let epoch_schedule: EpochSchedule = self.ctx.borrow().genesis_config().epoch_schedule; + let target_epoch = clock.epoch + num_epochs; + let target_slot = epoch_schedule.get_first_slot_in_epoch(target_epoch); + + self.ctx + .borrow_mut() + .warp_to_slot(target_slot) + .expect("Failed warping to future epoch"); + } + + pub async fn submit_transaction_assert_success(&self, transaction: Transaction) { + let mut ctx = self.ctx.borrow_mut(); + if let Err(e) = ctx + .banks_client + .process_transaction_with_preflight(transaction) + .await + { + panic!("Error: {}", e); + } + } + + pub async fn submit_transaction_assert_error( + &self, + transaction: Transaction, + error_message: &str, + ) { + if let Err(e) = self + .ctx + .borrow_mut() + .banks_client + .process_transaction_with_preflight(transaction) + .await + { + assert!(e.to_string().contains(error_message)); + } else { + panic!("Error: Transaction succeeded. Expected {}", error_message); + } + } +} + +pub fn system_account(lamports: u64) -> Account { + Account { + lamports, + owner: anchor_lang::system_program::ID, + executable: false, + rent_epoch: 0, + data: vec![], + } +} + +pub fn new_vote_account( + node_pubkey: Pubkey, + vote_pubkey: Pubkey, + commission: u8, + maybe_epoch_credits: Option>, +) -> Account { + let vote_init = VoteInit { + node_pubkey, + authorized_voter: vote_pubkey, + authorized_withdrawer: vote_pubkey, + commission, + }; + let clock = Clock { + epoch: 0, + slot: 0, + unix_timestamp: 0, + leader_schedule_epoch: 0, + epoch_start_timestamp: 0, + }; + let mut vote_state = VoteState::new(&vote_init, &clock); + if let Some(epoch_credits) = maybe_epoch_credits { + vote_state.epoch_credits = epoch_credits; + } + let vote_state_versions = VoteStateVersions::new_current(vote_state); + let mut data = vec![0; VoteState::size_of()]; + VoteState::serialize(&vote_state_versions, &mut data).unwrap(); + + Account { + lamports: 1000000, + data, + owner: anchor_lang::solana_program::vote::program::ID, + ..Account::default() + } +} + +pub fn new_tip_distribution_account(vote_account: Pubkey, mev_commission_bps: u16) -> Account { + let tda = TipDistributionAccount { + validator_vote_account: vote_account, + validator_commission_bps: mev_commission_bps, + ..TipDistributionAccount::default() + }; + let mut data = vec![]; + tda.try_serialize(&mut data).unwrap(); + Account { + lamports: 1000000, + data, + owner: jito_tip_distribution::id(), + ..Account::default() + } +} diff --git a/tests/src/lib.rs b/tests/src/lib.rs new file mode 100644 index 00000000..d066349c --- /dev/null +++ b/tests/src/lib.rs @@ -0,0 +1 @@ +pub mod fixtures; diff --git a/tests/tests/test_gossip.rs b/tests/tests/test_gossip.rs new file mode 100644 index 00000000..53dc267e --- /dev/null +++ b/tests/tests/test_gossip.rs @@ -0,0 +1,405 @@ +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + +use anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}; +use bincode::serialize; +use rand::thread_rng; +use solana_gossip::{ + contact_info::ContactInfo, + crds_value::{CrdsData, NodeInstance, Version}, + legacy_contact_info::LegacyContactInfo, +}; +use solana_program_test::*; +use solana_sdk::{ + clock::Clock, ed25519_instruction::new_ed25519_instruction, signer::Signer, + transaction::Transaction, +}; +use solana_version::LegacyVersion2; +use tests::fixtures::TestFixture; +use validator_history::{ + crds_value::{CrdsData as ValidatorHistoryCrdsData, LegacyVersion, LegacyVersion1}, + ValidatorHistory, +}; + +fn create_gossip_tx(fixture: &TestFixture, crds_data: &CrdsData) -> Transaction { + let ctx = &fixture.ctx; + let dalek_keypair = + ed25519_dalek::Keypair::from_bytes(&fixture.identity_keypair.to_bytes()).unwrap(); + + // create ed25519 instruction + let ed25519_ix = new_ed25519_instruction(&dalek_keypair, &serialize(crds_data).unwrap()); + + // create CopyGossipContactInfo instruction + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::CopyGossipContactInfo { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + instructions: anchor_lang::solana_program::sysvar::instructions::id(), + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::CopyGossipContactInfo {}.data(), + }; + Transaction::new_signed_with_payer( + &[ed25519_ix, instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ) +} + +#[tokio::test] +async fn test_copy_legacy_contact_info() { + let fixture = TestFixture::new().await; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // create legacycontactinfo as signed crdsdata struct + let mut legacy_contact_info = LegacyContactInfo::new_rand( + &mut rand::thread_rng(), + Some(fixture.identity_keypair.pubkey()), + ); + legacy_contact_info.set_wallclock(0); + let crds_data = CrdsData::LegacyContactInfo(legacy_contact_info.clone()); + let transaction = create_gossip_tx(&fixture, &crds_data); + + fixture.submit_transaction_assert_success(transaction).await; + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + + let ip = if let IpAddr::V4(ipv4) = legacy_contact_info.gossip().unwrap().ip() { + ipv4.octets() + } else { + panic!("IPV6 not supported") + }; + assert!(account.history.arr[0].ip == ip); + assert!(account.history.arr[0].epoch == 0); +} + +#[tokio::test] +async fn test_copy_contact_info() { + let fixture = TestFixture::new().await; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + let wallclock = 0; + let mut contact_info = ContactInfo::new(fixture.identity_keypair.pubkey(), wallclock, 0); + let ipv4 = Ipv4Addr::new(1, 2, 3, 4); + let ip = IpAddr::V4(ipv4); + contact_info + .set_socket(0, SocketAddr::new(ip, 1234)) + .expect("could not set socket"); + + let crds_data = CrdsData::ContactInfo(contact_info.clone()); + let transaction = create_gossip_tx(&fixture, &crds_data); + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + + let version = solana_version::Version::default(); + assert!(account.history.arr[0].version.major == version.major as u8); + assert!(account.history.arr[0].version.minor == version.minor as u8); + assert!(account.history.arr[0].version.patch == version.patch); + assert!(account.history.arr[0].ip == ipv4.octets()); + assert!(account.history.arr[0].epoch == 0); +} + +#[tokio::test] +async fn test_copy_legacy_version() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // can't import LegacyVersion from gossip cause inner fields are private + let version = LegacyVersion { + from: fixture.identity_keypair.pubkey(), + wallclock: 0, + version: LegacyVersion1 { + major: 1, + minor: 2, + patch: 3, + commit: None, + }, + }; + + let crds_data = ValidatorHistoryCrdsData::LegacyVersion(version.clone()); + let dalek_keypair = + ed25519_dalek::Keypair::from_bytes(&fixture.identity_keypair.to_bytes()).unwrap(); + + // create ed25519 instruction + let ed25519_ix = new_ed25519_instruction(&dalek_keypair, &serialize(&crds_data).unwrap()); + + // create CopyGossipContactInfo instruction + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::CopyGossipContactInfo { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + instructions: anchor_lang::solana_program::sysvar::instructions::id(), + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::CopyGossipContactInfo {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[ed25519_ix, instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.history.arr[0].version.major == version.version.major as u8); + assert!(account.history.arr[0].version.minor == version.version.minor as u8); + assert!(account.history.arr[0].version.patch == version.version.patch); + assert!(account.history.arr[0].epoch == 0); +} + +#[tokio::test] +async fn test_copy_version() { + let fixture = TestFixture::new().await; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + let version = Version { + from: fixture.identity_keypair.pubkey(), + wallclock: 0, + version: LegacyVersion2 { + major: 1, + minor: 2, + patch: 3, + commit: None, + feature_set: 0, + }, + }; + let crds_data = CrdsData::Version(version.clone()); + let transaction = create_gossip_tx(&fixture, &crds_data); + + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.history.arr[0].version.major == version.version.major as u8); + assert!(account.history.arr[0].version.minor == version.version.minor as u8); + assert!(account.history.arr[0].version.patch == version.version.patch); + assert!(account.history.arr[0].epoch == 0); +} + +#[tokio::test] +async fn test_gossip_wrong_signer() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + let version = Version { + from: fixture.identity_keypair.pubkey(), + wallclock: 0, + version: LegacyVersion2 { + major: 1, + minor: 2, + patch: 3, + commit: None, + feature_set: 0, + }, + }; + let crds_data = CrdsData::Version(version.clone()); + + // cranker keypair instead of node identity keypair + let dalek_keypair = ed25519_dalek::Keypair::from_bytes(&fixture.keypair.to_bytes()).unwrap(); + + let ed25519_ix = new_ed25519_instruction(&dalek_keypair, &serialize(&crds_data).unwrap()); + + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::CopyGossipContactInfo { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + instructions: anchor_lang::solana_program::sysvar::instructions::id(), + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::CopyGossipContactInfo {}.data(), + }; + + let transaction = Transaction::new_signed_with_payer( + &[ed25519_ix, instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + + fixture + .submit_transaction_assert_error(transaction, "GossipDataInvalid") + .await; +} + +#[tokio::test] +async fn test_gossip_wrong_node_pubkey() { + let fixture = TestFixture::new().await; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // vote account instead of identity account + let version = Version { + from: fixture.vote_account, + wallclock: 0, + version: LegacyVersion2 { + major: 1, + minor: 2, + patch: 3, + commit: None, + feature_set: 0, + }, + }; + let crds_data = CrdsData::Version(version.clone()); + let transaction = create_gossip_tx(&fixture, &crds_data); + + fixture + .submit_transaction_assert_error(transaction, "GossipDataInvalid") + .await; +} + +#[tokio::test] +async fn test_gossip_missing_sigverify_instruction() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::CopyGossipContactInfo { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + instructions: anchor_lang::solana_program::sysvar::instructions::id(), + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::CopyGossipContactInfo {}.data(), + }; + + let dummy_ix = anchor_lang::solana_program::system_instruction::transfer( + &fixture.keypair.pubkey(), + &fixture.vote_account, + 1, + ); + + let transaction = Transaction::new_signed_with_payer( + &[dummy_ix, instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture + .submit_transaction_assert_error(transaction, "NotSigVerified") + .await; +} + +#[tokio::test] +async fn test_gossip_wrong_message() { + let fixture = TestFixture::new().await; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // Not a crdsdata that we're expecting + let node_instance = NodeInstance::new(&mut thread_rng(), fixture.identity_keypair.pubkey(), 0); + let crds_data = CrdsData::NodeInstance(node_instance); + + let transaction = create_gossip_tx(&fixture, &crds_data); + + fixture + .submit_transaction_assert_error(transaction, "GossipDataInvalid") + .await; +} + +#[tokio::test] +async fn test_gossip_timestamps() { + let fixture = TestFixture::new().await; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + let mut banks_client = { + let ctx = fixture.ctx.borrow_mut(); + ctx.banks_client.clone() + }; + + let clock: Clock = banks_client.get_sysvar().await.unwrap(); + let wallclock = clock.unix_timestamp as u64; + let mut contact_info = ContactInfo::new(fixture.identity_keypair.pubkey(), wallclock, 0); + let ipv4 = Ipv4Addr::new(1, 2, 3, 4); + let ip = IpAddr::V4(ipv4); + contact_info + .set_socket(0, SocketAddr::new(ip, 1234)) + .expect("could not set socket"); + let crds_data = CrdsData::ContactInfo(contact_info.clone()); + + let transaction = create_gossip_tx(&fixture, &crds_data); + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.last_ip_timestamp == wallclock); + assert!(account.last_version_timestamp == wallclock); + + contact_info.set_wallclock(wallclock + 1); + + let crds_data = CrdsData::ContactInfo(contact_info.clone()); + let transaction = create_gossip_tx(&fixture, &crds_data); + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.last_ip_timestamp == wallclock + 1); + assert!(account.last_version_timestamp == wallclock + 1); + + // LegacyContactInfo with old wallclock + let mut legacy_contact_info = LegacyContactInfo::new_rand( + &mut rand::thread_rng(), + Some(fixture.identity_keypair.pubkey()), + ); + legacy_contact_info.set_wallclock(wallclock); + + let crds_data = CrdsData::LegacyContactInfo(legacy_contact_info); + let transaction = create_gossip_tx(&fixture, &crds_data); + fixture + .submit_transaction_assert_error(transaction, "GossipDataTooOld") + .await; + + // LegacyVersion with old wallclock + let version = Version { + from: fixture.identity_keypair.pubkey(), + wallclock, + version: LegacyVersion2 { + major: 1, + minor: 2, + patch: 3, + commit: None, + feature_set: 0, + }, + }; + let crds_data = CrdsData::Version(version); + let transaction = create_gossip_tx(&fixture, &crds_data); + + fixture + .submit_transaction_assert_error(transaction, "GossipDataTooOld") + .await; + + // ContactInfo with 11 minutes in the future wallclock - will fail + contact_info.set_wallclock(wallclock + 11 * 60); + let crds_data = CrdsData::ContactInfo(contact_info.clone()); + let transaction = create_gossip_tx(&fixture, &crds_data); + + fixture + .submit_transaction_assert_error(transaction, "GossipDataInFuture") + .await; +} diff --git a/tests/tests/test_initialize.rs b/tests/tests/test_initialize.rs new file mode 100644 index 00000000..d85c9836 --- /dev/null +++ b/tests/tests/test_initialize.rs @@ -0,0 +1,214 @@ +#![allow(clippy::await_holding_refcell_ref)] +use anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}; +use solana_program_test::*; +use solana_sdk::{signer::Signer, transaction::Transaction}; +use tests::fixtures::{new_vote_account, TestFixture}; +use validator_history::{constants::MAX_ALLOC_BYTES, Config, ValidatorHistory}; + +#[tokio::test] +async fn test_initialize() { + let test = TestFixture::new().await; + let ctx = &test.ctx; + + // Initialize config + // config keypair + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::InitializeConfig { + config: test.validator_history_config, + system_program: anchor_lang::solana_program::system_program::id(), + signer: test.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::InitializeConfig { + authority: test.keypair.pubkey(), + } + .data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&test.keypair.pubkey()), + &[&test.keypair], + ctx.borrow().last_blockhash, + ); + test.submit_transaction_assert_success(transaction).await; + + let config: Config = test + .load_and_deserialize(&test.validator_history_config) + .await; + + assert!(config.counter == 0); + assert!(config.stake_authority == test.keypair.pubkey()); + assert!(config.tip_distribution_authority == test.keypair.pubkey()); + + // Initialize validator history account + + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: test.validator_history_account, + vote_account: test.vote_account, + system_program: anchor_lang::solana_program::system_program::id(), + signer: test.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&test.keypair.pubkey()), + &[&test.keypair], + ctx.borrow().last_blockhash, + ); + test.submit_transaction_assert_success(transaction).await; + + // Get account and Assert exists + let account = ctx + .borrow_mut() + .banks_client + .get_account(test.validator_history_account) + .await + .unwrap(); + assert!(account.is_some()); + let account = account.unwrap(); + assert!(account.owner == validator_history::id()); + assert!(account.data.len() == MAX_ALLOC_BYTES); + + // Realloc validator history account + let num_reallocs = (ValidatorHistory::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1; + let ixs = vec![ + Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: test.validator_history_account, + vote_account: test.vote_account, + config: test.validator_history_config, + system_program: anchor_lang::solana_program::system_program::id(), + signer: test.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + num_reallocs + ]; + let transaction = Transaction::new_signed_with_payer( + &ixs, + Some(&test.keypair.pubkey()), + &[&test.keypair], + ctx.borrow().last_blockhash, + ); + test.submit_transaction_assert_success(transaction).await; + + // Assert final state + let account: ValidatorHistory = test + .load_and_deserialize(&test.validator_history_account) + .await; + assert!(account.index == 0); + assert!(account.vote_account == test.vote_account); + assert!(account.struct_version == 0); + assert!(account.history.idx == 511); + assert!(account.history.arr.len() == 512); + assert!(account.history.is_empty == 1); +} + +#[tokio::test] +async fn test_initialize_fail() { + let test = TestFixture::new().await; + let ctx = &test.ctx; + + test.initialize_config().await; + + // Bad vote account: less than 5 epochs of credits + let vote_account = new_vote_account(test.vote_account, test.vote_account, 0, None); + + ctx.borrow_mut() + .set_account(&test.vote_account, &vote_account.into()); + // Initialize validator history account + + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::InitializeValidatorHistoryAccount { + validator_history_account: test.validator_history_account, + vote_account: test.vote_account, + system_program: anchor_lang::solana_program::system_program::id(), + signer: test.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::InitializeValidatorHistoryAccount {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&test.keypair.pubkey()), + &[&test.keypair], + ctx.borrow().last_blockhash, + ); + test.submit_transaction_assert_error(transaction, "NotEnoughVotingHistory") + .await; +} + +#[tokio::test] +async fn test_extra_realloc() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // Set initial value + let stake = 1000; + let rank = 42; + let is_superminority = false; + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateStakeHistory { + epoch: 0, + lamports: stake, + rank, + is_superminority, + } + .data(), + accounts: validator_history::accounts::UpdateStakeHistory { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + stake_authority: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture.submit_transaction_assert_success(transaction).await; + + let ix = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::ReallocValidatorHistoryAccount { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + system_program: anchor_lang::solana_program::system_program::id(), + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::ReallocValidatorHistoryAccount {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[ix], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + + // Extra realloc should not wipe the account + assert!(account.history.idx == 0); + assert!(account.history.arr[0].activated_stake_lamports == stake); + assert!(account.history.arr[0].rank == rank); + assert!(account.history.arr[0].is_superminority == 0); +} diff --git a/tests/tests/test_mev_commission.rs b/tests/tests/test_mev_commission.rs new file mode 100644 index 00000000..9915bdef --- /dev/null +++ b/tests/tests/test_mev_commission.rs @@ -0,0 +1,250 @@ +#![allow(clippy::await_holding_refcell_ref)] +use anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}; +use jito_tip_distribution::sdk::derive_tip_distribution_account_address; +use solana_program_test::*; +use solana_sdk::{pubkey::Pubkey, signer::Signer, transaction::Transaction}; +use tests::fixtures::{new_tip_distribution_account, TestFixture}; +use validator_history::{Config, ValidatorHistory}; + +#[tokio::test] +async fn test_mev_commission() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + let epoch = 0; + let tip_distribution_account = derive_tip_distribution_account_address( + &jito_tip_distribution::id(), + &fixture.vote_account, + epoch, + ) + .0; + ctx.borrow_mut().set_account( + &tip_distribution_account, + &new_tip_distribution_account(fixture.vote_account, 42).into(), + ); + + // update mev commission + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateMevCommission {}.data(), + accounts: validator_history::accounts::UpdateMevCommission { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + tip_distribution_account, + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + + if let Err(e) = ctx + .borrow_mut() + .banks_client + .process_transaction_with_preflight(transaction) + .await + { + panic!("Error: {}", e); + } + + // assert value + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.history.idx == 0); + assert!(account.history.arr[0].epoch == 0); + assert!(account.history.arr[0].mev_commission == 42); + + // TODO this is causing a hash mismatch issue + // fixture.advance_num_epochs(1).await; + + // // create new TDA + // let tip_distribution_account = derive_tip_distribution_account_address( + // &jito_tip_distribution::id(), + // &fixture.vote_account, + // 1, + // ) + // .0; + // ctx.borrow_mut().set_account( + // &tip_distribution_account, + // &new_tip_distribution_account(fixture.vote_account, 43).into(), + // ); + + // let instruction = Instruction { + // program_id: validator_history::id(), + // data: validator_history::instruction::UpdateMevCommission {}.data(), + // accounts: validator_history::accounts::UpdateMevCommission { + // validator_history_account: fixture.validator_history_account, + // vote_account: fixture.vote_account, + // config: fixture.validator_history_config, + // tip_distribution_account, + // signer: fixture.keypair.pubkey(), + // } + // .to_account_metas(None), + // }; + + // let transaction = Transaction::new_signed_with_payer( + // &[instruction], + // Some(&fixture.keypair.pubkey()), + // &[&fixture.keypair], + // ctx.borrow().last_blockhash, + // ); + + // fixture.submit_transaction_assert_success(transaction).await; + // // Assert values + // let account: ValidatorHistory = fixture + // .load_and_deserialize(&fixture.validator_history_account) + // .await; + // assert!(account.history.idx == 1); + // assert!(account.history.arr[1].epoch == 1); + // assert!(account.history.arr[1].mev_commission == 43); +} + +#[tokio::test] +async fn test_mev_commission_fail() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // test update mev commission with uninitialized TDA + let tip_distribution_account = derive_tip_distribution_account_address( + &jito_tip_distribution::id(), + &fixture.vote_account, + 0, + ) + .0; + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateMevCommission {}.data(), + accounts: validator_history::accounts::UpdateMevCommission { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + tip_distribution_account, + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction.clone()], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture + .submit_transaction_assert_error(transaction, "ConstraintOwner") + .await; + + fixture.advance_num_epochs(1).await; + // test update mev commission with wrong epoch + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture + .submit_transaction_assert_error(transaction, "ConstraintSeeds") + .await; + + let new_vote_account = Pubkey::new_unique(); + let tip_distribution_account = + derive_tip_distribution_account_address(&jito_tip_distribution::id(), &new_vote_account, 1) + .0; + ctx.borrow_mut().set_account( + &tip_distribution_account, + &new_tip_distribution_account(new_vote_account, 42).into(), + ); + + // test update mev commission with wrong validator's TDA + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateMevCommission {}.data(), + accounts: validator_history::accounts::UpdateMevCommission { + validator_history_account: fixture.validator_history_account, + vote_account: new_vote_account, + config: fixture.validator_history_config, + tip_distribution_account, + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + + fixture + .submit_transaction_assert_error(transaction, "ConstraintSeeds") + .await; +} + +// Test change tip distribution authority +#[tokio::test] +async fn test_change_tip_distribution_authority() { + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + + fixture.initialize_config().await; + + let new_authority = Pubkey::new_unique(); + + // Change tip distribution authority + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::SetNewTipDistributionAuthority { + config: fixture.validator_history_config, + new_authority, + tip_distribution_authority: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewTipDistributionAuthority {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture.submit_transaction_assert_success(transaction).await; + + // Assert new authority + let config: Config = fixture + .load_and_deserialize(&fixture.validator_history_config) + .await; + + assert!(config.tip_distribution_authority == new_authority); + + // Change tip distribution authority + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::SetNewTipDistributionAuthority { + config: fixture.validator_history_config, + new_authority: fixture.keypair.pubkey(), + tip_distribution_authority: fixture.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewTipDistributionAuthority {}.data(), + }; + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture + .submit_transaction_assert_error(transaction, "ConstraintHasOne.") + .await; +} diff --git a/tests/tests/test_stake.rs b/tests/tests/test_stake.rs new file mode 100644 index 00000000..04dc200c --- /dev/null +++ b/tests/tests/test_stake.rs @@ -0,0 +1,242 @@ +#![allow(clippy::await_holding_refcell_ref)] +use anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}; +use solana_program_test::*; +use solana_sdk::{ + clock::Clock, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction, +}; +use tests::fixtures::{system_account, TestFixture}; +use validator_history::{Config, ValidatorHistory}; + +#[tokio::test] +async fn test_stake_history_basic_update() { + // init fixture + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // update stake history + let stake = 1000; + let rank = 42; + let is_superminority = false; + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateStakeHistory { + epoch: 0, + lamports: stake, + rank, + is_superminority, + } + .data(), + accounts: validator_history::accounts::UpdateStakeHistory { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + stake_authority: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture.submit_transaction_assert_success(transaction).await; + + // assert value + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.history.idx == 0); + assert!(account.history.arr[0].epoch == 0); + assert!(account.history.arr[0].activated_stake_lamports == stake); + assert!(account.history.arr[0].is_superminority == 0); + assert!(account.history.arr[0].rank == rank); + + // sleep 2 epochs, wait again + fixture.advance_num_epochs(2).await; + + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateStakeHistory { + epoch: 2, + lamports: stake + 1, + rank: rank - 1, + is_superminority: true, + } + .data(), + accounts: validator_history::accounts::UpdateStakeHistory { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + stake_authority: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture.submit_transaction_assert_success(transaction).await; + + // assert value + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + assert!(account.history.idx == 1); + assert!(account.history.arr[1].epoch == 2); + assert!(account.history.arr[1].activated_stake_lamports == stake + 1); + assert!(account.history.arr[1].is_superminority == 1); + assert!(account.history.arr[1].rank == rank - 1); +} + +#[tokio::test] +async fn test_stake_history_wrong_authority() { + // init fixture + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // attempt update with wrong authority + let new_authority = Keypair::new(); + ctx.borrow_mut() + .set_account(&new_authority.pubkey(), &system_account(10000000).into()); + + let stake = 1000; + let rank = 42; + let is_superminority = false; + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateStakeHistory { + epoch: 0, + lamports: stake, + rank, + is_superminority, + } + .data(), + accounts: validator_history::accounts::UpdateStakeHistory { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + stake_authority: new_authority.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&new_authority.pubkey()), + &[&new_authority], + ctx.borrow().last_blockhash, + ); + + fixture + .submit_transaction_assert_error(transaction, "ConstraintHasOne") + .await; +} + +#[tokio::test] +async fn test_stake_history_future_epoch() { + // init fixture + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + let clock: Clock = ctx + .borrow_mut() + .banks_client + .get_sysvar() + .await + .expect("Failed getting clock"); + // attempt update with future epoch + let stake = 1000; + let rank = 42; + let is_superminority = false; + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::UpdateStakeHistory { + epoch: clock.epoch + 1, + lamports: stake, + rank, + is_superminority, + } + .data(), + accounts: validator_history::accounts::UpdateStakeHistory { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + stake_authority: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + fixture + .submit_transaction_assert_error(transaction, "EpochOutOfRange") + .await; +} + +#[tokio::test] +async fn test_change_stake_authority() { + let test = TestFixture::new().await; + let ctx = &test.ctx; + + test.initialize_config().await; + + let new_authority = Pubkey::new_unique(); + + // Change stake authority + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::SetNewStakeAuthority { + config: test.validator_history_config, + new_authority, + stake_authority: test.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewStakeAuthority {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&test.keypair.pubkey()), + &[&test.keypair], + ctx.borrow().last_blockhash, + ); + test.submit_transaction_assert_success(transaction).await; + + // Assert + let config: Config = test + .load_and_deserialize(&test.validator_history_config) + .await; + + assert!(config.stake_authority == new_authority); + + // Try to change it back with wrong signer + let instruction = Instruction { + program_id: validator_history::id(), + accounts: validator_history::accounts::SetNewStakeAuthority { + config: test.validator_history_config, + new_authority: test.keypair.pubkey(), + stake_authority: test.keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewStakeAuthority {}.data(), + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&test.keypair.pubkey()), + &[&test.keypair], + ctx.borrow().last_blockhash, + ); + + test.submit_transaction_assert_error(transaction, "ConstraintHasOne") + .await; +} diff --git a/tests/tests/test_vote_account.rs b/tests/tests/test_vote_account.rs new file mode 100644 index 00000000..0e94692b --- /dev/null +++ b/tests/tests/test_vote_account.rs @@ -0,0 +1,112 @@ +#![allow(clippy::await_holding_refcell_ref)] +use anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}; +use solana_program_test::*; +use solana_sdk::{clock::Clock, signer::Signer, transaction::Transaction}; +use tests::fixtures::{new_vote_account, TestFixture}; +use validator_history::ValidatorHistory; + +#[tokio::test] +async fn test_copy_vote_account() { + // Initialize + let fixture = TestFixture::new().await; + let ctx = &fixture.ctx; + fixture.initialize_config().await; + fixture.initialize_validator_history_account().await; + + // Set with specific epoch credits, commission, last update slot + let epoch_credits = vec![(0, 20, 10)]; + ctx.borrow_mut().set_account( + &fixture.vote_account, + &new_vote_account( + fixture.vote_account, + fixture.vote_account, + 9, + Some(epoch_credits), + ) + .into(), + ); + + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::CopyVoteAccount {}.data(), + accounts: validator_history::accounts::CopyVoteAccount { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + + let clock: Clock = ctx + .borrow_mut() + .banks_client + .get_sysvar() + .await + .expect("clock"); + assert!(clock.epoch == 0); + + assert!(account.history.idx == 0); + assert!(account.history.arr[0].epoch == 0); + assert!(account.history.arr[0].vote_account_last_update_slot <= clock.slot); + assert!(account.history.arr[0].epoch_credits == 10); + assert!(account.history.arr[0].commission == 9); + + fixture.advance_num_epochs(2).await; + + let epoch_credits = vec![(0, 22, 10), (1, 34, 22), (2, 46, 34)]; + + ctx.borrow_mut().set_account( + &fixture.vote_account, + &new_vote_account( + fixture.vote_account, + fixture.vote_account, + 8, + Some(epoch_credits), + ) + .into(), + ); + + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::CopyVoteAccount {}.data(), + accounts: validator_history::accounts::CopyVoteAccount { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&fixture.keypair.pubkey()), + &[&fixture.keypair], + ctx.borrow().last_blockhash, + ); + + fixture.submit_transaction_assert_success(transaction).await; + + let account: ValidatorHistory = fixture + .load_and_deserialize(&fixture.validator_history_account) + .await; + + // check new epoch 0 values get copied over, but epoch 1 should be skipped + assert!(account.history.idx == 1); + assert!(account.history.arr[1].epoch == 2); + assert!(account.history.arr[1].commission == 8); + assert!(account.history.arr[1].epoch_credits == 12); + assert!(account.history.arr[0].epoch_credits == 12); +} diff --git a/utils/validator-history-cli/Cargo.toml b/utils/validator-history-cli/Cargo.toml new file mode 100644 index 00000000..2c39d269 --- /dev/null +++ b/utils/validator-history-cli/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "validator-history-cli" +version = "0.1.0" +edition = "2021" +description = "CLI to manage validator history program" + +[dependencies] +anchor-lang = "0.28.0" +clap = { version = "4.3.0", features = ["derive", "env"] } +futures = "0.3.21" +futures-util = "0.3.21" +jito-tip-distribution = { git = "https://github.com/jito-foundation/jito-programs", rev = "b8f8d315306d6ac32450bd1aa23b7c1322a83bc7" } +log = "0.4.18" +solana-account-decoder = "1.16" +solana-clap-utils = "1.16" +solana-client = "1.16" +solana-metrics = "1.16" +solana-program = "1.16" +solana-sdk = "1.16" +thiserror = "1.0.37" +tokio = { version = "~1.14.1", features = ["full"] } +validator-history = { path = "../../programs/validator-history" } diff --git a/utils/validator-history-cli/src/main.rs b/utils/validator-history-cli/src/main.rs new file mode 100644 index 00000000..5c4dc22d --- /dev/null +++ b/utils/validator-history-cli/src/main.rs @@ -0,0 +1,438 @@ +use std::{path::PathBuf, time::Duration}; + +use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas}; +use clap::{arg, command, Parser, Subcommand}; +use solana_client::{ + rpc_client::RpcClient, + rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, + rpc_filter::{Memcmp, RpcFilterType}, +}; +use solana_program::instruction::Instruction; +use solana_sdk::{ + pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction, +}; +use validator_history::{Config, ValidatorHistory, ValidatorHistoryEntry}; + +#[derive(Parser)] +#[command(about = "CLI for validator history program")] +struct Args { + /// RPC URL for the cluster + #[arg( + short, + long, + env, + default_value = "https://api.mainnet-beta.solana.com" + )] + json_rpc_url: String, + + #[command(subcommand)] + commands: Commands, +} + +#[derive(Subcommand)] +enum Commands { + InitConfig(InitConfig), + CrankerStatus(CrankerStatus), + History(History), +} + +#[derive(Parser)] +#[command(about = "Initialize config account")] +struct InitConfig { + /// Path to keypair used to pay for account creation and execute transactions + #[arg(short, long, env, default_value = "~/.config/solana/id.json")] + keypair_path: PathBuf, + + /// Tip distribution program ID (Pubkey as base58 string) + #[arg(short, long, env)] + tip_distribution_program_id: Pubkey, + + /// New tip distribution authority (Pubkey as base58 string) + /// + /// If not provided, the initial keypair will be the authority + #[arg(short, long, env, required(false))] + tip_distribution_authority: Option, + + // New stake authority (Pubkey as base58 string) + /// + /// If not provided, the initial keypair will be the authority + #[arg(short, long, env, required(false))] + stake_authority: Option, +} + +#[derive(Parser, Debug)] +#[command(about = "Get cranker status")] +struct CrankerStatus { + /// Epoch to get status for (default: current epoch) + #[arg(short, long, env)] + epoch: Option, +} + +#[derive(Parser)] +#[command(about = "Get validator history")] +struct History { + /// Validator to get history for + validator: Pubkey, + + /// Start epoch + #[arg(short, long, env)] + start_epoch: Option, +} + +fn command_init_config(args: InitConfig, client: RpcClient) { + // Creates config account, sets tip distribution program address, and optionally sets authority for commission history program + let keypair = read_keypair_file(args.keypair_path).expect("Failed reading keypair file"); + + let mut instructions = vec![]; + let (config_pda, _) = Pubkey::find_program_address(&[Config::SEED], &validator_history::ID); + instructions.push(Instruction { + program_id: validator_history::ID, + accounts: validator_history::accounts::InitializeConfig { + config: config_pda, + system_program: solana_program::system_program::id(), + signer: keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::InitializeConfig { + authority: keypair.pubkey(), + } + .data(), + }); + + instructions.push(Instruction { + program_id: validator_history::ID, + accounts: validator_history::accounts::SetNewTipDistributionProgram { + config: config_pda, + new_tip_distribution_program: args.tip_distribution_program_id, + tip_distribution_authority: keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewTipDistributionProgram {}.data(), + }); + + if let Some(new_authority) = args.tip_distribution_authority { + instructions.push(Instruction { + program_id: validator_history::ID, + accounts: validator_history::accounts::SetNewTipDistributionAuthority { + config: config_pda, + new_authority, + tip_distribution_authority: keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewTipDistributionAuthority {}.data(), + }); + } + + if let Some(new_authority) = args.stake_authority { + instructions.push(Instruction { + program_id: validator_history::ID, + accounts: validator_history::accounts::SetNewStakeAuthority { + config: config_pda, + new_authority, + stake_authority: keypair.pubkey(), + } + .to_account_metas(None), + data: validator_history::instruction::SetNewStakeAuthority {}.data(), + }); + } + + let blockhash = client + .get_latest_blockhash() + .expect("Failed to get recent blockhash"); + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&keypair.pubkey()), + &[&keypair], + blockhash, + ); + + let signature = client + .send_and_confirm_transaction_with_spinner(&transaction) + .expect("Failed to send transaction"); + println!("Signature: {}", signature); +} + +fn get_entry(validator_history: ValidatorHistory, epoch: u64) -> Option { + // Util to fetch an entry for a specific epoch + validator_history + .history + .arr + .into_iter() + .find(|entry| entry.epoch == epoch as u16) +} + +fn formatted_entry(entry: ValidatorHistoryEntry) -> String { + let commission_str = if entry.commission == ValidatorHistoryEntry::default().commission { + "[NULL]".to_string() + } else { + entry.commission.to_string() + }; + + let epoch_credits_str = if entry.epoch_credits == ValidatorHistoryEntry::default().epoch_credits + { + "[NULL]".to_string() + } else { + entry.epoch_credits.to_string() + }; + + let mev_commission_str = + if entry.mev_commission == ValidatorHistoryEntry::default().mev_commission { + "[NULL]".to_string() + } else { + entry.mev_commission.to_string() + }; + + let stake_str = if entry.activated_stake_lamports + == ValidatorHistoryEntry::default().activated_stake_lamports + { + "[NULL]".to_string() + } else { + entry.activated_stake_lamports.to_string() + }; + + let ip_str = if entry.ip == ValidatorHistoryEntry::default().ip { + "[NULL]".to_string() + } else { + format!( + "{}.{}.{}.{}", + entry.ip[0], entry.ip[1], entry.ip[2], entry.ip[3] + ) + }; + + let client_type_str = if entry.client_type == ValidatorHistoryEntry::default().client_type { + "[NULL]".to_string() + } else { + entry.client_type.to_string() + }; + + let client_version_str = if entry.version.major + == ValidatorHistoryEntry::default().version.major + && entry.version.minor == ValidatorHistoryEntry::default().version.minor + && entry.version.patch == ValidatorHistoryEntry::default().version.patch + { + "[NULL]".to_string() + } else { + format!( + "{}.{}.{}", + entry.version.major, entry.version.minor, entry.version.patch + ) + }; + + let rank_str = if entry.rank == ValidatorHistoryEntry::default().rank { + "[NULL]".to_string() + } else { + entry.rank.to_string() + }; + + let superminority_str = + if entry.is_superminority == ValidatorHistoryEntry::default().is_superminority { + "[NULL]".to_string() + } else { + entry.is_superminority.to_string() + }; + + let last_update_slot = if entry.vote_account_last_update_slot + == ValidatorHistoryEntry::default().vote_account_last_update_slot + { + "[NULL]".to_string() + } else { + entry.vote_account_last_update_slot.to_string() + }; + + format!( + "Commission: {}\t| Epoch Credits: {}\t| MEV Commission: {}\t| Stake: {}\t| Rank: {}\t| Superminority: {}\t| IP: {}\t| Client Type: {}\t| Client Version: {}\t| Last Updated: {}", + commission_str, + epoch_credits_str, + mev_commission_str, + stake_str, + rank_str, + superminority_str, + ip_str, + client_type_str, + client_version_str, + last_update_slot + ) +} + +fn command_cranker_status(args: CrankerStatus, client: RpcClient) { + // Displays current epoch ValidatorHistory entry for each validator, and summary of updated fields + let epoch = args.epoch.unwrap_or_else(|| { + client + .get_epoch_info() + .expect("Failed to get epoch info") + .epoch + }); + + // Config account + let (config_pda, _) = Pubkey::find_program_address(&[Config::SEED], &validator_history::ID); + // Fetch config account + let config_account = client + .get_account(&config_pda) + .expect("Failed to get config account"); + let config = Config::try_deserialize(&mut config_account.data.as_slice()) + .expect("Failed to deserialize config account"); + + // Fetch every ValidatorHistory account + let gpa_config = RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 0, + ValidatorHistory::discriminator().into(), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(solana_account_decoder::UiAccountEncoding::Base64), + ..RpcAccountInfoConfig::default() + }, + ..RpcProgramAccountsConfig::default() + }; + let mut validator_history_accounts = client + .get_program_accounts_with_config(&validator_history::id(), gpa_config) + .expect("Failed to get validator history accounts"); + + let mut validator_histories = validator_history_accounts + .iter_mut() + .map(|(_, account)| { + let validator_history = ValidatorHistory::try_deserialize(&mut account.data.as_slice()) + .expect("Failed to deserialize validator history account"); + validator_history + }) + .collect::>(); + + assert_eq!( + validator_histories.len(), + config.counter as usize, + "Number of validator history accounts does not match config counter" + ); + + validator_histories.sort_by(|a, b| a.index.cmp(&b.index)); + + // For each validator history account, print out the status + println!("Epoch {} Report", epoch); + let mut ips = 0; + let mut versions = 0; + let mut types = 0; + let mut mev_comms = 0; + let mut comms = 0; + let mut epoch_credits = 0; + let mut stakes = 0; + let mut ranks = 0; + let mut superminorities = 0; + let default = ValidatorHistoryEntry::default(); + for validator_history in validator_histories { + match get_entry(validator_history, epoch) { + Some(entry) => { + if entry.ip != default.ip { + ips += 1; + } + if !(entry.version.major == default.version.major + && entry.version.minor == default.version.minor + && entry.version.patch == default.version.patch) + { + versions += 1; + } + if entry.client_type != default.client_type { + types += 1; + } + if entry.mev_commission != default.mev_commission { + mev_comms += 1; + } + if entry.commission != default.commission { + comms += 1; + } + if entry.epoch_credits != default.epoch_credits { + epoch_credits += 1; + } + if entry.activated_stake_lamports != default.activated_stake_lamports { + stakes += 1; + } + if entry.rank != default.rank { + ranks += 1; + } + if entry.is_superminority != default.is_superminority { + superminorities += 1; + } + println!( + "{}.\tVote Account: {} | {}", + validator_history.index, + validator_history.vote_account, + formatted_entry(entry) + ); + } + None => { + println!( + "{}.\tVote Account: {} | {}", + validator_history.index, + validator_history.vote_account, + formatted_entry(ValidatorHistoryEntry::default()) + ); + } + }; + } + println!("Total Validators:\t\t{}", config.counter); + println!("Validators with IP:\t\t{}", ips); + println!("Validators with Version:\t{}", versions); + println!("Validators with Client Type:\t{}", types); + println!("Validators with MEV Commission: {}", mev_comms); + println!("Validators with Commission:\t{}", comms); + println!("Validators with Epoch Credits:\t{}", epoch_credits); + println!("Validators with Stake:\t\t{}", stakes); + println!("Validators with Rank:\t\t{}", ranks); + println!("Validators with Superminority:\t\t{}", superminorities); +} + +fn command_history(args: History, client: RpcClient) { + // Get single validator history account and display all epochs of history + let (validator_history_pda, _) = Pubkey::find_program_address( + &[ValidatorHistory::SEED, args.validator.as_ref()], + &validator_history::ID, + ); + let validator_history_account = client + .get_account(&validator_history_pda) + .expect("Failed to get validator history account"); + let validator_history = + ValidatorHistory::try_deserialize(&mut validator_history_account.data.as_slice()) + .expect("Failed to deserialize validator history account"); + let start_epoch = args.start_epoch.unwrap_or_else(|| { + validator_history + .history + .arr + .iter() + .filter_map(|entry| { + if entry.epoch > 0 { + Some(entry.epoch as u64) + } else { + None + } + }) + .min() + .unwrap_or(0) + }); + let current_epoch = client + .get_epoch_info() + .expect("Failed to get epoch info") + .epoch; + println!( + "History for validator {} | Validator History Account {}", + args.validator, validator_history_pda + ); + for epoch in start_epoch..=current_epoch { + match get_entry(validator_history, epoch) { + Some(entry) => { + println!("Epoch: {} | {}", epoch, formatted_entry(entry)); + } + None => { + println!("Epoch {}:\tNo history", epoch); + continue; + } + } + } +} + +fn main() { + let args = Args::parse(); + let client = RpcClient::new_with_timeout(args.json_rpc_url.clone(), Duration::from_secs(60)); + match args.commands { + Commands::InitConfig(args) => command_init_config(args, client), + Commands::CrankerStatus(args) => command_cranker_status(args, client), + Commands::History(args) => command_history(args, client), + }; +} diff --git a/utils/vote-state/Cargo.lock b/utils/vote-state/Cargo.lock new file mode 100644 index 00000000..8c15db9e --- /dev/null +++ b/utils/vote-state/Cargo.lock @@ -0,0 +1,1537 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5e1a413b311b039d29b61d0dbb401c9dbf04f792497ceca87593454bf6d7dd" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca9aeaf633c6e2365fed0525dcac68610be58eee5dc69d3b86fe0b1d4b320b9" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788e44f9e8501dabeb6f9229da0f3268fb2ae3208912608ffaa056a72031296f" +dependencies = [ + "anchor-syn", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c4d8c7e4a2605ede6fcdced9690288b2f74e24768619a85229d57e597bc97" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3b07d5c5d87b5edc72428b447b8e9ee1143b83dd1afc6a6b1d352c6a6164d8" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22ad0445115dbea5869b1d062da49ae125abed9132fc20c33227f25e42dfa6b" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48daeff6781ba2f02961b0ad211feb9a2de75af345d42c62b1a252fd4dfb0724" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4fe2886f92c4f33ec1b2b8b2b43ca1b9070cf4929e63c7eaaa09a9f2c0d5123" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-lang" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbbe5d1c7c057c6d63b4f2f538a320e4a22111126c9966340c3d9490e2f15ed1" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-program", + "anchor-derive-accounts", + "anchor-derive-space", + "arrayref", + "base64 0.13.1", + "bincode", + "borsh", + "bytemuck", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cb31fe143aedb36fc41409ea072aa0b840cbea727e62eb2ff6e7b6cea036ff" +dependencies = [ + "anyhow", + "bs58 0.3.1", + "heck", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.9.9", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "blake3" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "commission-history-vote-state" +version = "0.1.2" +dependencies = [ + "anchor-lang", + "bincode", + "serde", + "solana-program", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "serde", + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "rayon", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" + +[[package]] +name = "ryu" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a16be4fe5320ade08736447e3198294a5ea9a6d44dde6f35f0a5e06859c427a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "serde_json" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "solana-frozen-abi" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a100b7fa8198c20354eb7256c0d9789107d8a62280221f3efe15f7c9dc4cec" +dependencies = [ + "ahash", + "blake3", + "block-buffer 0.9.0", + "bs58 0.4.0", + "bv", + "byteorder", + "cc", + "either", + "generic-array", + "getrandom 0.1.16", + "hashbrown 0.12.3", + "im", + "lazy_static", + "log", + "memmap2", + "once_cell", + "rand_core 0.6.4", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.7", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f527f44601b35dd67d11bc72f2f7512976a466f9304ef574b87dac83ced8a42" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "solana-program" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ad5f48743ce505f6139a07e20aecdc689def12da7230fed661c2073ab97df8" +dependencies = [ + "base64 0.13.1", + "bincode", + "bitflags", + "blake3", + "borsh", + "borsh-derive", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.10", + "itertools", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1", + "log", + "memoffset 0.6.5", + "num-derive", + "num-traits", + "parking_lot", + "rand", + "rand_chacha", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.7", + "sha3", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bbc3ab3070c090e1a18fd5a0a07d729d0db2bc8524414dc3e16504286d38049" +dependencies = [ + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2", + "rand", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.25", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] diff --git a/utils/vote-state/Cargo.toml b/utils/vote-state/Cargo.toml new file mode 100644 index 00000000..1b64a15f --- /dev/null +++ b/utils/vote-state/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "validator-history-vote-state" +version = "0.1.2" +edition = "2021" +description = "VoteState code copied from Solana." +license = "Apache-2.0" +authors = ["Jito Foundation "] + +[dependencies] +anchor-lang = "0.28.0" +bincode = "1.3.3" +serde = { version = "1.0.138", features = ["derive"] } +solana-program = "1.16" diff --git a/utils/vote-state/src/lib.rs b/utils/vote-state/src/lib.rs new file mode 100644 index 00000000..0a751b0b --- /dev/null +++ b/utils/vote-state/src/lib.rs @@ -0,0 +1,585 @@ +//! This code was mostly copy-pasta'd from [here](https://github.com/solana-labs/solana/blob/df128573127c324cb5b53634a7e2d77427c6f2d8/programs/vote/src/vote_state/mod.rs#L1). +//! In all current releases [VoteState] is defined in the `solana-vote-program` crate which is not compatible +//! with programs targeting BPF bytecode due to some BPF-incompatible libraries being pulled in. +//! Additional methods added here for deserializing specific fields to get around runtime compute limits. + +use std::{ + collections::{BTreeMap, VecDeque}, + mem::size_of, +}; + +use anchor_lang::{error::ErrorCode::ConstraintOwner, prelude::*, solana_program::vote}; + +use serde::{Deserialize, Serialize}; + +#[error_code] +pub enum ErrorCode { + #[msg("Serialized vote account data contents are malformed or not supported.")] + VoteAccountDataNotValid, +} + +type Epoch = u64; +type Slot = u64; +type UnixTimestamp = i64; + +// Maximum number of votes to keep around, tightly coupled with epoch_schedule::MINIMUM_SLOTS_PER_EPOCH +pub const MAX_LOCKOUT_HISTORY: usize = 31; +pub const INITIAL_LOCKOUT: usize = 2; + +#[derive(Clone, Serialize, Deserialize, Default, Debug, PartialEq, Eq)] +pub struct Lockout { + pub slot: Slot, + pub confirmation_count: u32, +} + +#[derive(Default, Serialize, Deserialize)] +struct AuthorizedVoters { + authorized_voters: BTreeMap, +} + +const MAX_ITEMS: usize = 32; + +#[derive(Default, Serialize, Deserialize)] +pub struct CircBuf { + buf: [I; MAX_ITEMS], + /// next pointer + idx: usize, + is_empty: bool, +} + +#[derive(Clone, Serialize, Deserialize, Default)] +pub struct BlockTimestamp { + pub slot: Slot, + pub timestamp: UnixTimestamp, +} + +#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct LandedVote { + // Latency is the difference in slot number between the slot that was voted on (lockout.slot) and the slot in + // which the vote that added this Lockout landed. For votes which were cast before versions of the validator + // software which recorded vote latencies, latency is recorded as 0. + pub latency: u8, + pub lockout: Lockout, +} + +#[derive(Serialize)] +pub enum VoteStateVersions { + V0_23_5(Box), + V1_14_11(Box), + Current(Box), +} + +#[derive(Serialize)] +pub struct VoteState0_23_5 { + /// the node that votes in this account + pub node_pubkey: Pubkey, + + /// the signer for vote transactions + pub authorized_voter: Pubkey, + /// when the authorized voter was set/initialized + pub authorized_voter_epoch: Epoch, + + /// history of prior authorized voters and the epoch ranges for which + /// they were set + pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch, Slot)>, + + /// the signer for withdrawals + pub authorized_withdrawer: Pubkey, + /// percentage (0-100) that represents what part of a rewards + /// payout should be given to this VoteAccount + pub commission: u8, + + pub votes: VecDeque, + pub root_slot: Option, + + /// history of how many credits earned by the end of each epoch + /// each tuple is (Epoch, credits, prev_credits) + pub epoch_credits: Vec<(Epoch, u64, u64)>, + + /// most recent timestamp submitted with a vote + pub last_timestamp: BlockTimestamp, +} + +#[derive(Serialize)] +pub struct VoteState1_14_11 { + /// the node that votes in this account + pub node_pubkey: Pubkey, + + /// the signer for withdrawals + pub authorized_withdrawer: Pubkey, + /// percentage (0-100) that represents what part of a rewards + /// payout should be given to this VoteAccount + pub commission: u8, + + pub votes: VecDeque, + + /// This usually the last Lockout which was popped from self.votes. + /// However, it can be arbitrary slot, when being used inside Tower + pub root_slot: Option, + + /// the signer for vote transactions + authorized_voters: AuthorizedVoters, + + /// history of prior authorized voters and the epochs for which + /// they were set, the bottom end of the range is inclusive, + /// the top of the range is exclusive + prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>, + + /// history of how many credits earned by the end of each epoch + /// each tuple is (Epoch, credits, prev_credits) + pub(crate) epoch_credits: Vec<(Epoch, u64, u64)>, + + /// most recent timestamp submitted with a vote + pub last_timestamp: BlockTimestamp, +} + +// Newest version as of 1.16.0 +#[derive(Serialize)] +pub struct VoteState { + /// the node that votes in this account + pub node_pubkey: Pubkey, + + /// the signer for withdrawals + pub authorized_withdrawer: Pubkey, + /// percentage (0-100) that represents what part of a rewards + /// payout should be given to this VoteAccount + pub commission: u8, + + pub votes: VecDeque, + + // This usually the last Lockout which was popped from self.votes. + // However, it can be arbitrary slot, when being used inside Tower + pub root_slot: Option, + + /// the signer for vote transactions + authorized_voters: AuthorizedVoters, + + /// history of prior authorized voters and the epochs for which + /// they were set, the bottom end of the range is inclusive, + /// the top of the range is exclusive + prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>, + + /// history of how many credits earned by the end of each epoch + /// each tuple is (Epoch, credits, prev_credits) + pub epoch_credits: Vec<(Epoch, u64, u64)>, + + /// most recent timestamp submitted with a vote + pub last_timestamp: BlockTimestamp, +} + +impl VoteStateVersions { + // Enum index + Pubkey + Pubkey + const VOTE_STATE_COMMISSION_INDEX: usize = 68; + const VOTE_STATE_1_14_1_COMMISSION_INDEX: usize = 68; + // Enum index + Pubkey + Pubkey + Epoch + (CircBuf: 32 * (Pubkey + 2 * Epoch + Slot) + usize + bool) + Pubkey + const VOTE_STATE_0_23_5_COMMISSION_INDEX: usize = 1909; + const COLLECTION_LEN_BYTES: usize = 8; + const ENUM_LEN_BYTES: usize = 4; + const SLOT_BYTES: usize = 8; + const EPOCH_BYTES: usize = 8; + const PUBKEY_BYTES: usize = 32; + + /* + VoteState account is too large to fully deserialize, and can't be zero-copied due to + not implementing Zeroable, so this method manually extracts the field from the bincode-serialized data + */ + pub fn deserialize_commission(account_info: &AccountInfo) -> Result { + if account_info.owner != &vote::program::ID.key() { + return Err(ConstraintOwner.into()); + } + + let data = account_info.data.borrow(); + + let enum_index = Self::enum_value_at_index(&data, 0)?; + match enum_index { + 0 => { + if data.len() < Self::VOTE_STATE_0_23_5_COMMISSION_INDEX { + return Err(ErrorCode::VoteAccountDataNotValid.into()); + } + bincode::deserialize::(&data[Self::VOTE_STATE_0_23_5_COMMISSION_INDEX..]) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } + 1 => { + if data.len() < Self::VOTE_STATE_1_14_1_COMMISSION_INDEX { + return Err(ErrorCode::VoteAccountDataNotValid.into()); + } + bincode::deserialize::(&data[Self::VOTE_STATE_COMMISSION_INDEX..]) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } + 2 => { + if data.len() < Self::VOTE_STATE_COMMISSION_INDEX { + return Err(ErrorCode::VoteAccountDataNotValid.into()); + } + bincode::deserialize::(&data[Self::VOTE_STATE_COMMISSION_INDEX..]) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } + _ => Err(ErrorCode::VoteAccountDataNotValid.into()), + } + } + + pub fn deserialize_epoch_credits(account_info: &AccountInfo) -> Result> { + /* + VoteState is too large to fully deserialize given the compute budget, so we must manually parse the serialized bincode. + This function navigates to the proper index, because the existence of several variable-size collections and Options + before the target field means epoch credits are not at a predefined address. + + Bincode reference: https://github.com/bincode-org/bincode/blob/trunk/docs/spec.md + + Serialized with FixintEncoding. + Byte size reference: + bool: 1 + u8: 1 + u32: 4 + u64: 8 + usize: 8 + */ + if account_info.owner != &vote::program::ID.key() { + return Err(ConstraintOwner.into()); + } + + let data = account_info.data.borrow(); + let enum_index = Self::enum_value_at_index(&data, 0)?; + match enum_index { + // VoteState::0_23_5 + 0 => { + let prior_voters_idx: usize = + Self::ENUM_LEN_BYTES + 2 * Self::PUBKEY_BYTES + Self::EPOCH_BYTES; + let prior_voters_size = MAX_ITEMS + * (Self::PUBKEY_BYTES + 2 * Self::EPOCH_BYTES + Self::SLOT_BYTES) + + 8 + + 1; + + let votes_idx = prior_voters_idx + prior_voters_size + Self::PUBKEY_BYTES + 1; + let votes_len = Self::collection_length_at_index(&data, votes_idx)?; + + let root_slot_idx = + votes_idx + Self::COLLECTION_LEN_BYTES + (votes_len * (Self::SLOT_BYTES + 4)); + + let root_slot_option_variant: u8 = data[root_slot_idx]; + let epoch_credits_idx = match root_slot_option_variant { + 0 => root_slot_idx + 1, + 1 => root_slot_idx + 1 + 8, + _ => { + return Err(ErrorCode::VoteAccountDataNotValid.into()); + } + }; + + return Self::deserialize_epoch_credits_at_index(&data, epoch_credits_idx); + } + // VoteState::Current + 1 => { + let votes_idx: usize = Self::ENUM_LEN_BYTES + 2 * Self::PUBKEY_BYTES + 1; + let votes_len = Self::collection_length_at_index(&data, votes_idx)?; + + let root_slot_idx = + votes_idx + Self::COLLECTION_LEN_BYTES + (votes_len * (Self::SLOT_BYTES + 4)); + let root_slot_option_variant: u8 = data[root_slot_idx]; + + let authorized_voters_idx = match root_slot_option_variant { + 0 => root_slot_idx + 1, + 1 => root_slot_idx + 1 + Self::SLOT_BYTES, + _ => { + return Err(ErrorCode::VoteAccountDataNotValid.into()); + } + }; + let authorized_voters_len = + Self::collection_length_at_index(&data, authorized_voters_idx)?; + + let prior_voters_len = + MAX_ITEMS * (Self::PUBKEY_BYTES + 2 * Self::EPOCH_BYTES) + 8 + 1; + + let epoch_credits_idx: usize = authorized_voters_idx + + Self::COLLECTION_LEN_BYTES + + authorized_voters_len * (Self::EPOCH_BYTES + Self::PUBKEY_BYTES) + + prior_voters_len; + + return Self::deserialize_epoch_credits_at_index(&data, epoch_credits_idx); + } + 2 => { + let votes_idx: usize = Self::ENUM_LEN_BYTES + 2 * Self::PUBKEY_BYTES + 1; + let votes_len = Self::collection_length_at_index(&data, votes_idx)?; + + let root_slot_idx = votes_idx + + Self::COLLECTION_LEN_BYTES + + (votes_len * (1 + Self::SLOT_BYTES + 4)); + let root_slot_option_variant: u8 = data[root_slot_idx]; + + let authorized_voters_idx = match root_slot_option_variant { + 0 => root_slot_idx + 1, + 1 => root_slot_idx + 1 + Self::SLOT_BYTES, + _ => { + return Err(ErrorCode::VoteAccountDataNotValid.into()); + } + }; + let authorized_voters_len = + Self::collection_length_at_index(&data, authorized_voters_idx)?; + + let prior_voters_len = + MAX_ITEMS * (Self::PUBKEY_BYTES + 2 * Self::EPOCH_BYTES) + 8 + 1; + + let epoch_credits_idx: usize = authorized_voters_idx + + Self::COLLECTION_LEN_BYTES + + authorized_voters_len * (Self::EPOCH_BYTES + Self::PUBKEY_BYTES) + + prior_voters_len; + + return Self::deserialize_epoch_credits_at_index(&data, epoch_credits_idx); + } + _ => {} + } + + Ok(vec![]) + } + + pub fn deserialize_node_pubkey(account_info: &AccountInfo) -> Result { + if account_info.owner != &vote::program::ID.key() { + return Err(ConstraintOwner.into()); + } + + let data = account_info.data.borrow(); + let node_pubkey_idx = Self::ENUM_LEN_BYTES; + let node_pubkey_bytes = &data[node_pubkey_idx..node_pubkey_idx + Self::PUBKEY_BYTES]; + bincode::deserialize(node_pubkey_bytes) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } + + fn collection_length_at_index(bincode_data: &[u8], index: usize) -> Result { + bincode::deserialize::(&bincode_data[index..index + Self::COLLECTION_LEN_BYTES]) + .map(|x| x as usize) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } + + fn enum_value_at_index(bincode_data: &[u8], index: usize) -> Result { + bincode::deserialize::(&bincode_data[index..index + Self::ENUM_LEN_BYTES]) + .map(|x| x as usize) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } + + fn deserialize_epoch_credits_at_index( + bincode_data: &[u8], + epoch_credits_idx: usize, + ) -> Result> { + let epoch_credits_len = Self::collection_length_at_index(bincode_data, epoch_credits_idx)?; + let epoch_credits_size = epoch_credits_len * size_of::<(Epoch, u64, u64)>(); + + let epoch_credits_bytes = &bincode_data[(epoch_credits_idx) + ..(epoch_credits_idx + Self::COLLECTION_LEN_BYTES + epoch_credits_size)]; + + bincode::deserialize(epoch_credits_bytes) + .map_err(|_| ErrorCode::VoteAccountDataNotValid.into()) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + AuthorizedVoters, BlockTimestamp, CircBuf, Lockout, VoteState0_23_5, VoteStateVersions, + MAX_LOCKOUT_HISTORY, + }; + use anchor_lang::{ + prelude::{AccountInfo, Pubkey}, + solana_program::{clock::Epoch, vote}, + Key, + }; + use std::collections::VecDeque; + + #[test] + fn test_deserialize_epoch_credits() { + let test_epoch_credits: Vec<(Epoch, u64, u64)> = vec![(1, 2, 3), (6, 4, 5)]; + let test_votes = VecDeque::from(vec![Lockout::default(); MAX_LOCKOUT_HISTORY]); + let mut authorized_voters = AuthorizedVoters::default(); + let my_pubkey = Pubkey::new_unique(); + authorized_voters.authorized_voters.insert(99, my_pubkey); + // Test Current + // None + let vote_state = VoteStateVersions::V1_14_11(Box::new(crate::VoteState1_14_11 { + node_pubkey: Pubkey::new_unique(), + authorized_withdrawer: Pubkey::new_unique(), + commission: 96, + votes: test_votes, + root_slot: None, + authorized_voters, + prior_voters: CircBuf::default(), + epoch_credits: test_epoch_credits, + last_timestamp: BlockTimestamp { + slot: 1, + timestamp: 2, + }, + })); + + let mut encoded = bincode::serialize(&vote_state).unwrap(); + + let mut lamports: u64 = 0; + let key = Pubkey::new_unique(); + let owner = vote::program::ID.key(); + + let account_info = AccountInfo::new( + &key, + false, + false, + &mut lamports, + encoded.as_mut_slice(), + &owner, + false, + 0, + ); + let epoch_credits_result = + VoteStateVersions::deserialize_epoch_credits(&account_info).unwrap(); + assert!(epoch_credits_result == vec![(1, 2, 3), (6, 4, 5)]); + + // Test V0235 + let test_epoch_credits: Vec<(Epoch, u64, u64)> = vec![(70, 6, 9), (321, 4, 20)]; + let vote_state_0_23_5 = VoteStateVersions::V0_23_5(Box::new(VoteState0_23_5 { + node_pubkey: Pubkey::new_unique(), + authorized_voter: Pubkey::new_unique(), + authorized_voter_epoch: 0, + prior_voters: CircBuf::default(), + authorized_withdrawer: Pubkey::new_unique(), + commission: 69, + votes: VecDeque::new(), + root_slot: None, + epoch_credits: test_epoch_credits, + last_timestamp: BlockTimestamp::default(), + })); + let mut ser_0_23_5 = bincode::serialize(&vote_state_0_23_5).unwrap(); + let account_info = AccountInfo::new( + &key, + false, + false, + &mut lamports, + ser_0_23_5.as_mut_slice(), + &owner, + false, + 0, + ); + let epoch_credits_result = + VoteStateVersions::deserialize_epoch_credits(&account_info).unwrap(); + assert!(epoch_credits_result == vec![(70, 6, 9), (321, 4, 20)]); + + // Test Current + let test_epoch_credits: Vec<(Epoch, u64, u64)> = vec![(70, 9, 6), (321, 20, 4)]; + let vote_state_current = VoteStateVersions::Current(Box::new(crate::VoteState { + node_pubkey: Pubkey::new_unique(), + authorized_withdrawer: Pubkey::new_unique(), + commission: 99, + votes: VecDeque::new(), + root_slot: None, + authorized_voters: AuthorizedVoters::default(), + prior_voters: CircBuf::default(), + epoch_credits: test_epoch_credits, + last_timestamp: BlockTimestamp::default(), + })); + let mut ser_current = bincode::serialize(&vote_state_current).unwrap(); + let account_info = AccountInfo::new( + &key, + false, + false, + &mut lamports, + ser_current.as_mut_slice(), + &owner, + false, + 0, + ); + let epoch_credits_result = + VoteStateVersions::deserialize_epoch_credits(&account_info).unwrap(); + assert!(epoch_credits_result == vec![(70, 9, 6), (321, 20, 4)]); + } + + #[test] + fn test_deserialize_commission() { + let vote_state = VoteStateVersions::V1_14_11(Box::new(crate::VoteState1_14_11 { + node_pubkey: Pubkey::new_unique(), + authorized_withdrawer: Pubkey::new_unique(), + commission: 96, + votes: VecDeque::new(), + root_slot: None, + authorized_voters: AuthorizedVoters::default(), + prior_voters: CircBuf::default(), + epoch_credits: Vec::new(), + last_timestamp: BlockTimestamp::default(), + })); + + let mut ser = bincode::serialize(&vote_state).unwrap(); + + let mut lamports: u64 = 0; + let key = Pubkey::new_unique(); + let owner = vote::program::ID.key(); + + let account = AccountInfo::new( + &key, + false, + false, + &mut lamports, + ser.as_mut_slice(), + &owner, + false, + 0, + ); + + assert_eq!( + VoteStateVersions::deserialize_commission(&account).unwrap(), + 96 + ); + + let vote_state_0_23_5 = VoteStateVersions::V0_23_5(Box::new(VoteState0_23_5 { + node_pubkey: Pubkey::new_unique(), + authorized_voter: Pubkey::new_unique(), + authorized_voter_epoch: 0, + prior_voters: CircBuf::default(), + authorized_withdrawer: Pubkey::new_unique(), + commission: 69, + votes: VecDeque::new(), + root_slot: None, + epoch_credits: Vec::new(), + last_timestamp: BlockTimestamp::default(), + })); + + let mut ser_0_23_5 = bincode::serialize(&vote_state_0_23_5).unwrap(); + + let mut lamports: u64 = 0; + let key = Pubkey::new_unique(); + let owner = vote::program::ID.key(); + + let account_0_23_5 = AccountInfo::new( + &key, + false, + false, + &mut lamports, + ser_0_23_5.as_mut_slice(), + &owner, + false, + 0, + ); + + assert_eq!( + VoteStateVersions::deserialize_commission(&account_0_23_5).unwrap(), + 69 + ); + + let vote_state_current = VoteStateVersions::Current(Box::new(crate::VoteState { + node_pubkey: Pubkey::new_unique(), + authorized_withdrawer: Pubkey::new_unique(), + commission: 99, + votes: VecDeque::new(), + root_slot: None, + authorized_voters: AuthorizedVoters::default(), + prior_voters: CircBuf::default(), + epoch_credits: Vec::new(), + last_timestamp: BlockTimestamp::default(), + })); + let mut ser_current = bincode::serialize(&vote_state_current).unwrap(); + let account_current = AccountInfo::new( + &key, + false, + false, + &mut lamports, + ser_current.as_mut_slice(), + &owner, + false, + 0, + ); + assert_eq!( + VoteStateVersions::deserialize_commission(&account_current).unwrap(), + 99 + ); + } +}