diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..2f7896d1d1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +target/ diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index c2fc36a3e6..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,41 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: -- package-ecosystem: cargo - directory: "/" - schedule: - interval: daily - time: "01:00" - timezone: America/Los_Angeles - #labels: - # - "automerge" - open-pull-requests-limit: 3 - -- package-ecosystem: npm - directory: "/web3.js" - schedule: - interval: daily - time: "01:00" - timezone: America/Los_Angeles - labels: - - "automerge" - commit-message: - prefix: "chore:" - open-pull-requests-limit: 3 - -- package-ecosystem: npm - directory: "/explorer" - schedule: - interval: daily - time: "01:00" - timezone: America/Los_Angeles - labels: - - "automerge" - commit-message: - prefix: "chore:" - include: "scope" - open-pull-requests-limit: 3 diff --git a/.github/workflows/client-targets.yml b/.github/workflows/client-targets.yml index e7b78d83e9..12578422b4 100644 --- a/.github/workflows/client-targets.yml +++ b/.github/workflows/client-targets.yml @@ -45,8 +45,10 @@ jobs: platform: android os: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + submodules: recursive + ssh-key: ${{ secrets.DEPLOYER_SSH_KEY }} - uses: actions-rs/toolchain@v1 with: toolchain: stable diff --git a/.gitignore b/.gitignore index e2bf6fbb7a..9b7f0cdb54 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ log-*/ .DS_Store # scripts that may be generated by cargo *-bpf commands **/cargo-*-bpf-child-script-*.sh + +.env +docker-output/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..5ee418f4a8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "anchor"] + path = anchor + url = org-87542516@github.com:jito-labs/anchor.git +[submodule "jito-programs"] + path = jito-programs + url = org-87542516@github.com:jito-labs/jito-programs.git +[submodule "jito-protos/protos"] + path = jito-protos/protos + url = org-87542516@github.com:jito-labs/mev-protos-priv.git diff --git a/Cargo.lock b/Cargo.lock index 4864e616ef..18f8d056c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] @@ -36,7 +36,7 @@ dependencies = [ "cfg-if 1.0.0", "cipher 0.3.0", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -60,16 +60,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.7", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -82,48 +82,187 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "alloc-no-stdlib" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "anchor-attribute-access-control" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "regex", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.4.0", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustversion", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.46", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-interface" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "heck 0.3.3", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-state" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-lang" +version = "0.24.2" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-interface", + "anchor-attribute-program", + "anchor-attribute-state", + "anchor-derive-accounts", + "arrayref", + "base64 0.13.0", + "bincode", + "borsh", + "bytemuck", + "solana-program 1.15.0", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.24.2" +dependencies = [ + "anyhow", + "bs58 0.3.1", + "heck 0.3.3", + "proc-macro2 1.0.46", + "proc-macro2-diagnostics", + "quote 1.0.21", + "serde", + "serde_json", + "sha2 0.9.9", + "syn 1.0.102", + "thiserror", +] + [[package]] name = "android_system_properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi 0.3.9", ] [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arc-swap" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" [[package]] name = "arrayref" @@ -156,7 +295,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.9", + "time 0.3.15", ] [[package]] @@ -165,9 +304,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "synstructure", ] @@ -177,9 +316,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -204,9 +343,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "brotli", "flate2", @@ -227,9 +366,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", @@ -237,13 +376,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -252,9 +391,9 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -285,9 +424,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.5.1" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47594e438a243791dba58124b6669561f5baa14cb12046641d8008bf035e5a25" +checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" dependencies = [ "async-trait", "axum-core", @@ -297,11 +436,11 @@ dependencies = [ "http", "http-body", "hyper", - "itoa 1.0.1", + "itoa 1.0.4", "matchit", "memchr", "mime", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project-lite", "serde", "sync_wrapper", @@ -335,7 +474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.3", + "getrandom 0.2.7", "instant", "pin-project-lite", "rand 0.8.5", @@ -356,9 +495,32 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.3.3" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" + +[[package]] +name = "bench-get-confirmed-blocks-with-data" +version = "1.15.0" +dependencies = [ + "env_logger", + "log", + "solana-sdk 1.15.0", + "solana-storage-bigtable", + "solana-transaction-status", + "tokio", +] + +[[package]] +name = "bigdecimal" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874f8444adcb4952a8bc51305c8be95c8ec8237bb0d2e78d2e039f771f8828a0" +checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" +dependencies = [ + "num-bigint 0.4.3", + "num-integer", + "num-traits", +] [[package]] name = "bincode" @@ -381,8 +543,8 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "regex", "rustc-hash", "shlex", @@ -390,9 +552,9 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] @@ -429,19 +591,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "constant_time_eq", - "digest 0.10.3", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder", - "generic-array 0.12.4", + "digest 0.10.5", ] [[package]] @@ -450,26 +600,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding 0.2.1", - "generic-array 0.14.6", + "block-padding", + "generic-array", ] [[package]] name = "block-buffer" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "block-padding" -version = "0.1.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "byte-tools", + "generic-array", ] [[package]] @@ -497,8 +638,8 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.46", + "syn 1.0.102", ] [[package]] @@ -507,9 +648,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -518,9 +659,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -544,6 +685,12 @@ dependencies = [ "alloc-stdlib", ] +[[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" @@ -564,9 +711,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.8.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bv" @@ -578,12 +725,6 @@ dependencies = [ "serde", ] -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "byte-unit" version = "4.0.14" @@ -601,22 +742,22 @@ checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "bytemuck" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" +checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -660,9 +801,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.0.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" +checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" dependencies = [ "serde", ] @@ -694,7 +835,7 @@ checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" dependencies = [ "camino", "cargo-platform", - "semver 1.0.14", + "semver", "serde", "serde_json", ] @@ -705,7 +846,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" dependencies = [ - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -749,16 +890,16 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time 0.1.43", + "time 0.1.44", "wasm-bindgen", "winapi 0.3.9", ] [[package]] name = "chrono-humanize" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb" +checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e" dependencies = [ "chrono", ] @@ -769,7 +910,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] @@ -784,9 +925,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.2.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10612c0ec0e0a1ff0e97980647cb058a6e7aedb913d01d009c406b8b7d0b26ee" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -795,9 +936,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -810,32 +951,51 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.8" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.15.1", ] [[package]] name = "clap_derive" -version = "3.1.7" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[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 = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", ] [[package]] @@ -853,14 +1013,13 @@ dependencies = [ [[package]] name = "console" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "regex", "terminal_size", "unicode-width", "winapi 0.3.9", @@ -894,22 +1053,22 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] name = "const_format" -version = "0.2.26" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495" +checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.22" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" +checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "unicode-xid 0.2.2", + "proc-macro2 1.0.46", + "quote 1.0.21", + "unicode-xid 0.2.4", ] [[package]] @@ -926,9 +1085,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -954,18 +1113,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] @@ -995,9 +1154,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -1018,12 +1177,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", - "lazy_static", ] [[package]] @@ -1034,11 +1192,11 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array", "typenum", ] @@ -1048,7 +1206,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array", "subtle", ] @@ -1107,6 +1265,50 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cxx" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2 1.0.46", + "quote 1.0.21", + "scratch", + "syn 1.0.102", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + [[package]] name = "dashmap" version = "4.0.2" @@ -1120,13 +1322,15 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.2.0" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if 1.0.0", - "num_cpus", - "parking_lot 0.12.1", + "hashbrown 0.12.3", + "lock_api", + "once_cell", + "parking_lot_core 0.9.3", ] [[package]] @@ -1166,15 +1370,15 @@ checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" [[package]] name = "derive_more" -version = "0.99.16" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.41", - "quote 1.0.18", - "rustc_version 0.3.3", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustc_version", + "syn 1.0.102", ] [[package]] @@ -1194,31 +1398,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -1259,9 +1454,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -1301,9 +1496,9 @@ checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" [[package]] name = "ed25519" -version = "1.2.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ "signature", ] @@ -1331,26 +1526,26 @@ dependencies = [ "derivation-path", "ed25519-dalek", "hmac 0.12.1", - "sha2 0.10.5", + "sha2 0.10.6", ] [[package]] name = "educe" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" +checksum = "c07b7cc9cd8c08d10db74fca3b20949b9b6199725c04a0cce6d543496098fcac" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encode_unicode" @@ -1360,9 +1555,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.29" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if 1.0.0", ] @@ -1382,22 +1577,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "enum-ordinalize" -version = "3.1.10" +version = "3.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" +checksum = "2170fc0efee383079a8bdd05d6ea2a184d2a0f07a1c1dcabdb2fd5e9f24bc36c" dependencies = [ "num-bigint 0.4.3", "num-traits", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustc_version", + "syn 1.0.102", ] [[package]] @@ -1407,9 +1603,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" dependencies = [ "once_cell", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -1463,15 +1659,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" - -[[package]] -name = "fake-simd" -version = "0.1.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fast-math" @@ -1484,9 +1674,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -1499,7 +1689,7 @@ checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517" dependencies = [ "cfg-if 1.0.0", "rustix", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -1510,9 +1700,9 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" [[package]] name = "filedescriptor" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed3d8a5e20435ff00469e51a0d82049bae66504b5c429920dadf9bb54d47b3f" +checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" dependencies = [ "libc", "thiserror", @@ -1521,21 +1711,27 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "winapi 0.3.9", + "windows-sys", ] [[package]] name = "fixedbitset" -version = "0.4.0" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" @@ -1579,12 +1775,11 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", ] [[package]] @@ -1660,9 +1855,9 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -1730,15 +1925,6 @@ dependencies = [ "regex", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.6" @@ -1775,14 +1961,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "js-sys", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1794,9 +1980,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" dependencies = [ "aho-corasick", "bstr", @@ -1820,15 +2006,15 @@ dependencies = [ "serde_json", "simpl", "smpl_jwt", - "time 0.3.9", + "time 0.3.15", "tokio", ] [[package]] name = "goblin" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c955ab4e0ad8c843ea653a3d143048b87490d9be56bd7132a435c2407846ac8f" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" dependencies = [ "log", "plain", @@ -1837,9 +2023,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ "bytes", "fnv", @@ -1850,7 +2036,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.7.2", "tracing", ] @@ -1883,9 +2069,9 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.0", "bitflags", @@ -1894,7 +2080,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha-1 0.10.0", + "sha1", ] [[package]] @@ -1938,9 +2124,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hidapi" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b1717343691998deb81766bfcd1dce6df0d5d6c37070b5a3de2bb6d39f7822" +checksum = "9d26e1151deaab68f34fbfd16d491a2a0170cf98d69d3efa23873b567a4199e1" dependencies = [ "cc", "libc", @@ -1969,7 +2155,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -1979,7 +2165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array", "hmac 0.8.1", ] @@ -1991,7 +2177,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.1", + "itoa 1.0.4", ] [[package]] @@ -2013,15 +2199,15 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" @@ -2044,7 +2230,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.1", + "itoa 1.0.4", "pin-project-lite", "socket2", "tokio", @@ -2081,7 +2267,7 @@ dependencies = [ "hyper", "rustls 0.20.6", "tokio", - "tokio-rustls 0.23.3", + "tokio-rustls 0.23.4", ] [[package]] @@ -2111,17 +2297,28 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.46" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi 0.3.9", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "idna" version = "0.1.5" @@ -2135,11 +2332,10 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -2157,7 +2353,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core 0.6.3", + "rand_core 0.6.4", "rand_xoshiro", "rayon", "serde", @@ -2196,11 +2392,11 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1f03d4ab4d5dc9ec2d219f86c15d2a15fc08239d1cd3b2d6a19717c0a2f443" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] @@ -2220,9 +2416,9 @@ checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" [[package]] name = "ipnet" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" @@ -2241,24 +2437,35 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "jito-protos" +version = "1.15.0" +dependencies = [ + "bytes", + "prost 0.8.0", + "prost-types 0.8.0", + "tonic 0.5.2", + "tonic-build 0.5.2", +] [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -2325,9 +2532,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -2390,15 +2597,15 @@ dependencies = [ "log", "tokio", "tokio-stream", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "unicase", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "kernel32-sys" @@ -2424,9 +2631,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.131" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libloading" @@ -2440,9 +2647,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" [[package]] name = "librocksdb-sys" @@ -2508,20 +2715,29 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.3" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" dependencies = [ "cc", "pkg-config", "vcpkg", ] +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" @@ -2531,10 +2747,11 @@ checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] @@ -2576,12 +2793,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "matches" version = "0.1.9" @@ -2596,24 +2807,24 @@ checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] @@ -2626,7 +2837,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", "keccak", - "rand_core 0.6.3", + "rand_core 0.6.4", "zeroize", ] @@ -2644,15 +2855,15 @@ checksum = "2687e6cf9c00f48e9284cf9fd15f2ef341d03cc7743abf9df4c5f07fdee50b18" [[package]] name = "minimal-lexical" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", ] @@ -2695,9 +2906,9 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -2708,9 +2919,9 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "native-tls" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" dependencies = [ "lazy_static", "libc", @@ -2751,13 +2962,12 @@ dependencies = [ [[package]] name = "nom" -version = "7.0.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] @@ -2768,9 +2978,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi 0.3.9", ] @@ -2827,16 +3037,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -2899,17 +3109,17 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" dependencies = [ - "proc-macro-crate 1.1.0", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro-crate 1.2.1", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "num_threads" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] @@ -2931,15 +3141,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "opaque-debug" -version = "0.2.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -2949,9 +3153,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.41" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2968,16 +3172,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "openssl-probe" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" @@ -2990,9 +3194,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg", "cc", @@ -3015,7 +3219,7 @@ dependencies = [ "futures-util", "js-sys", "lazy_static", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project", "rand 0.8.5", "thiserror", @@ -3023,35 +3227,31 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "ouroboros" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f31a3b678685b150cba82b702dcdc5e155893f63610cf388d30cd988d4ca2bf" +checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca" dependencies = [ "aliasable", "ouroboros_macro", - "stable_deref_trait", ] [[package]] name = "ouroboros_macro" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408" +checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -3086,7 +3286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.1", + "parking_lot_core 0.9.3", ] [[package]] @@ -3105,15 +3305,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys 0.32.0", + "windows-sys", ] [[package]] @@ -3131,7 +3331,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -3157,9 +3357,9 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "percentage" @@ -3172,18 +3372,19 @@ dependencies = [ [[package]] name = "pest" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" dependencies = [ "pest", "pest_generator", @@ -3191,35 +3392,45 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1 0.8.2", + "sha1", ] [[package]] name = "petgraph" -version = "0.6.0" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset 0.2.0", + "indexmap", +] + +[[package]] +name = "petgraph" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" dependencies = [ - "fixedbitset", + "fixedbitset 0.4.2", "indexmap", ] @@ -3235,29 +3446,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -3278,9 +3489,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.22" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "plain" @@ -3296,15 +3507,15 @@ checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", "universal-hash", ] [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "predicates" @@ -3322,15 +3533,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" [[package]] name = "predicates-tree" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" dependencies = [ "predicates-core", "termtree", @@ -3344,12 +3555,12 @@ checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "prettyplease" -version = "0.1.9" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18" +checksum = "83fead41e178796ef8274dc612a7d8ce4c7e10ca35cd2c5b5ad24cac63aeb6c0" dependencies = [ - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.46", + "syn 1.0.102", ] [[package]] @@ -3363,10 +3574,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] @@ -3378,9 +3590,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "version_check", ] @@ -3390,8 +3602,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "version_check", ] @@ -3406,13 +3618,26 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.41" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", + "version_check", + "yansi", +] + [[package]] name = "proptest" version = "1.0.0" @@ -3433,6 +3658,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes", + "prost-derive 0.8.0", +] + [[package]] name = "prost" version = "0.9.0" @@ -3453,6 +3688,24 @@ dependencies = [ "prost-derive 0.11.0", ] +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools", + "log", + "multimap", + "petgraph 0.5.1", + "prost 0.8.0", + "prost-types 0.8.0", + "tempfile", + "which", +] + [[package]] name = "prost-build" version = "0.9.0" @@ -3465,7 +3718,7 @@ dependencies = [ "lazy_static", "log", "multimap", - "petgraph", + "petgraph 0.6.2", "prost 0.9.0", "prost-types 0.9.0", "regex", @@ -3475,9 +3728,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49d928704208aba2cb1fb022ce1a319bdedcb03caf51ddf82734fa903407762" +checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb" dependencies = [ "bytes", "heck 0.4.0", @@ -3485,7 +3738,7 @@ dependencies = [ "lazy_static", "log", "multimap", - "petgraph", + "petgraph 0.6.2", "prost 0.11.0", "prost-types 0.11.1", "regex", @@ -3493,6 +3746,19 @@ dependencies = [ "which", ] +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + [[package]] name = "prost-derive" version = "0.9.0" @@ -3501,9 +3767,9 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -3514,9 +3780,19 @@ checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes", + "prost 0.8.0", ] [[package]] @@ -3541,9 +3817,9 @@ dependencies = [ [[package]] name = "protobuf-src" -version = "1.0.5+3.19.3" +version = "1.1.0+21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe57f68bf9767f48f8cbcbceb5da21524e2b1330a821c1c2502c447d8043f078" +checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" dependencies = [ "autotools", ] @@ -3554,7 +3830,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", ] [[package]] @@ -3571,9 +3847,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quinn" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21afdc492bf2a8688cb386be6605d1163b6ace89afa5e3b529037d2b4334b860" +checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f" dependencies = [ "bytes", "futures-channel", @@ -3599,7 +3875,7 @@ dependencies = [ "rand 0.8.5", "ring", "rustls 0.20.6", - "rustls-native-certs", + "rustls-native-certs 0.6.2", "rustls-pemfile 0.2.1", "slab", "thiserror", @@ -3633,11 +3909,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.46", ] [[package]] @@ -3674,7 +3950,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3694,7 +3970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3723,11 +3999,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.7", ] [[package]] @@ -3745,7 +4021,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3754,7 +4030,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3777,9 +4053,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -3791,7 +4067,7 @@ dependencies = [ name = "rbpf-cli" version = "1.15.0" dependencies = [ - "clap 3.1.8", + "clap 3.2.22", "serde", "serde_json", "solana-bpf-loader-program", @@ -3803,13 +4079,13 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fa2d386df8533b02184941c76ae2e0d0c1d053f5d43339169d80f21275fc5e" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.9", + "time 0.3.15", "yasna", ] @@ -3824,21 +4100,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.7", "redox_syscall", + "thiserror", ] [[package]] @@ -3853,7 +4130,7 @@ dependencies = [ "lru", "parking_lot 0.11.2", "smallvec", - "spin 0.9.2", + "spin 0.9.4", ] [[package]] @@ -3912,23 +4189,23 @@ dependencies = [ "mime", "native-tls", "once_cell", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project-lite", "rustls 0.20.6", - "rustls-pemfile 1.0.0", + "rustls-pemfile 1.0.1", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.3", - "tokio-util 0.7.1", + "tokio-rustls 0.23.4", + "tokio-util 0.7.2", "tower-service", - "url 2.2.2", + "url 2.3.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.22.5", "winreg", ] @@ -3979,22 +4256,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.14", + "semver", ] [[package]] @@ -4008,16 +4276,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.35.9" +version = "0.35.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" +checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -4047,12 +4315,24 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943" +checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" dependencies = [ "openssl-probe", - "rustls-pemfile 0.2.1", + "rustls 0.19.1", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.1", "schannel", "security-framework", ] @@ -4068,9 +4348,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ "base64 0.13.0", ] @@ -4095,9 +4375,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "same-file" @@ -4110,12 +4390,12 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi 0.3.9", + "windows-sys", ] [[package]] @@ -4124,6 +4404,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "scroll" version = "0.11.0" @@ -4139,9 +4425,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -4166,9 +4452,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.4.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -4179,23 +4465,14 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.4.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.14" @@ -4205,20 +4482,11 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] @@ -4234,22 +4502,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.4", "ryu", "serde", ] @@ -4261,7 +4529,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.1", + "itoa 1.0.4", "ryu", "serde", ] @@ -4284,7 +4552,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" dependencies = [ - "dashmap 5.2.0", + "dashmap 5.4.0", "futures 0.3.24", "lazy_static", "log", @@ -4299,21 +4567,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", -] - -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -4326,7 +4582,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -4337,7 +4593,18 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", ] [[package]] @@ -4350,18 +4617,18 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] name = "sha2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -4373,16 +4640,16 @@ dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", "keccak", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] name = "sha3" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaedf34ed289ea47c2b741bb72e5357a209512d67bcd4bda44359e5bf0470f56" +checksum = "e2904bea16a1ae962b483322a1c7b81d976029203aea1f461e51cd7705db7ba9" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", "keccak", ] @@ -4422,9 +4689,9 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] name = "simpl" @@ -4444,15 +4711,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smpl_jwt" @@ -4467,14 +4737,14 @@ dependencies = [ "serde_derive", "serde_json", "simpl", - "time 0.3.9", + "time 0.3.15", ] [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi 0.3.9", @@ -4502,7 +4772,7 @@ dependencies = [ "Inflector", "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", "bv", "lazy_static", "serde", @@ -4521,7 +4791,7 @@ dependencies = [ name = "solana-accounts-bench" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "log", "rayon", "solana-logger 1.15.0", @@ -4535,7 +4805,7 @@ dependencies = [ name = "solana-accounts-cluster-bench" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "log", "rand 0.7.3", "rayon", @@ -4568,7 +4838,7 @@ dependencies = [ "log", "num-derive", "num-traits", - "rustc_version 0.4.0", + "rustc_version", "serde", "solana-frozen-abi 1.15.0", "solana-frozen-abi-macro 1.15.0", @@ -4593,7 +4863,7 @@ dependencies = [ name = "solana-banking-bench" version = "1.15.0" dependencies = [ - "clap 3.1.8", + "clap 3.2.22", "crossbeam-channel", "log", "rand 0.7.3", @@ -4647,9 +4917,11 @@ dependencies = [ "futures 0.3.24", "solana-banks-interface", "solana-client", + "solana-gossip", "solana-runtime", "solana-sdk 1.15.0", "solana-send-transaction-service", + "solana-streamer", "solana-tpu-client", "tarpc", "tokio", @@ -4657,11 +4929,26 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "solana-bench-batch-simulate-bundle" +version = "1.15.0" +dependencies = [ + "clap 3.2.22", + "env_logger", + "log", + "num-traits", + "rayon", + "solana-client", + "solana-runtime", + "solana-sdk 1.15.0", + "solana-transaction-status", +] + [[package]] name = "solana-bench-streamer" version = "1.15.0" dependencies = [ - "clap 3.1.8", + "clap 3.2.22", "crossbeam-channel", "solana-net-utils", "solana-streamer", @@ -4672,7 +4959,7 @@ dependencies = [ name = "solana-bench-tps" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "crossbeam-channel", "log", "rand 0.7.3", @@ -4715,7 +5002,7 @@ dependencies = [ "log", "rand 0.7.3", "rayon", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_derive", "solana-frozen-abi 1.15.0", @@ -4774,7 +5061,7 @@ name = "solana-cargo-build-bpf" version = "1.15.0" dependencies = [ "cargo_metadata", - "clap 3.1.8", + "clap 3.2.22", "log", "solana-logger 1.15.0", "solana-sdk 1.15.0", @@ -4787,7 +5074,7 @@ dependencies = [ "assert_cmd", "bzip2", "cargo_metadata", - "clap 3.1.8", + "clap 3.2.22", "log", "predicates", "regex", @@ -4803,7 +5090,7 @@ name = "solana-cargo-test-bpf" version = "1.15.0" dependencies = [ "cargo_metadata", - "clap 3.1.8", + "clap 3.2.22", ] [[package]] @@ -4811,7 +5098,7 @@ name = "solana-cargo-test-sbf" version = "1.15.0" dependencies = [ "cargo_metadata", - "clap 3.1.8", + "clap 3.2.22", ] [[package]] @@ -4819,7 +5106,7 @@ name = "solana-clap-utils" version = "1.15.0" dependencies = [ "chrono", - "clap 2.33.3", + "clap 2.34.0", "rpassword", "solana-perf", "solana-remote-wallet", @@ -4828,7 +5115,7 @@ dependencies = [ "thiserror", "tiny-bip39", "uriparse", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -4836,7 +5123,7 @@ name = "solana-clap-v3-utils" version = "1.15.0" dependencies = [ "chrono", - "clap 3.1.8", + "clap 3.2.22", "rpassword", "solana-perf", "solana-remote-wallet", @@ -4845,7 +5132,7 @@ dependencies = [ "thiserror", "tiny-bip39", "uriparse", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -4853,8 +5140,8 @@ name = "solana-cli" version = "1.15.0" dependencies = [ "bincode", - "bs58", - "clap 2.33.3", + "bs58 0.4.0", + "clap 2.34.0", "console", "const_format", "criterion-stats", @@ -4865,7 +5152,7 @@ dependencies = [ "num-traits", "pretty-hex", "reqwest", - "semver 1.0.14", + "semver", "serde", "serde_derive", "serde_json", @@ -4910,7 +5197,7 @@ dependencies = [ "serde_yaml", "solana-clap-utils", "solana-sdk 1.15.0", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -4920,13 +5207,13 @@ dependencies = [ "Inflector", "base64 0.13.0", "chrono", - "clap 2.33.3", + "clap 2.34.0", "console", "ed25519-dalek", "humantime", "indicatif", "pretty-hex", - "semver 1.0.14", + "semver", "serde", "serde_json", "solana-account-decoder", @@ -5008,28 +5295,37 @@ name = "solana-core" version = "1.15.0" dependencies = [ "ahash", + "anchor-lang", "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", + "bytes", "chrono", + "clap 3.2.22", "crossbeam-channel", "dashmap 4.0.2", "eager", "etcd-client", "fs_extra", + "futures 0.3.24", + "futures-util", "histogram", + "indexmap", "itertools", + "jito-protos", "lazy_static", "log", "lru", "matches", "min-max-heap", "num_enum", + "prost 0.8.0", + "prost-types 0.8.0", "rand 0.7.3", "rand_chacha 0.2.2", "raptorq", "rayon", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_derive", "serde_json", @@ -5068,8 +5364,14 @@ dependencies = [ "tempfile", "test-case", "thiserror", + "tip-distribution", + "tip-payment", "tokio", + "tokio-stream", + "tonic 0.5.2", + "tonic-build 0.5.2", "trees", + "uuid", ] [[package]] @@ -5077,7 +5379,7 @@ name = "solana-dos" version = "1.15.0" dependencies = [ "bincode", - "clap 3.1.8", + "clap 3.2.22", "crossbeam-channel", "itertools", "log", @@ -5154,7 +5456,7 @@ version = "1.15.0" dependencies = [ "bincode", "byteorder", - "clap 2.33.3", + "clap 2.34.0", "crossbeam-channel", "log", "serde", @@ -5172,19 +5474,19 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e4e35bc58c465f161bde764ebce41fdfcb503583cf3a77e0211274cc12b22d" +checksum = "3f07a997db3dac7b9da06b007d4a8df6dbd8281182e6ebbbd8a56f935f540b0a" dependencies = [ "ahash", "blake3", "block-buffer 0.9.0", - "bs58", + "bs58 0.4.0", "bv", "byteorder", "cc", "either", - "generic-array 0.14.6", + "generic-array", "getrandom 0.1.16", "hashbrown 0.12.3", "im", @@ -5192,14 +5494,14 @@ dependencies = [ "log", "memmap2", "once_cell", - "rand_core 0.6.3", - "rustc_version 0.4.0", + "rand_core 0.6.4", + "rustc_version", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "solana-frozen-abi-macro 1.11.5", + "sha2 0.10.6", + "solana-frozen-abi-macro 1.14.5", "subtle", "thiserror", ] @@ -5211,12 +5513,12 @@ dependencies = [ "ahash", "blake3", "block-buffer 0.9.0", - "bs58", + "bs58 0.4.0", "bv", "byteorder", "cc", "either", - "generic-array 0.14.6", + "generic-array", "getrandom 0.1.16", "hashbrown 0.12.3", "im", @@ -5224,13 +5526,13 @@ dependencies = [ "log", "memmap2", "once_cell", - "rand_core 0.6.3", - "rustc_version 0.4.0", + "rand_core 0.6.4", + "rustc_version", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", + "sha2 0.10.6", "solana-frozen-abi-macro 1.15.0", "solana-logger 1.15.0", "subtle", @@ -5239,24 +5541,24 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708f837d748e574b1e53b250ab1f4a69ba330bbc10d041d02381165f0f36291a" +checksum = "dcd7d529da0fa5b3b5ca71645122fc94c2aaf867744497969c109e1d4b8ad02d" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "rustc_version 0.4.0", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustc_version", + "syn 1.0.102", ] [[package]] name = "solana-frozen-abi-macro" version = "1.15.0" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "rustc_version 0.4.0", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustc_version", + "syn 1.0.102", ] [[package]] @@ -5264,7 +5566,7 @@ name = "solana-genesis" version = "1.15.0" dependencies = [ "base64 0.13.0", - "clap 2.33.3", + "clap 2.34.0", "serde", "serde_json", "serde_yaml", @@ -5304,7 +5606,7 @@ dependencies = [ name = "solana-geyser-plugin-manager" version = "1.15.0" dependencies = [ - "bs58", + "bs58 0.4.0", "crossbeam-channel", "json5", "libloading", @@ -5326,7 +5628,7 @@ version = "1.15.0" dependencies = [ "bincode", "bv", - "clap 2.33.3", + "clap 2.34.0", "crossbeam-channel", "flate2", "indexmap", @@ -5340,7 +5642,7 @@ dependencies = [ "rand_chacha 0.2.2", "rayon", "regex", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_bytes", "serde_derive", @@ -5375,7 +5677,7 @@ dependencies = [ "bincode", "bzip2", "chrono", - "clap 2.33.3", + "clap 2.34.0", "console", "crossbeam-channel", "ctrlc", @@ -5384,7 +5686,7 @@ dependencies = [ "lazy_static", "nix", "reqwest", - "semver 1.0.14", + "semver", "serde", "serde_yaml", "solana-clap-utils", @@ -5395,7 +5697,7 @@ dependencies = [ "solana-version", "tar", "tempfile", - "url 2.2.2", + "url 2.3.1", "winapi 0.3.9", "winreg", ] @@ -5404,8 +5706,8 @@ dependencies = [ name = "solana-keygen" version = "1.15.0" dependencies = [ - "bs58", - "clap 3.1.8", + "bs58 0.4.0", + "clap 3.2.22", "dirs-next", "num_cpus", "solana-clap-v3-utils", @@ -5423,7 +5725,7 @@ dependencies = [ "assert_matches", "bincode", "bitflags", - "bs58", + "bs58 0.4.0", "byteorder", "chrono", "chrono-humanize", @@ -5445,10 +5747,10 @@ dependencies = [ "rayon", "reed-solomon-erasure", "rocksdb", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_bytes", - "sha2 0.10.5", + "sha2 0.10.6", "solana-account-decoder", "solana-bpf-loader-program", "solana-entry", @@ -5483,10 +5785,10 @@ name = "solana-ledger-tool" version = "1.15.0" dependencies = [ "assert_cmd", - "bs58", + "bs58 0.4.0", "bytecount", "chrono", - "clap 2.33.3", + "clap 2.34.0", "crossbeam-channel", "csv", "dashmap 4.0.2", @@ -5555,7 +5857,7 @@ name = "solana-log-analyzer" version = "1.15.0" dependencies = [ "byte-unit", - "clap 3.1.8", + "clap 3.2.22", "serde", "serde_json", "solana-logger 1.15.0", @@ -5564,9 +5866,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ea6fc68d63d33d862d919d4c8ad7f613ec243ccf6762d595c660020b289b57" +checksum = "c690a6ae623bdd2d71229880a9f668ff714b5c6a9bc180a1abef4887da8b6f27" dependencies = [ "env_logger", "lazy_static", @@ -5598,7 +5900,7 @@ version = "1.15.0" name = "solana-merkle-root-bench" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "log", "solana-logger 1.15.0", "solana-measure", @@ -5636,7 +5938,7 @@ dependencies = [ name = "solana-net-shaper" version = "1.15.0" dependencies = [ - "clap 3.1.8", + "clap 3.2.22", "rand 0.7.3", "serde", "serde_json", @@ -5648,7 +5950,7 @@ name = "solana-net-utils" version = "1.15.0" dependencies = [ "bincode", - "clap 3.1.8", + "clap 3.2.22", "crossbeam-channel", "log", "nix", @@ -5660,7 +5962,7 @@ dependencies = [ "solana-sdk 1.15.0", "solana-version", "tokio", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -5726,7 +6028,7 @@ dependencies = [ name = "solana-poh-bench" version = "1.15.0" dependencies = [ - "clap 3.1.8", + "clap 3.2.22", "log", "rand 0.7.3", "rayon", @@ -5740,9 +6042,9 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd314d85b171bb20ccdcaf07346a9d52a012b10d84f4706f0628813d002fef8" +checksum = "f835be7a20e172209538241cdf46451c08b38eaaca65cf16e65658700c447b17" dependencies = [ "base64 0.13.0", "bincode", @@ -5750,14 +6052,14 @@ dependencies = [ "blake3", "borsh", "borsh-derive", - "bs58", + "bs58 0.4.0", "bv", "bytemuck", "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.3", + "getrandom 0.2.7", "itertools", "js-sys", "lazy_static", @@ -5770,17 +6072,17 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.7.3", "rand_chacha 0.2.2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", - "solana-frozen-abi 1.11.5", - "solana-frozen-abi-macro 1.11.5", - "solana-sdk-macro 1.11.5", + "sha2 0.10.6", + "sha3 0.10.5", + "solana-frozen-abi 1.14.5", + "solana-frozen-abi-macro 1.14.5", + "solana-sdk-macro 1.14.5", "thiserror", "tiny-bip39", "wasm-bindgen", @@ -5799,14 +6101,14 @@ dependencies = [ "blake3", "borsh", "borsh-derive", - "bs58", + "bs58 0.4.0", "bv", "bytemuck", "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.3", + "getrandom 0.2.7", "itertools", "js-sys", "lazy_static", @@ -5819,14 +6121,14 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.7.3", "rand_chacha 0.2.2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", + "sha2 0.10.6", + "sha3 0.10.5", "solana-frozen-abi 1.15.0", "solana-frozen-abi-macro 1.15.0", "solana-logger 1.15.0", @@ -5853,7 +6155,7 @@ dependencies = [ "num-derive", "num-traits", "rand 0.7.3", - "rustc_version 0.4.0", + "rustc_version", "serde", "solana-frozen-abi 1.15.0", "solana-frozen-abi-macro 1.15.0", @@ -5897,7 +6199,7 @@ dependencies = [ "futures-util", "log", "reqwest", - "semver 1.0.14", + "semver", "serde", "serde_derive", "serde_json", @@ -5909,7 +6211,7 @@ dependencies = [ "tokio-stream", "tokio-tungstenite", "tungstenite", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -5943,7 +6245,7 @@ dependencies = [ "num-traits", "parking_lot 0.12.1", "qstring", - "semver 1.0.14", + "semver", "solana-sdk 1.15.0", "thiserror", "uriparse", @@ -5955,7 +6257,7 @@ version = "1.15.0" dependencies = [ "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", "crossbeam-channel", "dashmap 4.0.2", "itertools", @@ -6002,7 +6304,7 @@ dependencies = [ "symlink", "thiserror", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.6.10", ] [[package]] @@ -6013,7 +6315,7 @@ dependencies = [ "async-trait", "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", "crossbeam-channel", "futures 0.3.24", "indicatif", @@ -6021,7 +6323,7 @@ dependencies = [ "jsonrpc-http-server", "log", "reqwest", - "semver 1.0.14", + "semver", "serde", "serde_derive", "serde_json", @@ -6039,10 +6341,10 @@ name = "solana-rpc-client-api" version = "1.15.0" dependencies = [ "base64 0.13.0", - "bs58", + "bs58 0.4.0", "jsonrpc-core", "reqwest", - "semver 1.0.14", + "semver", "serde", "serde_derive", "serde_json", @@ -6059,7 +6361,7 @@ name = "solana-rpc-client-nonce-utils" version = "1.15.0" dependencies = [ "anyhow", - "clap 2.33.3", + "clap 2.34.0", "futures 0.3.24", "serde_json", "solana-account-decoder", @@ -6076,7 +6378,7 @@ name = "solana-rpc-test" version = "1.15.0" dependencies = [ "bincode", - "bs58", + "bs58 0.4.0", "crossbeam-channel", "futures-util", "log", @@ -6133,7 +6435,7 @@ dependencies = [ "rand_chacha 0.2.2", "rayon", "regex", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_derive", "solana-address-lookup-table-program", @@ -6164,24 +6466,24 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7d954df63b267857e26670e3aacfd8e2943ca703653b0418e5afc85046c2f3" +checksum = "f74e8d699c3a441a5b0cd94c718e75b25c1a4295c2180a714b12fb1bcf66a51e" dependencies = [ "assert_matches", "base64 0.13.0", "bincode", "bitflags", "borsh", - "bs58", + "bs58 0.4.0", "bytemuck", "byteorder", "chrono", "derivation-path", - "digest 0.10.3", + "digest 0.10.5", "ed25519-dalek", "ed25519-dalek-bip32", - "generic-array 0.14.6", + "generic-array", "hmac 0.12.1", "itertools", "js-sys", @@ -6195,19 +6497,19 @@ dependencies = [ "qstring", "rand 0.7.3", "rand_chacha 0.2.2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", - "solana-frozen-abi 1.11.5", - "solana-frozen-abi-macro 1.11.5", - "solana-logger 1.11.5", - "solana-program 1.11.5", - "solana-sdk-macro 1.11.5", + "sha2 0.10.6", + "sha3 0.10.5", + "solana-frozen-abi 1.14.5", + "solana-frozen-abi-macro 1.14.5", + "solana-logger 1.14.5", + "solana-program 1.14.5", + "solana-sdk-macro 1.14.5", "thiserror", "uriparse", "wasm-bindgen", @@ -6217,22 +6519,23 @@ dependencies = [ name = "solana-sdk" version = "1.15.0" dependencies = [ + "anchor-lang", "anyhow", "assert_matches", "base64 0.13.0", "bincode", "bitflags", "borsh", - "bs58", + "bs58 0.4.0", "bytemuck", "byteorder", "chrono", "curve25519-dalek", "derivation-path", - "digest 0.10.3", + "digest 0.10.5", "ed25519-dalek", "ed25519-dalek-bip32", - "generic-array 0.14.6", + "generic-array", "hex", "hmac 0.12.1", "itertools", @@ -6247,14 +6550,14 @@ dependencies = [ "qstring", "rand 0.7.3", "rand_chacha 0.2.2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", + "sha2 0.10.6", + "sha3 0.10.5", "solana-frozen-abi 1.15.0", "solana-frozen-abi-macro 1.15.0", "solana-logger 1.15.0", @@ -6264,31 +6567,32 @@ dependencies = [ "thiserror", "tiny-bip39", "uriparse", + "uuid", "wasm-bindgen", ] [[package]] name = "solana-sdk-macro" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d9e81bc46edcc517b2df504856d57a5101c7586ec63f3143ae11fbe2eba613" +checksum = "92ffde9b5b7313629780baca10eaffec7421d53be725c76031ca409a5298705c" dependencies = [ - "bs58", - "proc-macro2 1.0.41", - "quote 1.0.18", + "bs58 0.4.0", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustversion", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] name = "solana-sdk-macro" version = "1.15.0" dependencies = [ - "bs58", - "proc-macro2 1.0.41", - "quote 1.0.18", + "bs58 0.4.0", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustversion", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] @@ -6297,11 +6601,13 @@ version = "1.15.0" dependencies = [ "crossbeam-channel", "log", + "solana-gossip", "solana-logger 1.15.0", "solana-measure", "solana-metrics", "solana-runtime", "solana-sdk 1.15.0", + "solana-streamer", "solana-tpu-client", ] @@ -6309,7 +6615,7 @@ dependencies = [ name = "solana-stake-accounts" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "solana-clap-utils", "solana-cli-config", "solana-remote-wallet", @@ -6328,7 +6634,7 @@ dependencies = [ "bincode", "log", "proptest", - "rustc_version 0.4.0", + "rustc_version", "solana-config-program", "solana-logger 1.15.0", "solana-program-runtime", @@ -6365,7 +6671,7 @@ dependencies = [ "solana-transaction-status", "thiserror", "tokio", - "tonic 0.8.0", + "tonic 0.8.2", "zstd", ] @@ -6374,7 +6680,7 @@ name = "solana-storage-proto" version = "1.15.0" dependencies = [ "bincode", - "bs58", + "bs58 0.4.0", "enum-iterator", "prost 0.11.0", "protobuf-src", @@ -6382,14 +6688,14 @@ dependencies = [ "solana-account-decoder", "solana-sdk 1.15.0", "solana-transaction-status", - "tonic-build 0.8.0", + "tonic-build 0.8.2", ] [[package]] name = "solana-store-tool" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "log", "solana-logger 1.15.0", "solana-runtime", @@ -6431,7 +6737,7 @@ dependencies = [ name = "solana-sys-tuner" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "libc", "log", "nix", @@ -6482,13 +6788,41 @@ dependencies = [ "solana-tpu-client", ] +[[package]] +name = "solana-tip-distributor" +version = "1.15.0" +dependencies = [ + "anchor-lang", + "bigdecimal", + "clap 3.2.22", + "env_logger", + "futures 0.3.24", + "im", + "itertools", + "log", + "num-traits", + "serde", + "serde_json", + "solana-client", + "solana-genesis-utils", + "solana-ledger", + "solana-merkle-tree", + "solana-program 1.15.0", + "solana-runtime", + "solana-sdk 1.15.0", + "solana-stake-program", + "thiserror", + "tip-distribution", + "tokio", +] + [[package]] name = "solana-tokens" version = "1.15.0" dependencies = [ "bincode", "chrono", - "clap 2.33.3", + "clap 2.34.0", "console", "csv", "ctrlc", @@ -6556,7 +6890,7 @@ name = "solana-transaction-dos" version = "1.15.0" dependencies = [ "bincode", - "clap 2.33.3", + "clap 2.34.0", "log", "rand 0.7.3", "rayon", @@ -6586,7 +6920,7 @@ dependencies = [ "base64 0.13.0", "bincode", "borsh", - "bs58", + "bs58 0.4.0", "lazy_static", "log", "serde", @@ -6623,7 +6957,7 @@ name = "solana-validator" version = "1.15.0" dependencies = [ "chrono", - "clap 2.33.3", + "clap 2.34.0", "console", "core_affinity", "crossbeam-channel", @@ -6672,6 +7006,7 @@ dependencies = [ "solana-vote-program", "symlink", "tikv-jemallocator", + "tonic 0.5.2", ] [[package]] @@ -6679,8 +7014,8 @@ name = "solana-version" version = "1.15.0" dependencies = [ "log", - "rustc_version 0.4.0", - "semver 1.0.14", + "rustc_version", + "semver", "serde", "serde_derive", "solana-frozen-abi 1.15.0", @@ -6696,7 +7031,7 @@ dependencies = [ "log", "num-derive", "num-traits", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_derive", "solana-frozen-abi 1.15.0", @@ -6713,7 +7048,7 @@ dependencies = [ name = "solana-watchtower" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "humantime", "log", "solana-clap-utils", @@ -6743,9 +7078,9 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62415c05a9ebfffaf8befaa61b24492ebf88269cf84cbeba714bac4125ec4ea3" +checksum = "0a23a2c443027e8cc2981131a38928cb37e554970c497b5735e888049cc85d3f" dependencies = [ "aes-gcm-siv", "arrayref", @@ -6756,6 +7091,7 @@ dependencies = [ "cipher 0.4.3", "curve25519-dalek", "getrandom 0.1.16", + "itertools", "lazy_static", "merlin", "num-derive", @@ -6764,8 +7100,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.9.1", - "solana-program 1.11.5", - "solana-sdk 1.11.5", + "solana-program 1.14.5", + "solana-sdk 1.14.5", "subtle", "thiserror", "zeroize", @@ -6826,9 +7162,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" [[package]] name = "spki" @@ -6850,7 +7186,7 @@ dependencies = [ "borsh", "num-derive", "num-traits", - "solana-program 1.11.5", + "solana-program 1.14.5", "spl-token", "spl-token-2022", "thiserror", @@ -6862,7 +7198,7 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" dependencies = [ - "solana-program 1.11.5", + "solana-program 1.14.5", ] [[package]] @@ -6876,7 +7212,7 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-program 1.11.5", + "solana-program 1.14.5", "thiserror", ] @@ -6891,19 +7227,13 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-program 1.11.5", - "solana-zk-token-sdk 1.11.5", + "solana-program 1.14.5", + "solana-zk-token-sdk 1.14.5", "spl-memo", "spl-token", "thiserror", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_assertions" version = "1.1.0" @@ -6935,24 +7265,24 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.24.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.0", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustversion", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] @@ -6980,12 +7310,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "unicode-ident", ] @@ -7001,10 +7331,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", - "unicode-xid 0.2.2", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", + "unicode-xid 0.2.4", ] [[package]] @@ -7019,9 +7349,9 @@ dependencies = [ [[package]] name = "sysctl" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1123645dfaf2b5eac6b6c88addafc359c789b8ef2a770ecaef758c1ddf363ea4" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" dependencies = [ "bitflags", "byteorder", @@ -7032,15 +7362,15 @@ dependencies = [ [[package]] name = "systemstat" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ef8054a60d76603befaecaf7efe08d32d1a05a1e5df70c547b07507e9e262d" +checksum = "795b3ce9d477f1e022df48a26626c1deff9ff6cadcf2b7d6cc782b13895c3d4c" dependencies = [ "bytesize", "lazy_static", "libc", "nom", - "time 0.3.9", + "time 0.3.15", "winapi 0.3.9", ] @@ -7074,7 +7404,7 @@ dependencies = [ "thiserror", "tokio", "tokio-serde", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "tracing", "tracing-opentelemetry", ] @@ -7085,9 +7415,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -7106,9 +7436,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -7125,30 +7455,30 @@ dependencies = [ [[package]] name = "termtree" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "test-case" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196e8a70562e252cc51eaaaee3ecddc39803d9b7fd4a772b7c7dae7cdf42a859" +checksum = "21d6cf5a7dffb3f9dceec8e6b8ca528d9bd71d36c9f074defb548ce161f598c0" dependencies = [ "test-case-macros", ] [[package]] name = "test-case-macros" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd461f47ade621665c9f4e44b20449341769911c253275dc5cb03726cbb852c" +checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26" dependencies = [ "cfg-if 1.0.0", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -7162,28 +7492,28 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -7203,9 +7533,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.4.2+5.2.1-patched.2" +version = "0.4.3+5.2.1-patched.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5844e429d797c62945a566f8da4e24c7fe3fbd5d6617fd8bf7a0b7dc1ee0f22e" +checksum = "a1792ccb507d955b46af42c123ea8863668fae24d03721e40cad6a41773dbb49" dependencies = [ "cc", "fs_extra", @@ -7214,9 +7544,9 @@ dependencies = [ [[package]] name = "tikv-jemallocator" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c14a5a604eb8715bc5785018a37d00739b180bcf609916ddf4393d33d49ccdf" +checksum = "a5b7bcecfafe4998587d636f9ae9d55eb9d0499877b88757767c346875067098" dependencies = [ "libc", "tikv-jemalloc-sys", @@ -7224,21 +7554,22 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi 0.3.9", ] [[package]] name = "time" -version = "0.3.9" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.4", "libc", "num_threads", "time-macros", @@ -7271,9 +7602,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -7284,6 +7615,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tip-distribution" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "solana-program 1.15.0", +] + +[[package]] +name = "tip-payment" +version = "0.1.0" +dependencies = [ + "anchor-lang", +] + [[package]] name = "tokio" version = "1.14.1" @@ -7306,9 +7652,9 @@ dependencies = [ [[package]] name = "tokio-io-timeout" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ "pin-project-lite", "tokio", @@ -7316,13 +7662,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -7348,9 +7694,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.6", "tokio", @@ -7375,9 +7721,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite", @@ -7394,17 +7740,17 @@ dependencies = [ "log", "rustls 0.20.6", "tokio", - "tokio-rustls 0.23.3", + "tokio-rustls 0.23.4", "tungstenite", "webpki 0.22.0", - "webpki-roots", + "webpki-roots 0.22.5", ] [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -7418,9 +7764,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" dependencies = [ "bytes", "futures-core", @@ -7432,13 +7778,47 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796c5e1cd49905e65dd8e700d4cb1dffcbfdb4fc9d017de08c1a537afd83627c" +dependencies = [ + "async-stream", + "async-trait", + "base64 0.13.0", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding 2.2.0", + "pin-project", + "prost 0.8.0", + "prost-derive 0.8.0", + "rustls-native-certs 0.5.0", + "tokio", + "tokio-rustls 0.22.0", + "tokio-stream", + "tokio-util 0.6.10", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", + "webpki-roots 0.21.1", +] + [[package]] name = "tonic" version = "0.6.2" @@ -7456,14 +7836,14 @@ dependencies = [ "http-body", "hyper", "hyper-timeout", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project", "prost 0.9.0", "prost-derive 0.9.0", "tokio", "tokio-rustls 0.22.0", "tokio-stream", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "tower", "tower-layer", "tower-service", @@ -7473,9 +7853,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498f271adc46acce75d66f639e4d35b31b2394c295c82496727dafa16d465dd2" +checksum = "55b9af819e54b8f33d453655bef9b9acc171568fb49523078d0cc4e7484200ec" dependencies = [ "async-stream", "async-trait", @@ -7489,15 +7869,15 @@ dependencies = [ "http-body", "hyper", "hyper-timeout", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project", "prost 0.11.0", "prost-derive 0.11.0", - "rustls-pemfile 1.0.0", + "rustls-pemfile 1.0.1", "tokio", - "tokio-rustls 0.23.3", + "tokio-rustls 0.23.4", "tokio-stream", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "tower", "tower-layer", "tower-service", @@ -7505,36 +7885,48 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "tonic-build" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b52d07035516c2b74337d2ac7746075e7dcae7643816c1b12c5ff8a7484c08" +dependencies = [ + "proc-macro2 1.0.46", + "prost-build 0.8.0", + "quote 1.0.21", + "syn 1.0.102", +] + [[package]] name = "tonic-build" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.46", "prost-build 0.9.0", - "quote 1.0.18", - "syn 1.0.98", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "tonic-build" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbcd2800e34e743b9ae795867d5f77b535d3a3be69fd731e39145719752df8c" +checksum = "48c6fd7c2581e36d63388a9e04c350c21beb7a8b059580b2e93993c526899ddc" dependencies = [ "prettyplease", - "proc-macro2 1.0.41", - "prost-build 0.11.0", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "prost-build 0.11.1", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "tower" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", @@ -7544,7 +7936,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "tower-layer", "tower-service", "tracing", @@ -7552,9 +7944,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.2.5" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", "bytes", @@ -7571,21 +7963,21 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", "log", @@ -7596,22 +7988,23 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "lazy_static", + "once_cell", + "valuable", ] [[package]] @@ -7626,10 +8019,11 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.17.2" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f9378e96a9361190ae297e7f3a8ff644aacd2897f244b1ff81f381669196fa6" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" dependencies = [ + "once_cell", "opentelemetry", "tracing", "tracing-core", @@ -7638,9 +8032,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ "sharded-slab", "thread_local", @@ -7675,10 +8069,10 @@ dependencies = [ "rustls 0.20.6", "sha-1 0.10.0", "thiserror", - "url 2.2.2", + "url 2.3.1", "utf-8", "webpki 0.22.0", - "webpki-roots", + "webpki-roots 0.22.5", ] [[package]] @@ -7689,9 +8083,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicase" @@ -7704,36 +8098,36 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" @@ -7743,9 +8137,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -7753,7 +8147,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.6", + "generic-array", "subtle", ] @@ -7804,14 +8198,13 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna 0.2.3", - "matches", - "percent-encoding 2.1.0", + "idna 0.3.0", + "percent-encoding 2.2.0", ] [[package]] @@ -7832,9 +8225,25 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8-width" -version = "0.1.5" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" + +[[package]] +name = "uuid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +dependencies = [ + "getrandom 0.2.7", + "rand 0.8.5", +] + +[[package]] +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vcpkg" @@ -7850,9 +8259,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" @@ -7898,15 +8307,21 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -7914,24 +8329,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.28" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -7941,38 +8356,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote 1.0.18", + "quote 1.0.21", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -8000,22 +8415,31 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.1" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki 0.21.4", +] + +[[package]] +name = "webpki-roots" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c475786c6f47219345717a043a37ec04cb4bc185e28853adcc4fa0a947eba630" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki 0.22.0", ] [[package]] name = "which" -version = "4.2.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] @@ -8061,86 +8485,43 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", -] - [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" - [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" - [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" - [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" - [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -8171,14 +8552,14 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.9", + "time 0.3.15", ] [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] @@ -8192,13 +8573,19 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "yasna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" dependencies = [ - "time 0.3.9", + "time 0.3.15", ] [[package]] @@ -8212,13 +8599,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.2.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "synstructure", ] @@ -8233,9 +8620,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "5.0.1+zstd.1.5.2" +version = "5.0.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c12659121420dd6365c5c3de4901f97145b79651fb1d25814020ed2ed0585ae" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" dependencies = [ "libc", "zstd-sys", diff --git a/Cargo.toml b/Cargo.toml index 848ff87bfd..035893384c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ members = [ "banks-client", "banks-interface", "banks-server", + "bench-batch-simulate-bundle", + "bench-get-confirmed-blocks-with-data", "bench-streamer", "bench-tps", "bloom", @@ -30,6 +32,7 @@ members = [ "geyser-plugin-manager", "gossip", "install", + "jito-protos", "keygen", "ledger", "ledger-tool", @@ -85,6 +88,7 @@ members = [ "sys-tuner", "test-validator", "thin-client", + "tip-distributor", "tokens", "tpu-client", "transaction-dos", @@ -98,6 +102,8 @@ members = [ ] exclude = [ + "anchor", + "jito-programs", "programs/bpf", ] diff --git a/README.md b/README.md index b5499fa0e5..2a0d41a109 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,14 @@

-[![Solana crate](https://img.shields.io/crates/v/solana-core.svg)](https://crates.io/crates/solana-core) -[![Solana documentation](https://docs.rs/solana-core/badge.svg)](https://docs.rs/solana-core) -[![Build status](https://badge.buildkite.com/8cc350de251d61483db98bdfc895b9ea0ac8ffa4a32ee850ed.svg?branch=master)](https://buildkite.com/solana-labs/solana/builds?branch=master) -[![codecov](https://codecov.io/gh/solana-labs/solana/branch/master/graph/badge.svg)](https://codecov.io/gh/solana-labs/solana) +[![Build status](https://badge.buildkite.com/a6981eb34c6e0c7c09e3a3cb4bda09579f0ff2dcb1bd74b2ad.svg?branch=master)](https://buildkite.com/jito-labs/jito-solana) + +[//]: # ([![Solana crate](https://img.shields.io/crates/v/solana-core.svg)](https://crates.io/crates/solana-core)) +[//]: # ([![Solana documentation](https://docs.rs/solana-core/badge.svg)](https://docs.rs/solana-core)) +[//]: # ([![codecov](https://codecov.io/gh/solana-labs/solana/branch/master/graph/badge.svg)](https://codecov.io/gh/solana-labs/solana)) + +# About +This repository contains Jito Lab's fork of the Solana validator. # Building diff --git a/anchor b/anchor new file mode 160000 index 0000000000..7532647bb8 --- /dev/null +++ b/anchor @@ -0,0 +1 @@ +Subproject commit 7532647bb86d26fd7497d9cbc7ac99e2b3941e86 diff --git a/banking-bench/src/main.rs b/banking-bench/src/main.rs index 65b48d2505..06307b5061 100644 --- a/banking-bench/src/main.rs +++ b/banking-bench/src/main.rs @@ -1,11 +1,12 @@ #![allow(clippy::integer_arithmetic)] + use { clap::{crate_description, crate_name, Arg, ArgEnum, Command}, crossbeam_channel::{unbounded, Receiver}, log::*, rand::{thread_rng, Rng}, rayon::prelude::*, - solana_core::banking_stage::BankingStage, + solana_core::{banking_stage::BankingStage, bundle_account_locker::BundleAccountLocker}, solana_gossip::cluster_info::{ClusterInfo, Node}, solana_ledger::{ blockstore::Blockstore, @@ -30,6 +31,7 @@ use { solana_streamer::socket::SocketAddrSpace, solana_tpu_client::connection_cache::{ConnectionCache, DEFAULT_TPU_CONNECTION_POOL_SIZE}, std::{ + collections::HashSet, sync::{atomic::Ordering, Arc, RwLock}, thread::sleep, time::{Duration, Instant}, @@ -45,9 +47,15 @@ fn check_txs( let now = Instant::now(); let mut no_bank = false; loop { - if let Ok((_bank, (entry, _tick_height))) = receiver.recv_timeout(Duration::from_millis(10)) + if let Ok(WorkingBankEntry { + bank: _, + entries_ticks, + }) = receiver.recv_timeout(Duration::from_millis(10)) { - total += entry.transactions.len(); + total += entries_ticks + .iter() + .map(|e| e.0.transactions.len()) + .sum::(); } if total >= ref_tx_count { break; @@ -359,6 +367,8 @@ fn main() { None, Arc::new(connection_cache), bank_forks.clone(), + HashSet::default(), + BundleAccountLocker::default(), ); poh_recorder.write().unwrap().set_bank(&bank, false); diff --git a/banks-server/Cargo.toml b/banks-server/Cargo.toml index af32367fc2..f89527e6e3 100644 --- a/banks-server/Cargo.toml +++ b/banks-server/Cargo.toml @@ -15,6 +15,7 @@ crossbeam-channel = "0.5" futures = "0.3" solana-banks-interface = { path = "../banks-interface", version = "=1.15.0" } solana-client = { path = "../client", version = "=1.15.0" } +solana-gossip = { path = "../gossip", version = "=1.15.0" } solana-runtime = { path = "../runtime", version = "=1.15.0" } solana-sdk = { path = "../sdk", version = "=1.15.0" } solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.15.0" } @@ -24,6 +25,9 @@ tokio = { version = "1", features = ["full"] } tokio-serde = { version = "0.8", features = ["bincode"] } tokio-stream = "0.1" +[dev-dependencies] +solana-streamer = { path = "../streamer", version = "=1.15.0" } + [lib] crate-type = ["lib"] name = "solana_banks_server" diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index d33eebdbdb..eb4ad9cb33 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -6,6 +6,7 @@ use { Banks, BanksRequest, BanksResponse, BanksTransactionResultWithSimulation, TransactionConfirmationStatus, TransactionSimulationDetails, TransactionStatus, }, + solana_gossip::cluster_info::ClusterInfo, solana_runtime::{ bank::{Bank, TransactionSimulationResult}, bank_forks::BankForks, @@ -387,7 +388,7 @@ pub async fn start_local_server( pub async fn start_tcp_server( listen_addr: SocketAddr, - tpu_addr: SocketAddr, + cluster_info: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, connection_cache: Arc, @@ -411,7 +412,7 @@ pub async fn start_tcp_server( let (sender, receiver) = unbounded(); SendTransactionService::new::( - tpu_addr, + cluster_info.clone(), &bank_forks, None, receiver, diff --git a/banks-server/src/rpc_banks_service.rs b/banks-server/src/rpc_banks_service.rs index 85ab418885..408397383f 100644 --- a/banks-server/src/rpc_banks_service.rs +++ b/banks-server/src/rpc_banks_service.rs @@ -3,6 +3,7 @@ use { crate::banks_server::start_tcp_server, futures::{future::FutureExt, pin_mut, prelude::stream::StreamExt, select}, + solana_gossip::cluster_info::ClusterInfo, solana_runtime::{bank_forks::BankForks, commitment::BlockCommitmentCache}, solana_tpu_client::connection_cache::ConnectionCache, std::{ @@ -27,7 +28,7 @@ pub struct RpcBanksService { /// Run the TCP service until `exit` is set to true async fn start_abortable_tcp_server( listen_addr: SocketAddr, - tpu_addr: SocketAddr, + cluster_info: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, connection_cache: Arc, @@ -35,7 +36,7 @@ async fn start_abortable_tcp_server( ) { let server = start_tcp_server( listen_addr, - tpu_addr, + cluster_info, bank_forks.clone(), block_commitment_cache.clone(), connection_cache, @@ -58,7 +59,7 @@ async fn start_abortable_tcp_server( impl RpcBanksService { fn run( listen_addr: SocketAddr, - tpu_addr: SocketAddr, + cluster_info: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, connection_cache: Arc, @@ -66,7 +67,7 @@ impl RpcBanksService { ) { let server = start_abortable_tcp_server( listen_addr, - tpu_addr, + cluster_info, bank_forks, block_commitment_cache, connection_cache, @@ -77,7 +78,7 @@ impl RpcBanksService { pub fn new( listen_addr: SocketAddr, - tpu_addr: SocketAddr, + cluster_info: Arc, bank_forks: &Arc>, block_commitment_cache: &Arc>, connection_cache: &Arc, @@ -92,7 +93,7 @@ impl RpcBanksService { .spawn(move || { Self::run( listen_addr, - tpu_addr, + cluster_info, bank_forks, block_commitment_cache, connection_cache, @@ -111,7 +112,14 @@ impl RpcBanksService { #[cfg(test)] mod tests { - use {super::*, solana_runtime::bank::Bank}; + use { + super::*, + solana_gossip::contact_info::ContactInfo, + solana_runtime::bank::Bank, + solana_sdk::signature::Keypair, + solana_streamer::socket::SocketAddrSpace, + std::net::{IpAddr, Ipv4Addr}, + }; #[test] fn test_rpc_banks_server_exit() { @@ -120,9 +128,18 @@ mod tests { let connection_cache = Arc::new(ConnectionCache::default()); let exit = Arc::new(AtomicBool::new(false)); let addr = "127.0.0.1:0".parse().unwrap(); + let contact_info = ContactInfo { + tpu: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080), + ..ContactInfo::default() + }; + let cluster_info: Arc = Arc::new(ClusterInfo::new( + contact_info, + Arc::new(Keypair::new()), + SocketAddrSpace::new(false), + )); let service = RpcBanksService::new( addr, - addr, + cluster_info, &bank_forks, &block_commitment_cache, &connection_cache, diff --git a/bench-batch-simulate-bundle/Cargo.toml b/bench-batch-simulate-bundle/Cargo.toml new file mode 100644 index 0000000000..ba49e0360d --- /dev/null +++ b/bench-batch-simulate-bundle/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "solana-bench-batch-simulate-bundle" +version = "1.15.0" +edition = "2021" +publish = false + +[dependencies] +clap = { version = "3.1.12", features = ["derive", "env"] } +env_logger = "0.9.0" +log = "0.4.17" +num-traits = "0.2.15" +rayon = "1.5.2" +solana-client = { path = "../client" } +solana-runtime = { path = "../runtime" } +solana-sdk = { path = "../sdk" } +solana-transaction-status = { path = "../transaction-status" } diff --git a/bench-batch-simulate-bundle/src/main.rs b/bench-batch-simulate-bundle/src/main.rs new file mode 100644 index 0000000000..56e8762ddb --- /dev/null +++ b/bench-batch-simulate-bundle/src/main.rs @@ -0,0 +1,396 @@ +mod simulator; + +use { + crate::simulator::{Simulator, Stats}, + clap::Parser, + log::*, + num_traits::abs_sub, + solana_client::{ + pubsub_client::PubsubClient, rpc_client::RpcClient, rpc_config::RpcBlockConfig, + }, + solana_runtime::cost_model::CostModel, + solana_sdk::{ + bundle::VersionedBundle, + clock::Slot, + commitment_config::{CommitmentConfig, CommitmentLevel}, + message::{ + v0::{LoadedAddresses, MessageAddressTableLookup}, + AddressLoaderError, + }, + transaction::{AddressLoader, SanitizedTransaction, VersionedTransaction}, + }, + solana_transaction_status::{TransactionDetails, UiConfirmedBlock, UiTransactionEncoding}, + std::{ + cmp::Reverse, + collections::BinaryHeap, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, RwLock, + }, + thread::{self, sleep, Builder, JoinHandle}, + time::Duration, + }, +}; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// URL of the RPC server with no simulations running + #[clap(long, env, default_value = "https://api.testnet.solana.com")] + baseline_rpc_url: String, + + /// websocket URL of the RPC server with no simulations running + #[clap(long, env, default_value = "ws://api.testnet.solana.com")] + baseline_ws_url: String, + + /// URL of the RPC server running simulations against + #[clap(long, env)] + simulation_rpc_url: String, + + /// websocket URL of the RPC server running simulations against + #[clap(long, env)] + simulation_ws_url: String, + + /// duration to run the test for, must be >= [SIMULATION_REFRESH_SECS] + #[clap(long, env, default_value_t = 60)] + test_duration_secs: u64, + + /// size of the bundle batch being sent for simulation + #[clap(long, env, default_value_t = 5)] + bundle_batch_size: usize, + + /// number of threads sharing a single RPC connection + #[clap(long, env, default_value_t = 16)] + n_threads: usize, + + /// number of unique RPC connections + #[clap(long, env, default_value_t = 32)] + n_rpc_connections: u64, +} + +const SIMULATION_REFRESH_SECS: u64 = 5; +const BUNDLE_SIZE: usize = 3; + +pub struct BundleBatch { + pub bundles: Vec, + pub simulation_slot: Slot, +} + +fn main() { + env_logger::init(); + + println!("starting load test..."); + + let args = Args::parse(); + assert!(args.test_duration_secs >= SIMULATION_REFRESH_SECS); + + let stats = Arc::new(Stats { + total_rpc_errs: Arc::new(AtomicU64::new(0)), + total_sim_errs: Arc::new(AtomicU64::new(0)), + total_sim_success: Arc::new(AtomicU64::new(0)), + }); + let simulation_refresh_interval = Duration::from_secs(SIMULATION_REFRESH_SECS); + let exit = Arc::new(AtomicBool::new(false)); + + // get the current finalized slots of each node and make sure they're not too far off + const TOLERABLE_SLOT_DIFF: i64 = 3; + let baseline_rpc_client = RpcClient::new(args.baseline_rpc_url.clone()); + let simulation_rpc_client = RpcClient::new(args.simulation_rpc_url.clone()); + let (baseline_node_slot, simulation_node_slot) = fetch_and_assert_slot_diff( + &baseline_rpc_client, + &simulation_rpc_client, + Some(TOLERABLE_SLOT_DIFF), + ); + println!( + "[baseline_node_slot: {}, simulation_node_slot: {}, diff: {}]", + baseline_node_slot, + simulation_node_slot, + abs_sub(baseline_node_slot, simulation_node_slot) + ); + + let t_hdls = vec![ + spawn_slots_subscribe_thread( + args.simulation_ws_url, + "simulation-node".into(), + exit.clone(), + ), + spawn_slots_subscribe_thread(args.baseline_ws_url, "baseline-node".into(), exit.clone()), + ]; + + let rpc_client = RpcClient::new(args.baseline_rpc_url.clone()); + let (transactions, simulation_slot) = + fetch_n_highest_cost_transactions(&rpc_client, BUNDLE_SIZE); + + let bundle = VersionedBundle { transactions }; + let bundles = (0..args.bundle_batch_size) + .map(|_| bundle.clone()) + .collect::>(); + drop(bundle); + + // This object is read-locked by all Simulator threads and write-locked by `spawn_highest_cost_bundle_scraper` + // periodically to update. + let bundle_batch = BundleBatch { + bundles, + simulation_slot, + }; + let bundle_batch = Arc::new(RwLock::new(bundle_batch)); + + spawn_highest_cost_bundle_scraper( + bundle_batch.clone(), + rpc_client, + simulation_refresh_interval, + args.bundle_batch_size, + BUNDLE_SIZE, + ); + + let simulators: Vec> = (0..args.n_rpc_connections) + .map(|_| { + let stats = stats.clone(); + let rpc_client = RpcClient::new(args.simulation_rpc_url.clone()); + Arc::new(Simulator::new( + rpc_client, + stats, + args.n_threads, + exit.clone(), + )) + }) + .collect(); + for s in &simulators { + let s = s.clone(); + let bundle_batch = bundle_batch.clone(); + thread::spawn(move || { + s.start(bundle_batch); + }); + } + + sleep(Duration::from_secs(args.test_duration_secs)); + exit.store(true, Ordering::Relaxed); + + for t in t_hdls { + info!("joining..."); + t.join().unwrap(); + } + + { + let t0 = stats.total_sim_success.load(Ordering::Acquire) as f64; + let t1 = stats.total_sim_errs.load(Ordering::Acquire) as f64; + let actual_rps = (t0 + t1) / args.test_duration_secs as f64; + println!( + "[successful simulations: {}, total_sim_errs: {}, total_rpc_errs: {}, actual_rps: {}]", + stats.total_sim_success.load(Ordering::Acquire), + stats.total_sim_errs.load(Ordering::Acquire), + stats.total_rpc_errs.load(Ordering::Acquire), + actual_rps, + ); + + let (baseline_node_slot, simulation_node_slot) = + fetch_and_assert_slot_diff(&baseline_rpc_client, &simulation_rpc_client, None); + println!( + "[baseline_node_slot: {}, simulation_node_slot: {}, diff: {}]", + baseline_node_slot, + simulation_node_slot, + abs_sub(baseline_node_slot, simulation_node_slot) + ); + } + + println!("finished load test..."); +} + +fn spawn_highest_cost_bundle_scraper( + bundle_batch: Arc>, + rpc_client: RpcClient, + refresh: Duration, + batch_size: usize, + bundle_size: usize, +) -> JoinHandle<()> { + Builder::new() + .name("highest-cost-tx-scraper".into()) + .spawn(move || loop { + let (transactions, simulation_slot) = + fetch_n_highest_cost_transactions(&rpc_client, bundle_size); + + let bundle = VersionedBundle { transactions }; + let bundles = (0..batch_size) + .map(|_| bundle.clone()) + .collect::>(); + drop(bundle); + + let mut w_bundle_batch = bundle_batch.write().unwrap(); + *w_bundle_batch = BundleBatch { + bundles, + simulation_slot, + }; + drop(w_bundle_batch); + + sleep(refresh); + }) + .unwrap() +} + +fn spawn_slots_subscribe_thread( + pubsub_addr: String, + node_name: String, + exit: Arc, +) -> JoinHandle<()> { + let mut slots_sub = PubsubClient::slot_subscribe(&*pubsub_addr).unwrap(); + thread::spawn(move || loop { + if exit.load(Ordering::Acquire) { + let _ = slots_sub.0.shutdown(); + break; + } + + match slots_sub.1.recv() { + Ok(slot_info) => info!("[RPC={} slot={:?}]", node_name, slot_info.slot), + Err(e) => { + error!("error receiving on slots_sub channel: {}", e); + slots_sub = PubsubClient::slot_subscribe(&*pubsub_addr).unwrap(); + } + } + }) +} + +/// Fetches the N highest cost transactions from the last confirmed block and returns said block's parent slot +fn fetch_n_highest_cost_transactions( + rpc_client: &RpcClient, + n: usize, +) -> (Vec, Slot) { + let slot = rpc_client + .get_slot_with_commitment(CommitmentConfig::confirmed()) + .unwrap(); + info!("fetched slot {}", slot); + + let config = RpcBlockConfig { + encoding: Some(UiTransactionEncoding::Base64), + transaction_details: Some(TransactionDetails::Full), + rewards: None, + commitment: Some(CommitmentConfig { + commitment: CommitmentLevel::Confirmed, + }), + max_supported_transaction_version: None, + }; + let block = rpc_client + .get_block_with_config(slot, config) + .expect(&*format!("failed to fetch block at slot: {}", slot)); + + let parent_slot = block.parent_slot; + ( + n_highest_cost_transactions_from_block(block, &CostModel::default(), n), + parent_slot, + ) +} + +#[derive(Eq)] +struct TransactionCost { + transaction: VersionedTransaction, + cost: u64, +} + +impl PartialEq for TransactionCost { + fn eq(&self, other: &Self) -> bool { + self.cost == other.cost + } +} + +impl PartialOrd for TransactionCost { + fn partial_cmp(&self, other: &Self) -> Option { + self.cost.partial_cmp(&other.cost) + } +} + +impl Ord for TransactionCost { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.cost.cmp(&other.cost) + } +} + +/// Returns the N highest cost transactions from a given block +fn n_highest_cost_transactions_from_block( + block: UiConfirmedBlock, + cost_model: &CostModel, + n: usize, +) -> Vec { + let txs: Vec = block + .transactions + .unwrap() + .into_iter() + .filter(|encoded_tx| encoded_tx.meta.as_ref().unwrap().err.is_none()) + .filter_map(|encoded_tx| encoded_tx.transaction.decode()) + .collect(); + let mut max_costs: BinaryHeap> = BinaryHeap::with_capacity(n); + + for tx in txs { + if let Ok(sanitized_tx) = SanitizedTransaction::try_create( + tx.clone(), + tx.message.hash(), + None, + MockAddressLoader {}, + false, + ) { + let cost = cost_model.calculate_cost(&sanitized_tx).sum(); + if let Some(min_cost) = max_costs.peek() { + if cost > min_cost.0.cost { + if max_costs.len() == n { + let _ = max_costs.pop(); + } + max_costs.push(Reverse(TransactionCost { + cost, + transaction: tx.clone(), + })); + } + } else { + max_costs.push(Reverse(TransactionCost { + cost, + transaction: tx.clone(), + })); + } + } + } + + max_costs + .into_iter() + .map(|tx_cost| tx_cost.0.transaction) + .collect::>() +} + +fn fetch_and_assert_slot_diff( + rpc_client_0: &RpcClient, + rpc_client_1: &RpcClient, + tolerable_diff: Option, +) -> (i64, i64) { + let slot_0 = rpc_client_0 + .get_slot_with_commitment(CommitmentConfig { + commitment: CommitmentLevel::Finalized, + }) + .unwrap() as i64; + let slot_1 = rpc_client_1 + .get_slot_with_commitment(CommitmentConfig { + commitment: CommitmentLevel::Finalized, + }) + .unwrap() as i64; + + if let Some(tolerable_diff) = tolerable_diff { + let actual_diff = abs_sub(slot_0, slot_1); + assert!( + actual_diff < tolerable_diff, + "{}", + format!( + "actual_diff: {}, tolerable_diff: {}", + actual_diff, tolerable_diff + ) + ); + } + + (slot_0, slot_1) +} + +#[derive(Clone)] +struct MockAddressLoader; + +impl AddressLoader for MockAddressLoader { + fn load_addresses( + self, + _lookups: &[MessageAddressTableLookup], + ) -> Result { + Ok(LoadedAddresses::default()) + } +} diff --git a/bench-batch-simulate-bundle/src/simulator.rs b/bench-batch-simulate-bundle/src/simulator.rs new file mode 100644 index 0000000000..e8d844ba58 --- /dev/null +++ b/bench-batch-simulate-bundle/src/simulator.rs @@ -0,0 +1,149 @@ +use { + crate::{BundleBatch, Slot}, + log::*, + rayon::{ThreadPool, ThreadPoolBuilder}, + solana_client::{ + rpc_client::RpcClient, + rpc_config::{RpcSimulateBundleConfig, SimulationSlotConfig}, + rpc_response::RpcBundleSimulationSummary, + }, + solana_sdk::bundle::VersionedBundle, + std::{ + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, RwLock, + }, + thread::sleep, + time::Duration, + }, +}; + +pub struct Simulator { + t_pool: ThreadPool, + /// shared tcp socket amongst the thread pool + rpc_client: Arc, + stats: Arc, + exit: Arc, +} + +pub struct Stats { + pub total_rpc_errs: Arc, + pub total_sim_errs: Arc, + pub total_sim_success: Arc, +} + +impl Simulator { + pub fn new( + rpc_client: RpcClient, + stats: Arc, + n_threads: usize, + exit: Arc, + ) -> Self { + let t_pool = ThreadPoolBuilder::new() + .num_threads(n_threads) + .build() + .unwrap(); + let rpc_client = Arc::new(rpc_client); + + Self { + t_pool, + rpc_client, + stats, + exit, + } + } + + pub fn start(&self, bundle_batch: Arc>) { + info!("starting bundle batch simulator..."); + + loop { + if self.exit.load(Ordering::Relaxed) { + info!("simulator exiting..."); + break; + } + + let (bundles, simulation_slot) = { + let r_bundle_batch = bundle_batch.read().unwrap(); + ( + r_bundle_batch.bundles.clone(), + r_bundle_batch.simulation_slot, + ) + }; + let rpc_client = self.rpc_client.clone(); + let stats = self.stats.clone(); + + self.t_pool.spawn(move || { + // TODO: is this slow? + if let Some((n_succeeded, n_failed)) = + Self::do_simulate(bundles, simulation_slot, &rpc_client) + { + stats + .total_sim_success + .fetch_add(n_succeeded, Ordering::Relaxed); + stats.total_sim_errs.fetch_add(n_failed, Ordering::Relaxed); + info!( + "succeeded={}, failed={}, simulation_slot={}", + n_succeeded, n_failed, simulation_slot + ); + } else { + stats.total_rpc_errs.fetch_add(1, Ordering::Relaxed); + } + }); + + sleep(Duration::from_millis(10)); + } + } + + /// returns (num_succeeded, num_failed) simulations + fn do_simulate( + bundles: Vec, + simulation_slot: Slot, + rpc_client: &Arc, + ) -> Option<(u64, u64)> { + let configs = bundles + .iter() + .map(|b| RpcSimulateBundleConfig { + // TODO: Let's set some accounts data for more realistic performance metrics. + pre_execution_accounts_configs: vec![None; b.transactions.len()], + post_execution_accounts_configs: vec![None; b.transactions.len()], + replace_recent_blockhash: true, + simulation_bank: Some(SimulationSlotConfig::Slot(simulation_slot)), + skip_sig_verify: true, + transaction_encoding: None, + }) + .collect::>(); + + match rpc_client + .batch_simulate_bundle_with_config(bundles.into_iter().zip(configs).collect()) + { + Ok(response) => { + let mut n_succeeded: u64 = 0; + let mut n_failed: u64 = 0; + + for result in response { + match result.result.value.summary { + RpcBundleSimulationSummary::Failed { + error, + tx_signature, + } => { + error!( + "bundle simulation failed [error={:?}, tx_signature={}]", + error, tx_signature + ); + n_failed = n_failed.checked_add(1).unwrap(); + } + RpcBundleSimulationSummary::Succeeded => { + n_succeeded = n_succeeded.checked_add(1).unwrap() + } + } + } + + Some((n_succeeded, n_failed)) + } + Err(e) => { + error!("error from rpc {}", e); + None + } + } + } +} diff --git a/bench-get-confirmed-blocks-with-data/Cargo.toml b/bench-get-confirmed-blocks-with-data/Cargo.toml new file mode 100644 index 0000000000..e3648fd640 --- /dev/null +++ b/bench-get-confirmed-blocks-with-data/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bench-get-confirmed-blocks-with-data" +version = "1.15.0" +edition = "2021" + +[dependencies] +env_logger = "0.9.0" +log = "0.4.17" +solana-sdk = { path = "../sdk", version = "=1.15.0" } +solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.15.0" } +solana-transaction-status = { path = "../transaction-status", version = "=1.15.0" } +tokio = { version = "1", features = ["full"] } diff --git a/bench-get-confirmed-blocks-with-data/src/main.rs b/bench-get-confirmed-blocks-with-data/src/main.rs new file mode 100644 index 0000000000..5361ab667c --- /dev/null +++ b/bench-get-confirmed-blocks-with-data/src/main.rs @@ -0,0 +1,120 @@ +use { + log::info, + solana_sdk::clock::Slot, + solana_transaction_status::ConfirmedBlock, + std::{ + sync::{Arc, Mutex}, + thread::{self, sleep}, + time::{Duration, Instant}, + }, + tokio::task::JoinHandle, +}; + +fn main() { + env_logger::init(); + + let num_blocks_to_fetch: Vec = vec![10]; + let num_tasks = 128; + let lowest_slot: Slot = 1_000_000; + let highest_slot: Slot = 135_000_000; + let task_unit = (highest_slot.checked_sub(lowest_slot).unwrap()) + .checked_div(num_tasks) + .unwrap(); + let test_duration_s = 4_u64.checked_mul(60).unwrap().checked_mul(60).unwrap(); + + let log_duration = Duration::from_secs(1); + + let test_duration = Duration::from_secs(test_duration_s); + + for chunk_size in num_blocks_to_fetch { + info!( + "Benchmarking performance of get_confirmed_blocks_with_data for {:?} blocks", + chunk_size + ); + + let total_blocks_read = Arc::new(Mutex::new(0_usize)); + + let thread = { + let total_blocks_read = total_blocks_read.clone(); + thread::spawn(move || { + let test_start = Instant::now(); + + let mut last_update_time = Instant::now(); + let mut last_update_count = 0; + + while test_start.elapsed() < test_duration { + let elapsed = last_update_time.elapsed(); + if elapsed > log_duration { + let total_blocks_read = *total_blocks_read.lock().unwrap(); + let blocks_received = + total_blocks_read.checked_sub(last_update_count).unwrap(); + let recent_block_rate = blocks_received as f64 / elapsed.as_secs_f64(); + let total_block_rate = + total_blocks_read as f64 / test_start.elapsed().as_secs_f64(); + info!( + "tasks: {}, chunk_size: {}, recent_block_rate: {:.2}, total_blocks_read: {}, total_elapsed: {:.2}, total blocks/s: {:.2}", + num_tasks, + chunk_size, + recent_block_rate, + total_blocks_read, + test_start.elapsed().as_secs_f64(), + total_block_rate + ); + + last_update_time = Instant::now(); + last_update_count = total_blocks_read; + } + + sleep(Duration::from_millis(100)); + } + }) + }; + + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(async { + let tasks: Vec> = (0..num_tasks) + .map(|i| { + let total_blocks_read = total_blocks_read.clone(); + runtime.spawn(async move { + let bigtable = + solana_storage_bigtable::LedgerStorage::new(true, None, None) + .await + .expect("connected to bigtable"); + + let start = Instant::now(); + let mut starting_slot = (task_unit.checked_mul(i).unwrap()) + .checked_add(lowest_slot) + .unwrap(); + let stopping_slot = starting_slot.checked_add(task_unit).unwrap(); + + while start.elapsed() < test_duration { + let slot_requests: Vec<_> = (starting_slot + ..starting_slot.checked_add(chunk_size).unwrap_or(u64::MAX)) + .collect(); + let slots_blocks: Vec<(Slot, ConfirmedBlock)> = bigtable + .get_confirmed_blocks_with_data(slot_requests.as_slice()) + .await + .expect("got blocks") + .collect(); + starting_slot = slots_blocks.last().unwrap().0; + { + let mut total_blocks_read = total_blocks_read.lock().unwrap(); + *total_blocks_read = + total_blocks_read.checked_add(slots_blocks.len()).unwrap(); + } + if starting_slot >= stopping_slot { + info!("work here is done!!"); + break; + } + } + }) + }) + .collect(); + for t in tasks { + t.await.expect("results fetched"); + } + }); + + thread.join().unwrap(); + } +} diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000000..b01bc6734f --- /dev/null +++ b/bootstrap @@ -0,0 +1,21 @@ +#!/usr/bin/env sh +bank_hash=$(./target/release/solana-ledger-tool -l config/bootstrap-validator bank-hash) + +# NOTE: make sure tip-payment and tip-distribution program are deployed using the correct pubkeys +RUST_LOG=INFO,solana_core::bundle_stage=DEBUG \ + NDEBUG=1 ./multinode-demo/bootstrap-validator.sh \ + --wait-for-supermajority 0 \ + --expected-bank-hash $bank_hash \ + --block-engine-address http://127.0.0.1:1003 \ + --block-engine-auth-service-address http://127.0.0.1:1005 \ + --relayer-auth-service-address http://127.0.0.1:11226 \ + --relayer-address http://127.0.0.1:11226 \ + --rpc-pubsub-enable-block-subscription \ + --enable-rpc-transaction-history \ + --tip-payment-program-pubkey 6veFRUKJBNGMR58LEcKn5Bc6MR17WZF4rsgD4Lqq7fsU \ + --tip-distribution-program-pubkey 3PX9z1qPj37eNZqH7e5fyaVDyG7ARqkjkYEe1a4xsBkA \ + --commission-bps 0 \ + --shred-receiver-address 127.0.0.1:1002 \ + --allow-private-addr \ + --trust-relayer-packets \ + --trust-block-engine-packets diff --git a/ci/buildkite-pipeline-in-disk.sh b/ci/buildkite-pipeline-in-disk.sh index 361bef9f36..1f9461193c 100644 --- a/ci/buildkite-pipeline-in-disk.sh +++ b/ci/buildkite-pipeline-in-disk.sh @@ -186,7 +186,7 @@ all_test_steps() { queue: "gcp" EOF else - annotate --style info \ + annotate --style info --context test-stable-bpf \ "Stable-SBF skipped as no relevant files were modified" fi @@ -204,16 +204,20 @@ EOF ^programs/ \ ^sdk/ \ ; then - cat >> "$output_file" <<"EOF" - - command: "ci/test-stable-perf.sh" - name: "stable-perf" - timeout_in_minutes: 20 - artifact_paths: "log-*.txt" - agents: - queue: "cuda" -EOF + +annotate --style warning --context test-stable-perf \ + "test-stable-perf is currently disabled because it requires GPUs (LB)" +#cat >> "$output_file" <<"EOF" +# - command: "ci/test-stable-perf.sh" +# name: "stable-perf" +# timeout_in_minutes: 20 +# artifact_paths: "log-*.txt" +# agents: +# queue: "cuda" +#EOF + else - annotate --style info \ + annotate --style info --context test-stable-perf \ "Stable-perf skipped as no relevant files were modified" fi @@ -238,7 +242,7 @@ EOF timeout_in_minutes: 30 EOF else - annotate --style info \ + annotate --style info --context test-downstream-projects \ "downstream-projects skipped as no relevant files were modified" fi @@ -248,9 +252,11 @@ EOF ^ci/test-stable.sh \ ^sdk/ \ ; then - command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20 + annotate --style warning --context test-wasm \ + "test-wasm is currently disabled because it times out (LB)" +# command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20 else - annotate --style info \ + annotate --style info --context test-wasm \ "wasm skipped as no relevant files were modified" fi @@ -313,7 +319,7 @@ if [[ -n $BUILDKITE_TAG ]]; then "https://github.com/solana-labs/solana/releases/$BUILDKITE_TAG" # Jump directly to the secondary build to publish release artifacts quickly - trigger_secondary_step +# trigger_secondary_step exit 0 fi @@ -341,5 +347,5 @@ fi start_pipeline "Push pipeline for ${BUILDKITE_BRANCH:-?unknown branch?}" pull_or_push_steps wait_step -trigger_secondary_step +#trigger_secondary_step exit 0 diff --git a/ci/buildkite-pipeline.sh b/ci/buildkite-pipeline.sh index 53c3f23756..3cec8b2715 100755 --- a/ci/buildkite-pipeline.sh +++ b/ci/buildkite-pipeline.sh @@ -186,7 +186,7 @@ all_test_steps() { queue: "gcp" EOF else - annotate --style info \ + annotate --style info --context test-stable-bpf \ "Stable-SBF skipped as no relevant files were modified" fi @@ -204,16 +204,20 @@ EOF ^programs/ \ ^sdk/ \ ; then - cat >> "$output_file" <<"EOF" - - command: "ci/test-stable-perf.sh" - name: "stable-perf" - timeout_in_minutes: 20 - artifact_paths: "log-*.txt" - agents: - queue: "cuda" -EOF + +annotate --style warning --context test-stable-perf \ + "test-stable-perf is currently disabled because it requires GPUs (LB)" +#cat >> "$output_file" <<"EOF" +# - command: "ci/test-stable-perf.sh" +# name: "stable-perf" +# timeout_in_minutes: 20 +# artifact_paths: "log-*.txt" +# agents: +# queue: "cuda" +#EOF + else - annotate --style info \ + annotate --style info --context test-stable-perf \ "Stable-perf skipped as no relevant files were modified" fi @@ -240,7 +244,7 @@ EOF queue: "solana" EOF else - annotate --style info \ + annotate --style info --context test-downstream-projects \ "downstream-projects skipped as no relevant files were modified" fi @@ -250,9 +254,11 @@ EOF ^ci/test-stable.sh \ ^sdk/ \ ; then - command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20 + annotate --style warning --context test-wasm \ + "test-wasm is currently disabled because it times out (LB)" +# command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20 else - annotate --style info \ + annotate --style info --context test-wasm \ "wasm skipped as no relevant files were modified" fi @@ -315,7 +321,7 @@ if [[ -n $BUILDKITE_TAG ]]; then "https://github.com/solana-labs/solana/releases/$BUILDKITE_TAG" # Jump directly to the secondary build to publish release artifacts quickly - trigger_secondary_step +# trigger_secondary_step exit 0 fi @@ -343,5 +349,5 @@ fi start_pipeline "Push pipeline for ${BUILDKITE_BRANCH:-?unknown branch?}" pull_or_push_steps wait_step -trigger_secondary_step +#trigger_secondary_step exit 0 diff --git a/ci/buildkite-solana-private.sh b/ci/buildkite-solana-private.sh index d05972808a..7b755dc5a3 100644 --- a/ci/buildkite-solana-private.sh +++ b/ci/buildkite-solana-private.sh @@ -186,7 +186,7 @@ all_test_steps() { queue: "sol-private" EOF else - annotate --style info \ + annotate --style info --context test-stable-bpf \ "Stable-SBF skipped as no relevant files were modified" fi @@ -204,16 +204,19 @@ EOF ^programs/ \ ^sdk/ \ ; then - cat >> "$output_file" <<"EOF" - - command: "ci/test-stable-perf.sh" - name: "stable-perf" - timeout_in_minutes: 35 - artifact_paths: "log-*.txt" - agents: - queue: "sol-private" -EOF + +annotate --style warning --context test-stable-perf \ + "test-stable-perf is currently disabled because it requires GPUs (LB)" +# cat >> "$output_file" <<"EOF" +# - command: "ci/test-stable-perf.sh" +# name: "stable-perf" +# timeout_in_minutes: 35 +# artifact_paths: "log-*.txt" +# agents: +# queue: "sol-private" +#EOF else - annotate --style info \ + annotate --style info --context test-stable-perf \ "Stable-perf skipped as no relevant files were modified" fi @@ -240,7 +243,7 @@ EOF queue: "sol-private" EOF else - annotate --style info \ + annotate --style info --context test-downstream-projects \ "downstream-projects skipped as no relevant files were modified" fi @@ -250,9 +253,11 @@ EOF ^ci/test-stable.sh \ ^sdk/ \ ; then - command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20 + annotate --style warning --context test-wasm \ + "test-wasm is currently disabled because it times out (LB)" +# command_step wasm ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-wasm.sh" 20 else - annotate --style info \ + annotate --style info --context test-wasm \ "wasm skipped as no relevant files were modified" fi diff --git a/ci/docker-rust/Dockerfile b/ci/docker-rust/Dockerfile index f1e4a8b156..b979e76ca6 100644 --- a/ci/docker-rust/Dockerfile +++ b/ci/docker-rust/Dockerfile @@ -40,6 +40,7 @@ RUN set -x \ && cargo install mdbook-linkcheck \ && cargo install svgbob_cli \ && cargo install wasm-pack \ + && cargo install sccache \ && rustc --version \ && cargo --version \ && curl -OL https://github.com/google/protobuf/releases/download/v$PROTOC_VERSION/$PROTOC_ZIP \ diff --git a/ci/test-stable.sh b/ci/test-stable.sh index ccd75adca2..4a86bba528 100755 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -119,7 +119,7 @@ test-stable-sbf) # latest mainbeta release version. solana_program_count=$(grep -c 'solana-program v' cargo.log) rm -f cargo.log - if ((solana_program_count > 4)); then + if ((solana_program_count > 20)); then echo "Regression of build redundancy ${solana_program_count}." echo "Review dependency features that trigger redundant rebuilds of solana-program." exit 1 diff --git a/core/Cargo.toml b/core/Cargo.toml index b0f6e6ac69..b74fa21ee2 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,22 +15,31 @@ codecov = { repository = "solana-labs/solana", branch = "master", service = "git [dependencies] ahash = "0.7.6" +anchor-lang = { path = "../anchor/lang" } base64 = "0.13.0" bincode = "1.3.3" bs58 = "0.4.0" +bytes = "1.1.0" chrono = { version = "0.4.22", features = ["serde"] } +clap = { version = "3.1.15", features = ["derive"] } crossbeam-channel = "0.5" dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] } eager = "0.1.0" etcd-client = { version = "0.8.1", features = ["tls"] } fs_extra = "1.2.0" +futures = "0.3" +futures-util = "0.3" histogram = "0.6.9" +indexmap = "1.8.1" itertools = "0.10.5" +jito-protos = { path = "../jito-protos", version = "=1.15.0" } lazy_static = "1.4.0" log = "0.4.17" lru = "0.7.7" min-max-heap = "1.3.0" num_enum = "0.5.7" +prost = "0.8.0" +prost-types = "0.8.0" rand = "0.7.0" rand_chacha = "0.2.2" rayon = "1.5.3" @@ -64,8 +73,13 @@ solana-vote-program = { path = "../programs/vote", version = "=1.15.0" } sys-info = "0.9.1" tempfile = "3.3.0" thiserror = "1.0" +tip-distribution = { path = "../jito-programs/tip-payment/programs/tip-distribution", features = ["no-entrypoint"] } +tip-payment = { path = "../jito-programs/tip-payment/programs/tip-payment", features = ["no-entrypoint"] } tokio = { version = "~1.14.1", features = ["full"] } +tokio-stream = "0.1.8" +tonic = { version = "0.5.2", features = ["tls"] } trees = "0.4.2" +uuid = { version = "1.0.0", features = ["v4", "fast-rng"] } [dev-dependencies] matches = "0.1.9" @@ -84,6 +98,7 @@ sysctl = "0.4.4" [build-dependencies] rustc_version = "0.4" +tonic-build = "0.5.2" [[bench]] name = "banking_stage" diff --git a/core/benches/banking_stage.rs b/core/benches/banking_stage.rs index ae0bd6b07b..8e8833e00f 100644 --- a/core/benches/banking_stage.rs +++ b/core/benches/banking_stage.rs @@ -10,6 +10,7 @@ use { rayon::prelude::*, solana_core::{ banking_stage::{BankingStage, BankingStageStats}, + bundle_account_locker::BundleAccountLocker, leader_slot_banking_stage_metrics::LeaderSlotMetricsTracker, qos_service::QosService, unprocessed_packet_batches::*, @@ -41,6 +42,7 @@ use { vote_state::VoteStateUpdate, vote_transaction::new_vote_state_update_transaction, }, std::{ + collections::HashSet, sync::{atomic::Ordering, Arc, RwLock}, time::{Duration, Instant}, }, @@ -51,8 +53,15 @@ fn check_txs(receiver: &Arc>, ref_tx_count: usize) { let mut total = 0; let now = Instant::now(); loop { - if let Ok((_bank, (entry, _tick_height))) = receiver.recv_timeout(Duration::new(1, 0)) { - total += entry.transactions.len(); + if let Ok(WorkingBankEntry { + bank: _, + entries_ticks, + }) = receiver.recv_timeout(Duration::new(1, 0)) + { + total += entries_ticks + .iter() + .map(|e| e.0.transactions.len()) + .sum::(); } if total >= ref_tx_count { break; @@ -103,6 +112,8 @@ fn bench_consume_buffered(bencher: &mut Bencher) { &mut LeaderSlotMetricsTracker::new(0), 10, None, + &HashSet::default(), + &BundleAccountLocker::default(), ); }); @@ -287,6 +298,8 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) { None, Arc::new(ConnectionCache::default()), bank_forks, + HashSet::new(), + BundleAccountLocker::default(), ); poh_recorder.write().unwrap().set_bank(&bank, false); diff --git a/core/benches/cluster_info.rs b/core/benches/cluster_info.rs index ea12f7d089..84e9d5685e 100644 --- a/core/benches/cluster_info.rs +++ b/core/benches/cluster_info.rs @@ -79,6 +79,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) { &cluster_info, &bank_forks, &SocketAddrSpace::Unspecified, + None, ) .unwrap(); }); diff --git a/core/benches/proto_to_packet.rs b/core/benches/proto_to_packet.rs new file mode 100644 index 0000000000..87f85f9c7f --- /dev/null +++ b/core/benches/proto_to_packet.rs @@ -0,0 +1,56 @@ +#![feature(test)] + +extern crate test; + +use { + jito_protos::proto::packet::{ + Meta as PbMeta, Packet as PbPacket, PacketBatch, PacketFlags as PbFlags, + }, + solana_core::proto_packet_to_packet, + solana_sdk::packet::{Packet, PACKET_DATA_SIZE}, + std::iter::repeat, + test::{black_box, Bencher}, +}; + +fn get_proto_packet(i: u8) -> PbPacket { + PbPacket { + data: repeat(i).take(PACKET_DATA_SIZE).collect(), + meta: Some(PbMeta { + size: PACKET_DATA_SIZE as u64, + addr: "255.255.255.255:65535".to_string(), + port: 65535, + flags: Some(PbFlags { + discard: false, + forwarded: false, + repair: false, + simple_vote_tx: false, + tracer_packet: false, + }), + sender_stake: 0, + }), + } +} + +#[bench] +fn bench_proto_to_packet(bencher: &mut Bencher) { + bencher.iter(|| { + black_box(proto_packet_to_packet(get_proto_packet(1))); + }); +} + +#[bench] +fn bench_batch_list_to_packets(bencher: &mut Bencher) { + let packet_batch = PacketBatch { + packets: (0..128).map(get_proto_packet).collect(), + }; + + bencher.iter(|| { + black_box( + packet_batch + .packets + .iter() + .map(|p| proto_packet_to_packet(p.clone())) + .collect::>(), + ); + }); +} diff --git a/core/benches/retransmit_stage.rs b/core/benches/retransmit_stage.rs index bad02d043a..c6bec24fbd 100644 --- a/core/benches/retransmit_stage.rs +++ b/core/benches/retransmit_stage.rs @@ -121,6 +121,7 @@ fn bench_retransmitter(bencher: &mut Bencher) { shreds_receiver, Arc::default(), // solana_rpc::max_slots::MaxSlots None, + None, ); let mut index = 0; diff --git a/core/src/backoff.rs b/core/src/backoff.rs new file mode 100644 index 0000000000..223b3d30ac --- /dev/null +++ b/core/src/backoff.rs @@ -0,0 +1,45 @@ +// BackoffStrategy currently implements a simple +// Fibonacci backoff strategy with hardcoded values. +// Currently the only use case is for retrying long lived +// connection loops in recv_verify_stage, as use cases +// expand more strategies will be added. + +use std::cmp::min; + +const INITIAL_LAST_WAIT: u64 = 0; +const INITIAL_CUR_WAIT: u64 = 100; +const MAX_WAIT: u64 = 1000; + +#[derive(Copy, Clone)] +pub struct BackoffStrategy { + // Wait times in ms + last_wait: u64, + cur_wait: u64, +} + +impl Default for BackoffStrategy { + fn default() -> Self { + Self::new() + } +} + +impl BackoffStrategy { + pub fn new() -> BackoffStrategy { + BackoffStrategy { + last_wait: INITIAL_LAST_WAIT, + cur_wait: INITIAL_CUR_WAIT, + } + } + + pub fn next_wait(&mut self) -> u64 { + let next_wait = min(self.cur_wait + self.last_wait, MAX_WAIT); + self.last_wait = self.cur_wait; + self.cur_wait = next_wait; + next_wait + } + + pub fn reset(&mut self) { + self.last_wait = INITIAL_LAST_WAIT; + self.cur_wait = INITIAL_CUR_WAIT; + } +} diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index 70105973de..c10b625e84 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -4,6 +4,7 @@ use { crate::{ + bundle_account_locker::BundleAccountLocker, forward_packet_batches_by_accounts::ForwardPacketBatchesByAccounts, immutable_deserialized_packet::ImmutableDeserializedPacket, leader_slot_banking_stage_metrics::{LeaderSlotMetricsTracker, ProcessTransactionsSummary}, @@ -65,7 +66,7 @@ use { solana_transaction_status::token_balances::TransactionTokenBalancesSet, std::{ cmp, - collections::HashMap, + collections::{HashMap, HashSet}, env, net::{SocketAddr, UdpSocket}, sync::{ @@ -403,6 +404,8 @@ impl BankingStage { log_messages_bytes_limit: Option, connection_cache: Arc, bank_forks: Arc>, + blacklisted_accounts: HashSet, + bundle_account_locker: BundleAccountLocker, ) -> Self { Self::new_num_threads( cluster_info, @@ -417,6 +420,8 @@ impl BankingStage { log_messages_bytes_limit, connection_cache, bank_forks, + blacklisted_accounts, + bundle_account_locker, ) } @@ -434,6 +439,8 @@ impl BankingStage { log_messages_bytes_limit: Option, connection_cache: Arc, bank_forks: Arc>, + blacklisted_accounts: HashSet, + bundle_account_locker: BundleAccountLocker, ) -> Self { assert!(num_threads >= MIN_TOTAL_THREADS); // Single thread to generate entries from many banks. @@ -467,6 +474,9 @@ impl BankingStage { let data_budget = data_budget.clone(); let cost_model = cost_model.clone(); let connection_cache = connection_cache.clone(); + let blacklisted_accounts = blacklisted_accounts.clone(); + let bundle_account_locker = bundle_account_locker.clone(); + let bank_forks = bank_forks.clone(); Builder::new() .name(format!("solBanknStgTx{:02}", i)) @@ -486,6 +496,8 @@ impl BankingStage { log_messages_bytes_limit, connection_cache, &bank_forks, + blacklisted_accounts, + bundle_account_locker, ); }) .unwrap() @@ -604,12 +616,15 @@ impl BankingStage { slot_metrics_tracker: &mut LeaderSlotMetricsTracker, num_packets_to_process_per_iteration: usize, log_messages_bytes_limit: Option, + blacklisted_accounts: &HashSet, + bundle_account_locker: &BundleAccountLocker, ) { let mut rebuffered_packet_count = 0; let mut consumed_buffered_packets_count = 0; let buffered_packets_len = buffered_packet_batches.len(); let mut proc_start = Measure::start("consume_buffered_process"); let mut reached_end_of_slot = false; + let mut retryable_packets = { let capacity = buffered_packet_batches.capacity(); std::mem::replace( @@ -651,7 +666,9 @@ impl BankingStage { banking_stage_stats, qos_service, slot_metrics_tracker, - log_messages_bytes_limit + log_messages_bytes_limit, + blacklisted_accounts, + bundle_account_locker, ), "process_packets_transactions", ); @@ -806,6 +823,8 @@ impl BankingStage { connection_cache: &ConnectionCache, tracer_packet_stats: &mut TracerPacketStats, bank_forks: &Arc>, + blacklisted_accounts: &HashSet, + bundle_account_locker: &BundleAccountLocker, ) { let ((metrics_action, decision), make_decision_time) = measure!( { @@ -865,7 +884,9 @@ impl BankingStage { qos_service, slot_metrics_tracker, UNPROCESSED_BUFFER_STEP_SIZE, - log_messages_bytes_limit + log_messages_bytes_limit, + blacklisted_accounts, + bundle_account_locker ), "consume_buffered_packets", ); @@ -1329,6 +1350,8 @@ impl BankingStage { log_messages_bytes_limit: Option, connection_cache: Arc, bank_forks: &Arc>, + blacklisted_accounts: HashSet, + bundle_account_locker: BundleAccountLocker, ) { let recorder = poh_recorder.read().unwrap().recorder(); let socket = UdpSocket::bind("0.0.0.0:0").unwrap(); @@ -1364,6 +1387,8 @@ impl BankingStage { &connection_cache, &mut tracer_packet_stats, bank_forks, + &blacklisted_accounts, + &bundle_account_locker, ), "process_buffered_packets", ); @@ -1435,8 +1460,10 @@ impl BankingStage { let (hash, hash_time) = measure!(hash_transactions(&transactions), "hash"); record_transactions_timings.hash_us = hash_time.as_us(); - let (res, poh_record_time) = - measure!(recorder.record(bank_slot, hash, transactions), "hash"); + let (res, poh_record_time) = measure!( + recorder.record(bank_slot, vec![(hash, transactions)]), + "hash" + ); record_transactions_timings.poh_record_us = poh_record_time.as_us(); match res { @@ -1490,7 +1517,7 @@ impl BankingStage { }; let pre_token_balances = if transaction_status_sender.is_some() { - collect_token_balances(bank, batch, &mut mint_decimals) + collect_token_balances(bank, batch, &mut mint_decimals, None) } else { vec![] }; @@ -1635,7 +1662,7 @@ impl BankingStage { let txs = batch.sanitized_transactions().to_vec(); let post_balances = bank.collect_balances(batch); let post_token_balances = - collect_token_balances(bank, batch, &mut mint_decimals); + collect_token_balances(bank, batch, &mut mint_decimals, None); let mut transaction_index = starting_transaction_index.unwrap_or_default(); let batch_transaction_indexes: Vec<_> = tx_results .execution_results @@ -1716,6 +1743,7 @@ impl BankingStage { gossip_vote_sender: &ReplayVoteSender, qos_service: &QosService, log_messages_bytes_limit: Option, + bundle_account_locker: &BundleAccountLocker, ) -> ProcessTransactionBatchOutput { let mut cost_model_time = Measure::start("cost_model"); @@ -1736,9 +1764,20 @@ impl BankingStage { // Only lock accounts for those transactions are selected for the block; // Once accounts are locked, other threads cannot encode transactions that will modify the - // same account state + // same account state. let mut lock_time = Measure::start("lock_time"); - let batch = bank.prepare_sanitized_batch_with_results(txs, transactions_qos_results.iter()); + + let batch = { + // BundleStage locks ALL accounts in ALL transactions in a bundle to avoid race + // conditions with BankingStage + let account_locks = bundle_account_locker.account_locks(); + bank.prepare_sanitized_batch_with_results( + txs, + transactions_qos_results.iter(), + &account_locks.read_locks(), + &account_locks.write_locks(), + ) + }; lock_time.stop(); // retryable_txs includes AccountInUse, WouldExceedMaxBlockCostLimit @@ -1909,6 +1948,7 @@ impl BankingStage { gossip_vote_sender: &ReplayVoteSender, qos_service: &QosService, log_messages_bytes_limit: Option, + bundle_account_locker: &BundleAccountLocker, ) -> ProcessTransactionsSummary { let mut chunk_start = 0; let mut all_retryable_tx_indexes = vec![]; @@ -1941,6 +1981,7 @@ impl BankingStage { gossip_vote_sender, qos_service, log_messages_bytes_limit, + bundle_account_locker, ); let ProcessTransactionBatchOutput { @@ -2136,6 +2177,8 @@ impl BankingStage { qos_service: &'a QosService, slot_metrics_tracker: &'a mut LeaderSlotMetricsTracker, log_messages_bytes_limit: Option, + blacklisted_accounts: &HashSet, + bundle_account_locker: &BundleAccountLocker, ) -> ProcessTransactionsSummary { // Convert packets to transactions let ((transactions, transaction_to_packet_indexes), packet_conversion_time): ( @@ -2151,6 +2194,12 @@ impl BankingStage { bank.vote_only_bank(), bank.as_ref(), ) + .filter(|tx| { + !tx.message() + .account_keys() + .iter() + .any(|acc| blacklisted_accounts.contains(acc)) + }) .map(|transaction| (transaction, i)) }) .unzip(), @@ -2175,6 +2224,7 @@ impl BankingStage { gossip_vote_sender, qos_service, log_messages_bytes_limit, + bundle_account_locker ), "process_transaction_time", ); @@ -2396,7 +2446,7 @@ mod tests { super::*, crossbeam_channel::{unbounded, Receiver}, solana_address_lookup_table_program::state::{AddressLookupTable, LookupTableMeta}, - solana_entry::entry::{next_entry, next_versioned_entry, Entry, EntrySlice}, + solana_entry::entry::{next_entry, next_versioned_entry, EntrySlice}, solana_gossip::{cluster_info::Node, contact_info::ContactInfo}, solana_ledger::{ blockstore::{entries_to_test_shreds, Blockstore}, @@ -2466,6 +2516,8 @@ mod tests { let cluster_info = Arc::new(cluster_info); let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let banking_stage = BankingStage::new( &cluster_info, &poh_recorder, @@ -2478,6 +2530,8 @@ mod tests { None, Arc::new(ConnectionCache::default()), bank_forks, + HashSet::default(), + bundle_locker, ); drop(verified_sender); drop(gossip_verified_vote_sender); @@ -2492,6 +2546,7 @@ mod tests { #[test] fn test_banking_stage_tick() { solana_logger::setup(); + let GenesisConfigInfo { mut genesis_config, .. } = create_genesis_config(2); @@ -2520,6 +2575,8 @@ mod tests { let (verified_gossip_vote_sender, verified_gossip_vote_receiver) = unbounded(); let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let banking_stage = BankingStage::new( &cluster_info, &poh_recorder, @@ -2532,6 +2589,8 @@ mod tests { None, Arc::new(ConnectionCache::default()), bank_forks, + HashSet::default(), + bundle_locker, ); trace!("sending bank"); drop(verified_sender); @@ -2544,7 +2603,12 @@ mod tests { trace!("getting entries"); let entries: Vec<_> = entry_receiver .iter() - .map(|(_bank, (entry, _tick_height))| entry) + .flat_map( + |WorkingBankEntry { + bank: _, + entries_ticks, + }| entries_ticks.into_iter().map(|e| e.0), + ) .collect(); trace!("done"); assert_eq!(entries.len(), genesis_config.ticks_per_slot as usize); @@ -2569,6 +2633,7 @@ mod tests { #[test] fn test_banking_stage_entries_only() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -2599,6 +2664,7 @@ mod tests { let cluster_info = Arc::new(cluster_info); let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); let banking_stage = BankingStage::new( &cluster_info, &poh_recorder, @@ -2611,6 +2677,8 @@ mod tests { None, Arc::new(ConnectionCache::default()), bank_forks, + HashSet::default(), + bundle_locker, ); // fund another account so we can send 2 good transactions in a single batch. @@ -2662,9 +2730,14 @@ mod tests { bank.process_transaction(&fund_tx).unwrap(); //receive entries + ticks loop { - let entries: Vec = entry_receiver + let entries: Vec<_> = entry_receiver .iter() - .map(|(_bank, (entry, _tick_height))| entry) + .flat_map( + |WorkingBankEntry { + bank: _, + entries_ticks, + }| entries_ticks.into_iter().map(|e| e.0), + ) .collect(); assert!(entries.verify(&blockhash)); @@ -2695,6 +2768,7 @@ mod tests { #[test] fn test_banking_stage_entryfication() { solana_logger::setup(); + // In this attack we'll demonstrate that a verifier can interpret the ledger // differently if either the server doesn't signal the ledger to add an // Entry OR if the verifier tries to parallelize across multiple Entries. @@ -2754,6 +2828,9 @@ mod tests { create_test_recorder(&bank, &blockstore, Some(poh_config), None); let cluster_info = new_test_cluster_info(Node::new_localhost().info); let cluster_info = Arc::new(cluster_info); + + let bundle_locker = BundleAccountLocker::default(); + let _banking_stage = BankingStage::new_num_threads( &cluster_info, &poh_recorder, @@ -2767,6 +2844,8 @@ mod tests { None, Arc::new(ConnectionCache::default()), bank_forks, + HashSet::default(), + bundle_locker, ); // wait for banking_stage to eat the packets @@ -2785,7 +2864,12 @@ mod tests { // check that the balance is what we expect. let entries: Vec<_> = entry_receiver .iter() - .map(|(_bank, (entry, _tick_height))| entry) + .flat_map( + |WorkingBankEntry { + bank: _, + entries_ticks, + }| entries_ticks.into_iter().map(|e| e.0), + ) .collect(); let bank = Bank::new_no_wallclock_throttle_for_tests(&genesis_config); @@ -2853,7 +2937,12 @@ mod tests { ]; let _ = BankingStage::record_transactions(bank.slot(), txs.clone(), &recorder); - let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap(); + let WorkingBankEntry { + bank: _, + entries_ticks, + } = entry_receiver.recv().unwrap(); + assert_eq!(entries_ticks.len(), 1); + let entry = entries_ticks.get(0).unwrap().0.clone(); assert_eq!(entry.transactions, txs); // Once bank is set to a new bank (setting bank.slot() + 1 in record_transactions), @@ -2885,7 +2974,7 @@ mod tests { Ok(()), Err(TransactionError::BlockhashNotFound), Ok(()), - Ok(()) + Ok(()), ] ); @@ -2914,7 +3003,7 @@ mod tests { (Ok(()), None), (Ok(()), None), ], - &[2, 4, 5, 9, 11, 13] + &[2, 4, 5, 9, 11, 13], ), [5, 11, 13] ); @@ -2929,7 +3018,7 @@ mod tests { (Ok(()), None), (Ok(()), None), ], - &[1, 6, 7, 9, 31, 43] + &[1, 6, 7, 9, 31, 43], ), [1, 9, 31, 43] ); @@ -3027,6 +3116,7 @@ mod tests { #[test] fn test_bank_process_and_record_transactions() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -3066,6 +3156,8 @@ mod tests { poh_recorder.write().unwrap().set_bank(&bank, false); let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let process_transactions_batch_output = BankingStage::process_and_record_transactions( &bank, &transactions, @@ -3075,6 +3167,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); let ExecuteAndCommitTransactionsOutput { @@ -3097,7 +3190,13 @@ mod tests { let mut done = false; // read entries until I find mine, might be ticks... - while let Ok((_bank, (entry, _tick_height))) = entry_receiver.recv() { + while let Ok(WorkingBankEntry { + bank: _, + entries_ticks, + }) = entry_receiver.recv() + { + assert_eq!(entries_ticks.len(), 1); + let entry = entries_ticks.get(0).unwrap().0.clone(); if !entry.is_tick() { trace!("got entry"); assert_eq!(entry.transactions.len(), transactions.len()); @@ -3119,6 +3218,8 @@ mod tests { genesis_config.hash(), )]); + let bundle_locker = BundleAccountLocker::default(); + let process_transactions_batch_output = BankingStage::process_and_record_transactions( &bank, &transactions, @@ -3128,6 +3229,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); let ExecuteAndCommitTransactionsOutput { @@ -3163,6 +3265,7 @@ mod tests { #[test] fn test_bank_process_and_record_transactions_all_unexecuted() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -3203,6 +3306,8 @@ mod tests { poh_recorder.write().unwrap().set_bank(&bank, false); let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let process_transactions_batch_output = BankingStage::process_and_record_transactions( &bank, &transactions, @@ -3212,6 +3317,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); let ExecuteAndCommitTransactionsOutput { @@ -3245,6 +3351,7 @@ mod tests { #[test] fn test_bank_process_and_record_transactions_cost_tracker() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -3295,6 +3402,8 @@ mod tests { genesis_config.hash(), )]); + let bundle_locker = BundleAccountLocker::default(); + let process_transactions_batch_output = BankingStage::process_and_record_transactions( &bank, &transactions, @@ -3304,6 +3413,7 @@ mod tests { &gossip_vote_sender, &qos_service, None, + &bundle_locker, ); let ExecuteAndCommitTransactionsOutput { @@ -3344,6 +3454,7 @@ mod tests { &gossip_vote_sender, &qos_service, None, + &bundle_locker, ); let ExecuteAndCommitTransactionsOutput { @@ -3393,6 +3504,7 @@ mod tests { #[test] fn test_bank_process_and_record_transactions_account_in_use() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -3431,6 +3543,7 @@ mod tests { let poh_simulator = simulate_poh(record_receiver, &poh_recorder); let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); let process_transactions_batch_output = BankingStage::process_and_record_transactions( &bank, @@ -3441,6 +3554,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); poh_recorder @@ -3607,6 +3721,7 @@ mod tests { #[test] fn test_process_transactions_returns_unprocessed_txs() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -3648,6 +3763,8 @@ mod tests { let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let process_transactions_summary = BankingStage::process_transactions( &bank, &Instant::now(), @@ -3657,6 +3774,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); let ProcessTransactionsSummary { @@ -3715,6 +3833,8 @@ mod tests { let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let process_transactions_summary = BankingStage::process_transactions( &bank, &Instant::now(), @@ -3724,6 +3844,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); poh_recorder @@ -3797,6 +3918,7 @@ mod tests { (1..transactions_count - 1).collect::>() ); } + #[test] fn test_process_transactions_account_in_use() { solana_logger::setup(); @@ -3817,7 +3939,7 @@ mod tests { &mint_keypair, &Pubkey::new_unique(), 1, - genesis_config.hash() + genesis_config.hash(), ); MAX_NUM_TRANSACTIONS_PER_BATCH ]; @@ -3859,6 +3981,7 @@ mod tests { #[test] fn test_write_persist_transaction_status() { solana_logger::setup(); + let GenesisConfigInfo { mut genesis_config, mint_keypair, @@ -3943,6 +4066,8 @@ mod tests { let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let _ = BankingStage::process_and_record_transactions( &bank, &transactions, @@ -3954,6 +4079,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); transaction_status_service.join().unwrap(); @@ -4020,6 +4146,7 @@ mod tests { #[test] fn test_write_persist_loaded_addresses() { solana_logger::setup(); + let GenesisConfigInfo { genesis_config, mint_keypair, @@ -4112,6 +4239,8 @@ mod tests { let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + let _ = BankingStage::process_and_record_transactions( &bank, &[sanitized_tx.clone()], @@ -4123,6 +4252,7 @@ mod tests { &gossip_vote_sender, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), None, + &bundle_locker, ); transaction_status_service.join().unwrap(); @@ -4230,6 +4360,8 @@ mod tests { let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let bundle_locker = BundleAccountLocker::default(); + // When the working bank in poh_recorder is None, no packets should be processed assert!(!poh_recorder.read().unwrap().has_bank()); let max_tx_processing_ns = std::u128::MAX; @@ -4247,6 +4379,8 @@ mod tests { &mut LeaderSlotMetricsTracker::new(0), num_conflicting_transactions, None, + &HashSet::default(), + &bundle_locker, ); assert_eq!(buffered_packet_batches.len(), num_conflicting_transactions); // When the poh recorder has a bank, should process all non conflicting buffered packets. @@ -4268,6 +4402,8 @@ mod tests { &mut LeaderSlotMetricsTracker::new(0), num_packets_to_process_per_iteration, None, + &HashSet::default(), + &bundle_locker, ); if num_expected_unprocessed == 0 { assert!(buffered_packet_batches.is_empty()) @@ -4328,6 +4464,9 @@ mod tests { .iter() .map(|packet| *packet.immutable_section().message_hash()) .collect(); + + let bundle_locker = BundleAccountLocker::default(); + BankingStage::consume_buffered_packets( &Pubkey::default(), std::u128::MAX, @@ -4342,6 +4481,8 @@ mod tests { &mut LeaderSlotMetricsTracker::new(0), num_packets_to_process_per_iteration, None, + &HashSet::default(), + &bundle_locker, ); // Check everything is correct. All indexes after `interrupted_iteration` @@ -4740,4 +4881,7 @@ mod tests { BankingStage::filter_processed_packets(retryable_indexes.iter(), f); assert_eq!(non_retryable_indexes, vec![(0, 1), (4, 5), (6, 8)]); } + + // TODO (LB): test that banking stage doesn't process packets that contain accounts + // in BundleAccountLocker } diff --git a/core/src/broadcast_stage.rs b/core/src/broadcast_stage.rs index a42c8a6885..1d34ec7ac8 100644 --- a/core/src/broadcast_stage.rs +++ b/core/src/broadcast_stage.rs @@ -35,7 +35,7 @@ use { }, std::{ collections::{HashMap, HashSet}, - net::UdpSocket, + net::{SocketAddr, UdpSocket}, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, RwLock, @@ -86,6 +86,7 @@ impl BroadcastStageType { blockstore: Arc, bank_forks: Arc>, shred_version: u16, + shred_receiver_addr: Option, ) -> BroadcastStage { match self { BroadcastStageType::Standard => BroadcastStage::new( @@ -97,6 +98,7 @@ impl BroadcastStageType { blockstore, bank_forks, StandardBroadcastRun::new(shred_version), + shred_receiver_addr, ), BroadcastStageType::FailEntryVerification => BroadcastStage::new( @@ -108,6 +110,7 @@ impl BroadcastStageType { blockstore, bank_forks, FailEntryVerificationBroadcastRun::new(shred_version), + None, ), BroadcastStageType::BroadcastFakeShreds => BroadcastStage::new( @@ -119,6 +122,7 @@ impl BroadcastStageType { blockstore, bank_forks, BroadcastFakeShredsRun::new(0, shred_version), + None, ), BroadcastStageType::BroadcastDuplicates(config) => BroadcastStage::new( @@ -130,6 +134,7 @@ impl BroadcastStageType { blockstore, bank_forks, BroadcastDuplicatesRun::new(shred_version, config.clone()), + None, ), } } @@ -150,6 +155,7 @@ trait BroadcastRun { cluster_info: &ClusterInfo, sock: &UdpSocket, bank_forks: &RwLock, + shred_receiver_addr: Option, ) -> Result<()>; fn record(&mut self, receiver: &Mutex, blockstore: &Blockstore) -> Result<()>; } @@ -245,6 +251,7 @@ impl BroadcastStage { blockstore: Arc, bank_forks: Arc>, broadcast_stage_run: impl BroadcastRun + Send + 'static + Clone, + shred_receiver_addr: Option, ) -> Self { let (socket_sender, socket_receiver) = unbounded(); let (blockstore_sender, blockstore_receiver) = unbounded(); @@ -279,8 +286,13 @@ impl BroadcastStage { let t = Builder::new() .name("solBroadcastTx".to_string()) .spawn(move || loop { - let res = - bs_transmit.transmit(&socket_receiver, &cluster_info, &sock, &bank_forks); + let res = bs_transmit.transmit( + &socket_receiver, + &cluster_info, + &sock, + &bank_forks, + shred_receiver_addr, + ); let res = Self::handle_error(res, "solana-broadcaster-transmit"); if let Some(res) = res { return res; @@ -396,6 +408,7 @@ pub fn broadcast_shreds( cluster_info: &ClusterInfo, bank_forks: &RwLock, socket_addr_space: &SocketAddrSpace, + shred_receiver_addr: Option, ) -> Result<()> { let mut result = Ok(()); let mut shred_select = Measure::start("shred_select"); @@ -405,18 +418,23 @@ pub fn broadcast_shreds( }; let packets: Vec<_> = shreds .iter() - .group_by(|shred| shred.slot()) - .into_iter() - .flat_map(|(slot, shreds)| { - let cluster_nodes = - cluster_nodes_cache.get(slot, &root_bank, &working_bank, cluster_info); - update_peer_stats(&cluster_nodes, last_datapoint_submit); - shreds.flat_map(move |shred| { - let node = cluster_nodes.get_broadcast_peer(&shred.id())?; - ContactInfo::is_valid_address(&node.tvu, socket_addr_space) - .then(|| (shred.payload(), node.tvu)) - }) - }) + .filter_map(|s| Some((s.payload(), shred_receiver_addr?))) + .chain( + shreds + .iter() + .group_by(|shred| shred.slot()) + .into_iter() + .flat_map(|(slot, shreds)| { + let cluster_nodes = + cluster_nodes_cache.get(slot, &root_bank, &working_bank, cluster_info); + update_peer_stats(&cluster_nodes, last_datapoint_submit); + shreds.flat_map(move |shred| { + let node = cluster_nodes.get_broadcast_peer(&shred.id())?; + ContactInfo::is_valid_address(&node.tvu, socket_addr_space) + .then(|| (shred.payload(), node.tvu)) + }) + }), + ) .collect(); shred_select.stop(); transmit_stats.shred_select += shred_select.as_us(); @@ -617,6 +635,7 @@ pub mod test { blockstore.clone(), bank_forks, StandardBroadcastRun::new(0), + None, ); MockBroadcastStage { @@ -656,7 +675,10 @@ pub mod test { let ticks = create_ticks(max_tick_height - start_tick_height, 0, Hash::default()); for (i, tick) in ticks.into_iter().enumerate() { entry_sender - .send((bank.clone(), (tick, i as u64 + 1))) + .send(WorkingBankEntry { + bank: bank.clone(), + entries_ticks: vec![(tick, i as u64 + 1)], + }) .expect("Expect successful send to broadcast service"); } } diff --git a/core/src/broadcast_stage/broadcast_duplicates_run.rs b/core/src/broadcast_stage/broadcast_duplicates_run.rs index 0315a343b9..3aee2d123d 100644 --- a/core/src/broadcast_stage/broadcast_duplicates_run.rs +++ b/core/src/broadcast_stage/broadcast_duplicates_run.rs @@ -10,7 +10,7 @@ use { signature::{Keypair, Signature, Signer}, system_transaction, }, - std::collections::HashSet, + std::{collections::HashSet, net::SocketAddr}, }; pub const MINIMUM_DUPLICATE_SLOT: Slot = 20; @@ -266,6 +266,7 @@ impl BroadcastRun for BroadcastDuplicatesRun { cluster_info: &ClusterInfo, sock: &UdpSocket, bank_forks: &RwLock, + _shred_receiver_addr: Option, ) -> Result<()> { let (shreds, _) = receiver.lock().unwrap().recv()?; if shreds.is_empty() { diff --git a/core/src/broadcast_stage/broadcast_fake_shreds_run.rs b/core/src/broadcast_stage/broadcast_fake_shreds_run.rs index 69aaf410af..867a424f91 100644 --- a/core/src/broadcast_stage/broadcast_fake_shreds_run.rs +++ b/core/src/broadcast_stage/broadcast_fake_shreds_run.rs @@ -3,6 +3,7 @@ use { solana_entry::entry::Entry, solana_ledger::shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, solana_sdk::{hash::Hash, signature::Keypair}, + std::net::SocketAddr, }; #[derive(Clone)] @@ -132,6 +133,7 @@ impl BroadcastRun for BroadcastFakeShredsRun { cluster_info: &ClusterInfo, sock: &UdpSocket, _bank_forks: &RwLock, + _shred_receiver_addr: Option, ) -> Result<()> { for (data_shreds, batch_info) in receiver.lock().unwrap().iter() { let fake = batch_info.is_some(); diff --git a/core/src/broadcast_stage/broadcast_utils.rs b/core/src/broadcast_stage/broadcast_utils.rs index f9485d59a9..6150bf4fec 100644 --- a/core/src/broadcast_stage/broadcast_utils.rs +++ b/core/src/broadcast_stage/broadcast_utils.rs @@ -36,13 +36,22 @@ pub(super) fn recv_slot_entries(receiver: &Receiver) -> Result 32 * ShredData::capacity(/*merkle_proof_size*/ None).unwrap() as u64; let timer = Duration::new(1, 0); let recv_start = Instant::now(); - let (mut bank, (entry, mut last_tick_height)) = receiver.recv_timeout(timer)?; - let mut entries = vec![entry]; + + let WorkingBankEntry { + mut bank, + entries_ticks, + } = receiver.recv_timeout(timer)?; + let mut last_tick_height = entries_ticks.iter().last().unwrap().1; + let mut entries: Vec = entries_ticks.into_iter().map(|(e, _)| e).collect(); + assert!(last_tick_height <= bank.max_tick_height()); // Drain channel while last_tick_height != bank.max_tick_height() { - let (try_bank, (entry, tick_height)) = match receiver.try_recv() { + let WorkingBankEntry { + bank: try_bank, + entries_ticks: new_entries_ticks, + } = match receiver.try_recv() { Ok(working_bank_entry) => working_bank_entry, Err(_) => break, }; @@ -53,8 +62,8 @@ pub(super) fn recv_slot_entries(receiver: &Receiver) -> Result entries.clear(); bank = try_bank; } - last_tick_height = tick_height; - entries.push(entry); + last_tick_height = new_entries_ticks.iter().last().unwrap().1; + entries.extend(new_entries_ticks.into_iter().map(|(entry, _)| entry)); assert!(last_tick_height <= bank.max_tick_height()); } @@ -65,11 +74,13 @@ pub(super) fn recv_slot_entries(receiver: &Receiver) -> Result while last_tick_height != bank.max_tick_height() && serialized_batch_byte_count < target_serialized_batch_byte_count { - let (try_bank, (entry, tick_height)) = - match receiver.recv_deadline(coalesce_start + ENTRY_COALESCE_DURATION) { - Ok(working_bank_entry) => working_bank_entry, - Err(_) => break, - }; + let WorkingBankEntry { + bank: try_bank, + entries_ticks: new_entries_ticks, + } = match receiver.recv_deadline(coalesce_start + ENTRY_COALESCE_DURATION) { + Ok(working_bank_entry) => working_bank_entry, + Err(_) => break, + }; // If the bank changed, that implies the previous slot was interrupted and we do not have to // broadcast its entries. if try_bank.slot() != bank.slot() { @@ -79,10 +90,12 @@ pub(super) fn recv_slot_entries(receiver: &Receiver) -> Result bank = try_bank; coalesce_start = Instant::now(); } - last_tick_height = tick_height; - let entry_bytes = serialized_size(&entry)?; - serialized_batch_byte_count += entry_bytes; - entries.push(entry); + last_tick_height = new_entries_ticks.iter().last().unwrap().1; + + for (entry, _) in &new_entries_ticks { + serialized_batch_byte_count += serialized_size(entry)?; + } + entries.extend(new_entries_ticks.into_iter().map(|(entry, _)| entry)); assert!(last_tick_height <= bank.max_tick_height()); } let time_coalesced = coalesce_start.elapsed(); @@ -139,7 +152,11 @@ mod tests { .map(|i| { let entry = Entry::new(&last_hash, 1, vec![tx.clone()]); last_hash = entry.hash; - s.send((bank1.clone(), (entry.clone(), i))).unwrap(); + s.send(WorkingBankEntry { + bank: bank1.clone(), + entries_ticks: vec![(entry.clone(), i)], + }) + .unwrap(); entry }) .collect(); @@ -173,11 +190,18 @@ mod tests { last_hash = entry.hash; // Interrupt slot 1 right before the last tick if tick_height == expected_last_height { - s.send((bank2.clone(), (entry.clone(), tick_height))) - .unwrap(); + s.send(WorkingBankEntry { + bank: bank2.clone(), + entries_ticks: vec![(entry.clone(), tick_height)], + }) + .unwrap(); Some(entry) } else { - s.send((bank1.clone(), (entry, tick_height))).unwrap(); + s.send(WorkingBankEntry { + bank: bank1.clone(), + entries_ticks: vec![(entry, tick_height)], + }) + .unwrap(); None } }) diff --git a/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs b/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs index e7b899ab0f..57cb9532da 100644 --- a/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs +++ b/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs @@ -3,7 +3,7 @@ use { crate::cluster_nodes::ClusterNodesCache, solana_ledger::shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, solana_sdk::{hash::Hash, signature::Keypair}, - std::{thread::sleep, time::Duration}, + std::{net::SocketAddr, thread::sleep, time::Duration}, }; pub const NUM_BAD_SLOTS: u64 = 10; @@ -162,6 +162,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun { cluster_info: &ClusterInfo, sock: &UdpSocket, bank_forks: &RwLock, + shred_receiver_addr: Option, ) -> Result<()> { let (shreds, _) = receiver.lock().unwrap().recv()?; broadcast_shreds( @@ -173,6 +174,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun { cluster_info, bank_forks, cluster_info.socket_addr_space(), + shred_receiver_addr, ) } fn record(&mut self, receiver: &Mutex, blockstore: &Blockstore) -> Result<()> { diff --git a/core/src/broadcast_stage/standard_broadcast_run.rs b/core/src/broadcast_stage/standard_broadcast_run.rs index f58ab368e9..ea8a8b62b4 100644 --- a/core/src/broadcast_stage/standard_broadcast_run.rs +++ b/core/src/broadcast_stage/standard_broadcast_run.rs @@ -15,7 +15,7 @@ use { signature::Keypair, timing::{duration_as_us, AtomicInterval}, }, - std::{sync::RwLock, time::Duration}, + std::{net::SocketAddr, sync::RwLock, time::Duration}, }; #[derive(Clone)] @@ -180,10 +180,10 @@ impl StandardBroadcastRun { let brecv = Arc::new(Mutex::new(brecv)); //data - let _ = self.transmit(&srecv, cluster_info, sock, bank_forks); + let _ = self.transmit(&srecv, cluster_info, sock, bank_forks, None); let _ = self.record(&brecv, blockstore); //coding - let _ = self.transmit(&srecv, cluster_info, sock, bank_forks); + let _ = self.transmit(&srecv, cluster_info, sock, bank_forks, None); let _ = self.record(&brecv, blockstore); Ok(()) } @@ -378,6 +378,7 @@ impl StandardBroadcastRun { shreds: Arc>, broadcast_shred_batch_info: Option, bank_forks: &RwLock, + shred_receiver_addr: Option, ) -> Result<()> { trace!("Broadcasting {:?} shreds", shreds.len()); let mut transmit_stats = TransmitShredsStats::default(); @@ -393,6 +394,7 @@ impl StandardBroadcastRun { cluster_info, bank_forks, cluster_info.socket_addr_space(), + shred_receiver_addr, )?; transmit_time.stop(); @@ -462,9 +464,17 @@ impl BroadcastRun for StandardBroadcastRun { cluster_info: &ClusterInfo, sock: &UdpSocket, bank_forks: &RwLock, + shred_receiver_addr: Option, ) -> Result<()> { let (shreds, batch_info) = receiver.lock().unwrap().recv()?; - self.broadcast(sock, cluster_info, shreds, batch_info, bank_forks) + self.broadcast( + sock, + cluster_info, + shreds, + batch_info, + bank_forks, + shred_receiver_addr, + ) } fn record(&mut self, receiver: &Mutex, blockstore: &Blockstore) -> Result<()> { let (shreds, slot_start_ts) = receiver.lock().unwrap().recv()?; diff --git a/core/src/bundle_account_locker.rs b/core/src/bundle_account_locker.rs new file mode 100644 index 0000000000..955a4dd170 --- /dev/null +++ b/core/src/bundle_account_locker.rs @@ -0,0 +1,334 @@ +///! Handles pre-locking bundle accounts so that accounts bundles touch can be reserved ahead +/// of time for execution. Also, ensures that ALL accounts mentioned across a bundle are locked +/// to avoid race conditions between BundleStage and BankingStage. +/// +/// For instance, imagine a bundle with three transactions and the set of accounts for each transaction +/// is: {{A, B}, {B, C}, {C, D}}. We need to lock A, B, and C even though only one is executed at a time. +/// Imagine BundleStage is in the middle of processing {C, D} and we didn't have a lock on accounts {A, B, C}. +/// In this situation, there's a chance that BankingStage can process a transaction containing A or B +/// and commit the results before the bundle completes. By the time the bundle commits the new account +/// state for {A, B, C}, A and B would be incorrect and the entries containing the bundle would be +/// replayed improperly and that leader would have produced an invalid block. +use { + solana_runtime::bank::Bank, + solana_sdk::{ + bundle::sanitized::SanitizedBundle, pubkey::Pubkey, transaction::TransactionAccountLocks, + }, + std::collections::{hash_map::Entry, HashMap, HashSet}, + std::sync::{Arc, Mutex, MutexGuard}, +}; + +#[derive(Debug)] +pub enum BundleAccountLockerError { + LockingError, +} + +pub type BundleAccountLockerResult = Result; + +pub struct LockedBundle<'a, 'b> { + bundle_account_locker: &'a BundleAccountLocker, + sanitized_bundle: &'b SanitizedBundle, + bank: Arc, +} + +impl<'a, 'b> LockedBundle<'a, 'b> { + pub fn new( + bundle_account_locker: &'a BundleAccountLocker, + sanitized_bundle: &'b SanitizedBundle, + bank: &Arc, + ) -> Self { + Self { + bundle_account_locker, + sanitized_bundle, + bank: bank.clone(), + } + } + + pub fn sanitized_bundle(&self) -> &SanitizedBundle { + self.sanitized_bundle + } +} + +// Automatically unlock bundle accounts when destructed +impl<'a, 'b> Drop for LockedBundle<'a, 'b> { + fn drop(&mut self) { + let _ = self + .bundle_account_locker + .unlock_bundle_accounts(self.sanitized_bundle, &self.bank); + } +} + +#[derive(Default, Clone)] +pub struct BundleAccountLocks { + read_locks: HashMap, + write_locks: HashMap, +} + +impl BundleAccountLocks { + pub fn read_locks(&self) -> HashSet { + self.read_locks.keys().cloned().collect() + } + + pub fn write_locks(&self) -> HashSet { + self.write_locks.keys().cloned().collect() + } + + pub fn lock_accounts( + &mut self, + read_locks: HashMap, + write_locks: HashMap, + ) { + for (acc, count) in read_locks { + *self.read_locks.entry(acc).or_insert(0) += count; + } + for (acc, count) in write_locks { + *self.write_locks.entry(acc).or_insert(0) += count; + } + } + + pub fn unlock_accounts( + &mut self, + read_locks: HashMap, + write_locks: HashMap, + ) { + for (acc, count) in read_locks { + if let Entry::Occupied(mut entry) = self.read_locks.entry(acc) { + let val = entry.get_mut(); + *val = val.saturating_sub(count); + if entry.get() == &0 { + let _ = entry.remove(); + } + } else { + warn!("error unlocking read-locked account, account: {:?}", acc); + } + } + for (acc, count) in write_locks { + if let Entry::Occupied(mut entry) = self.write_locks.entry(acc) { + let val = entry.get_mut(); + *val = val.saturating_sub(count); + if entry.get() == &0 { + let _ = entry.remove(); + } + } else { + warn!("error unlocking write-locked account, account: {:?}", acc); + } + } + } +} + +#[derive(Clone, Default)] +pub struct BundleAccountLocker { + account_locks: Arc>, +} + +impl BundleAccountLocker { + /// used in BankingStage during TransactionBatch construction to ensure that BankingStage + /// doesn't lock anything currently locked in the BundleAccountLocker + pub fn read_locks(&self) -> HashSet { + self.account_locks.lock().unwrap().read_locks() + } + + /// used in BankingStage during TransactionBatch construction to ensure that BankingStage + /// doesn't lock anything currently locked in the BundleAccountLocker + pub fn write_locks(&self) -> HashSet { + self.account_locks.lock().unwrap().write_locks() + } + + /// used in BankingStage during TransactionBatch construction to ensure that BankingStage + /// doesn't lock anything currently locked in the BundleAccountLocker + pub fn account_locks(&self) -> MutexGuard { + self.account_locks.lock().unwrap() + } + + /// Prepares a locked bundle and returns a LockedBundle containing locked accounts. + /// When a LockedBundle is dropped, the accounts are automatically unlocked + pub fn prepare_locked_bundle<'a, 'b>( + &'a self, + sanitized_bundle: &'b SanitizedBundle, + bank: &Arc, + ) -> BundleAccountLockerResult> { + let (read_locks, write_locks) = Self::get_read_write_locks(sanitized_bundle, bank)?; + + self.account_locks + .lock() + .unwrap() + .lock_accounts(read_locks, write_locks); + Ok(LockedBundle::new(self, sanitized_bundle, bank)) + } + + /// Unlocks bundle accounts. Note that LockedBundle::drop will auto-drop the bundle account locks + fn unlock_bundle_accounts( + &self, + sanitized_bundle: &SanitizedBundle, + bank: &Bank, + ) -> BundleAccountLockerResult<()> { + let (read_locks, write_locks) = Self::get_read_write_locks(sanitized_bundle, bank)?; + + self.account_locks + .lock() + .unwrap() + .unlock_accounts(read_locks, write_locks); + Ok(()) + } + + /// Returns the read and write locks for this bundle + /// Each lock type contains a HashMap which maps Pubkey to number of locks held + fn get_read_write_locks( + bundle: &SanitizedBundle, + bank: &Bank, + ) -> BundleAccountLockerResult<(HashMap, HashMap)> { + let transaction_locks: Vec = bundle + .transactions + .iter() + .filter_map(|tx| { + tx.get_account_locks(bank.get_transaction_account_lock_limit()) + .ok() + }) + .collect(); + + if transaction_locks.len() != bundle.transactions.len() { + return Err(BundleAccountLockerError::LockingError); + } + + let bundle_read_locks = transaction_locks + .iter() + .flat_map(|tx| tx.readonly.iter().map(|a| **a)); + let bundle_read_locks = + bundle_read_locks + .into_iter() + .fold(HashMap::new(), |mut map, acc| { + *map.entry(acc).or_insert(0) += 1; + map + }); + + let bundle_write_locks = transaction_locks + .iter() + .flat_map(|tx| tx.writable.iter().map(|a| **a)); + let bundle_write_locks = + bundle_write_locks + .into_iter() + .fold(HashMap::new(), |mut map, acc| { + *map.entry(acc).or_insert(0) += 1; + map + }); + + Ok((bundle_read_locks, bundle_write_locks)) + } +} + +#[cfg(test)] +mod tests { + use { + crate::{ + bundle_account_locker::BundleAccountLocker, bundle_sanitizer::get_sanitized_bundle, + packet_bundle::PacketBundle, + }, + solana_ledger::genesis_utils::create_genesis_config, + solana_perf::packet::PacketBatch, + solana_runtime::{ + bank::Bank, genesis_utils::GenesisConfigInfo, + transaction_error_metrics::TransactionErrorMetrics, + }, + solana_sdk::{ + packet::Packet, signature::Signer, signer::keypair::Keypair, system_program, + system_transaction::transfer, transaction::VersionedTransaction, + }, + std::{collections::HashSet, sync::Arc}, + uuid::Uuid, + }; + + #[test] + fn test_simple_lock_bundles() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let bundle_account_locker = BundleAccountLocker::default(); + + let kp0 = Keypair::new(); + let kp1 = Keypair::new(); + + let tx0 = VersionedTransaction::from(transfer( + &mint_keypair, + &kp0.pubkey(), + 1, + genesis_config.hash(), + )); + let tx1 = VersionedTransaction::from(transfer( + &mint_keypair, + &kp1.pubkey(), + 1, + genesis_config.hash(), + )); + + let packet_bundle0 = PacketBundle { + batch: PacketBatch::new(vec![Packet::from_data(None, &tx0).unwrap()]), + uuid: Uuid::new_v4(), + }; + let packet_bundle1 = PacketBundle { + batch: PacketBatch::new(vec![Packet::from_data(None, &tx1).unwrap()]), + uuid: Uuid::new_v4(), + }; + + let mut transaction_errors = TransactionErrorMetrics::default(); + + let sanitized_bundle0 = get_sanitized_bundle( + &packet_bundle0, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors, + ) + .expect("sanitize bundle 0"); + let sanitized_bundle1 = get_sanitized_bundle( + &packet_bundle1, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors, + ) + .expect("sanitize bundle 1"); + + let locked_bundle0 = bundle_account_locker + .prepare_locked_bundle(&sanitized_bundle0, &bank) + .unwrap(); + + assert_eq!( + bundle_account_locker.write_locks(), + HashSet::from_iter([mint_keypair.pubkey(), kp0.pubkey()]) + ); + assert_eq!( + bundle_account_locker.read_locks(), + HashSet::from_iter([system_program::id()]) + ); + + let locked_bundle1 = bundle_account_locker + .prepare_locked_bundle(&sanitized_bundle1, &bank) + .unwrap(); + assert_eq!( + bundle_account_locker.write_locks(), + HashSet::from_iter([mint_keypair.pubkey(), kp0.pubkey(), kp1.pubkey()]) + ); + assert_eq!( + bundle_account_locker.read_locks(), + HashSet::from_iter([system_program::id()]) + ); + + drop(locked_bundle0); + assert_eq!( + bundle_account_locker.write_locks(), + HashSet::from_iter([mint_keypair.pubkey(), kp1.pubkey()]) + ); + assert_eq!( + bundle_account_locker.read_locks(), + HashSet::from_iter([system_program::id()]) + ); + + drop(locked_bundle1); + assert!(bundle_account_locker.write_locks().is_empty()); + assert!(bundle_account_locker.read_locks().is_empty()); + } +} diff --git a/core/src/bundle_sanitizer.rs b/core/src/bundle_sanitizer.rs new file mode 100644 index 0000000000..4fde24e57b --- /dev/null +++ b/core/src/bundle_sanitizer.rs @@ -0,0 +1,612 @@ +///! Turns packets into SanitizedTransactions and ensure they pass sanity checks +use { + crate::immutable_deserialized_packet::ImmutableDeserializedPacket, + crate::packet_bundle::PacketBundle, + crate::unprocessed_packet_batches::deserialize_packets, + solana_perf::sigverify::verify_packet, + solana_runtime::{bank::Bank, transaction_error_metrics::TransactionErrorMetrics}, + solana_sdk::{ + bundle::sanitized::SanitizedBundle, + clock::MAX_PROCESSING_AGE, + feature_set::FeatureSet, + pubkey::Pubkey, + signature::Signature, + transaction::{AddressLoader, SanitizedTransaction}, + }, + std::{ + collections::{hash_map::RandomState, HashSet}, + iter::repeat, + sync::Arc, + }, + thiserror::Error, +}; + +pub const MAX_PACKETS_PER_BUNDLE: usize = 5; + +#[derive(Error, Debug, PartialEq, Eq, Clone)] +pub enum BundleSanitizerError { + #[error("Bank is in vote-only mode")] + VoteOnlyMode, + #[error("Bundle packet batch failed pre-check")] + FailedPacketBatchPreCheck, + #[error("Bundle mentions blacklisted account")] + BlacklistedAccount, + #[error("Bundle contains a transaction that failed to serialize")] + FailedToSerializeTransaction, + #[error("Bundle contains a duplicate transaction")] + DuplicateTransaction, + #[error("Bundle failed check_transactions")] + FailedCheckTransactions, +} + +pub type BundleSanitizationResult = Result; + +/// An invalid bundle contains one of the following: +/// No packets. +/// Too many packets. +/// Packets marked for discard (not sure why someone would do this) +/// One of the packets fails signature verification. +/// Mentions an account in consensus or blacklisted accounts. +/// Contains a packet that failed to serialize to a transaction. +/// Contains duplicate transactions within the same bundle. +/// Contains a transaction that was already processed or one with an invalid blockhash. +/// NOTE: bundles need to be sanitized for a given bank. For instance, a bundle sanitized +/// on bank n-1 will be valid for all of bank n-1, and may or may not be valid for bank n +pub fn get_sanitized_bundle( + packet_bundle: &PacketBundle, + bank: &Arc, + consensus_accounts_cache: &HashSet, + blacklisted_accounts: &HashSet, + transaction_error_metrics: &mut TransactionErrorMetrics, +) -> BundleSanitizationResult { + if bank.vote_only_bank() { + return Err(BundleSanitizerError::VoteOnlyMode); + } + + if packet_bundle.batch.is_empty() + || packet_bundle.batch.len() > MAX_PACKETS_PER_BUNDLE + || packet_bundle.batch.iter().any(|p| p.meta.discard()) + || packet_bundle + .batch + .iter() + .any(|p| !verify_packet(&mut p.clone(), false)) + { + return Err(BundleSanitizerError::FailedPacketBatchPreCheck); + } + + let packet_indexes = (0..packet_bundle.batch.len()).collect::>(); + let deserialized_packets = deserialize_packets(&packet_bundle.batch, &packet_indexes); + let transactions: Vec = deserialized_packets + .filter_map(|p| { + let immutable_packet = p.immutable_section().clone(); + transaction_from_deserialized_packet( + &immutable_packet, + &bank.feature_set, + bank.as_ref(), + ) + }) + .collect(); + + let unique_signatures: HashSet<&Signature, RandomState> = + HashSet::from_iter(transactions.iter().map(|tx| tx.signature())); + let contains_blacklisted_account = transactions.iter().any(|tx| { + let accounts = tx.message().account_keys(); + accounts + .iter() + .any(|acc| blacklisted_accounts.contains(acc) || consensus_accounts_cache.contains(acc)) + }); + + if contains_blacklisted_account { + return Err(BundleSanitizerError::BlacklistedAccount); + } + + if transactions.is_empty() || packet_bundle.batch.len() != transactions.len() { + return Err(BundleSanitizerError::FailedToSerializeTransaction); + } + + if unique_signatures.len() != transactions.len() { + return Err(BundleSanitizerError::DuplicateTransaction); + } + + // assume everything locks okay to check for already-processed transaction or expired/invalid blockhash + let lock_results: Vec<_> = repeat(Ok(())).take(transactions.len()).collect(); + let check_results = bank.check_transactions( + &transactions, + &lock_results, + MAX_PROCESSING_AGE, + transaction_error_metrics, + ); + if check_results.iter().any(|r| r.0.is_err()) { + return Err(BundleSanitizerError::FailedCheckTransactions); + } + + Ok(SanitizedBundle { transactions }) +} + +// This function deserializes packets into transactions, computes the blake3 hash of transaction +// messages, and verifies secp256k1 instructions. A list of sanitized transactions are returned +// with their packet indexes. +// NOTES on tx v2: +// - tx v2 can only load addresses set in previous slots +// - tx v2 can't reorg indices in a lookup table +// - tx v2 transaction loading fails if it tries to access an invalid index (either doesn't exist +// or exists but was set in the current slot +#[allow(clippy::needless_collect)] +fn transaction_from_deserialized_packet( + deserialized_packet: &ImmutableDeserializedPacket, + feature_set: &Arc, + address_loader: impl AddressLoader, +) -> Option { + let tx = SanitizedTransaction::try_new( + deserialized_packet.transaction().clone(), + *deserialized_packet.message_hash(), + deserialized_packet.is_simple_vote(), + address_loader, + ) + .ok()?; + tx.verify_precompiles(feature_set).ok()?; + Some(tx) +} + +#[cfg(test)] +mod tests { + use { + crate::{ + bundle_sanitizer::{get_sanitized_bundle, MAX_PACKETS_PER_BUNDLE}, + packet_bundle::PacketBundle, + tip_manager::{TipDistributionAccountConfig, TipManager, TipManagerConfig}, + }, + solana_address_lookup_table_program::instruction::create_lookup_table, + solana_ledger::genesis_utils::create_genesis_config, + solana_perf::packet::PacketBatch, + solana_runtime::{ + bank::Bank, genesis_utils::GenesisConfigInfo, + transaction_error_metrics::TransactionErrorMetrics, + }, + solana_sdk::{ + hash::Hash, + instruction::Instruction, + packet::Packet, + pubkey::Pubkey, + signature::{Keypair, Signer}, + system_transaction::transfer, + transaction::{SanitizedTransaction, Transaction, VersionedTransaction}, + }, + std::{collections::HashSet, sync::Arc}, + uuid::Uuid, + }; + + #[test] + fn test_simple_get_sanitized_bundle() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + 1, + genesis_config.hash(), + )); + let packet = Packet::from_data(None, &tx).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + let mut transaction_errors = TransactionErrorMetrics::default(); + let sanitized_bundle = get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors, + ) + .unwrap(); + assert_eq!(sanitized_bundle.transactions.len(), 1); + assert_eq!( + sanitized_bundle.transactions[0].signature(), + &tx.signatures[0] + ); + } + + #[test] + fn test_fail_to_sanitize_consensus_account() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + 1, + genesis_config.hash(), + )); + let packet = Packet::from_data(None, &tx).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + let consensus_accounts_cache = HashSet::from([kp.pubkey()]); + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &consensus_accounts_cache, + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fail_to_sanitize_duplicate_transaction() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + 1, + genesis_config.hash(), + )); + let packet = Packet::from_data(None, &tx).unwrap(); + + // bundle with a duplicate transaction + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet.clone(), packet]), + uuid: Uuid::new_v4(), + }; + + // fails to pop because bundle it locks the same transaction twice + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fails_to_sanitize_bad_blockhash() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let tx = + VersionedTransaction::from(transfer(&mint_keypair, &kp.pubkey(), 1, Hash::default())); + let packet = Packet::from_data(None, &tx).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet.clone(), packet]), + uuid: Uuid::new_v4(), + }; + + // fails to pop because bundle has bad blockhash + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fails_to_sanitize_already_processed() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + 1, + genesis_config.hash(), + )); + let packet = Packet::from_data(None, &tx).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet.clone()]), + uuid: Uuid::new_v4(), + }; + + let mut transaction_errors = TransactionErrorMetrics::default(); + let sanitized_bundle = get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors, + ) + .unwrap(); + + let results = bank.process_entry_transactions( + sanitized_bundle + .transactions + .into_iter() + .map(|tx| tx.to_versioned_transaction()) + .collect(), + ); + assert_eq!(results.len(), 1); + assert_eq!(results[0], Ok(())); + + // try to process the same one again shall fail + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fails_to_sanitize_bundle_tip_program() { + solana_logger::setup(); + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let tip_manager = TipManager::new(TipManagerConfig { + tip_payment_program_id: Pubkey::new_unique(), + tip_distribution_program_id: Pubkey::new_unique(), + tip_distribution_account_config: TipDistributionAccountConfig { + payer: Arc::new(Keypair::new()), + merkle_root_upload_authority: Pubkey::new_unique(), + vote_account: Pubkey::new_unique(), + commission_bps: 0, + }, + }); + + let kp = Keypair::new(); + let tx = + SanitizedTransaction::try_from_legacy_transaction(Transaction::new_signed_with_payer( + &[Instruction::new_with_bytes( + tip_manager.tip_payment_program_id(), + &[0], + vec![], + )], + Some(&kp.pubkey()), + &[&kp], + genesis_config.hash(), + )) + .unwrap(); + + let packet = Packet::from_data(None, &tx.to_versioned_transaction()).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + // fails to pop because bundle mentions tip program + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::from_iter([tip_manager.tip_payment_program_id()]), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_txv2_sanitized_bundle_ok() { + solana_logger::setup(); + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + let tx = + SanitizedTransaction::try_from_legacy_transaction(Transaction::new_signed_with_payer( + &[create_lookup_table(kp.pubkey(), kp.pubkey(), bank.slot()).0], + Some(&kp.pubkey()), + &[&kp], + genesis_config.hash(), + )) + .unwrap(); + + let packet = Packet::from_data(None, &tx.to_versioned_transaction()).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_ok()); + } + + #[test] + fn test_fails_to_sanitize_empty_bundle() { + solana_logger::setup(); + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![]), + uuid: Uuid::new_v4(), + }; + // fails to pop because empty bundle + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fails_to_sanitize_too_many_packets() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let packets = (0..MAX_PACKETS_PER_BUNDLE + 1).map(|i| { + let tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + i as u64, + genesis_config.hash(), + )); + Packet::from_data(None, &tx).unwrap() + }); + let packet_bundle = PacketBundle { + batch: PacketBatch::new(packets.collect()), + uuid: Uuid::new_v4(), + }; + // fails to pop because too many packets in a bundle + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fails_to_sanitize_discarded() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + 1, + genesis_config.hash(), + )); + let mut packet = Packet::from_data(None, &tx).unwrap(); + packet.meta.set_discard(true); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + // fails to pop because one of the packets is marked as discard + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } + + #[test] + fn test_fails_to_sanitize_bad_sigverify() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(2); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + + let kp = Keypair::new(); + + let mut tx = VersionedTransaction::from(transfer( + &mint_keypair, + &kp.pubkey(), + 1, + genesis_config.hash(), + )); + + let _ = tx.signatures.pop(); + + let bad_kp = Keypair::new(); + let serialized = tx.message.serialize(); + let bad_sig = bad_kp.sign_message(&serialized); + tx.signatures.push(bad_sig); + + let packet = Packet::from_data(None, &tx).unwrap(); + + let packet_bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + let mut transaction_errors = TransactionErrorMetrics::default(); + assert!(get_sanitized_bundle( + &packet_bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + &mut transaction_errors + ) + .is_err()); + } +} diff --git a/core/src/bundle_stage.rs b/core/src/bundle_stage.rs new file mode 100644 index 0000000000..c82ddce6f4 --- /dev/null +++ b/core/src/bundle_stage.rs @@ -0,0 +1,1943 @@ +//! The `banking_stage` processes Transaction messages. It is intended to be used +//! to contruct a software pipeline. The stage uses all available CPU cores and +//! can do its processing in parallel with signature verification on the GPU. +use { + crate::{ + banking_stage::{BatchedTransactionDetails, CommitTransactionDetails}, + bundle_account_locker::{BundleAccountLocker, BundleAccountLockerResult, LockedBundle}, + bundle_sanitizer::{get_sanitized_bundle, BundleSanitizerError}, + bundle_stage_leader_stats::{BundleStageLeaderSlotTrackingMetrics, BundleStageLeaderStats}, + consensus_cache_updater::ConsensusCacheUpdater, + leader_slot_banking_stage_timing_metrics::RecordTransactionsTimings, + packet_bundle::PacketBundle, + qos_service::QosService, + tip_manager::TipManager, + }, + crossbeam_channel::{Receiver, RecvTimeoutError}, + solana_entry::entry::hash_transactions, + solana_gossip::cluster_info::ClusterInfo, + solana_ledger::{ + blockstore_processor::TransactionStatusSender, token_balances::collect_token_balances, + }, + solana_measure::measure, + solana_poh::poh_recorder::{ + BankStart, PohRecorder, + PohRecorderError::{self}, + TransactionRecorder, + }, + solana_program_runtime::timings::ExecuteTimings, + solana_runtime::{ + account_overrides::AccountOverrides, + accounts::TransactionLoadResult, + bank::{ + Bank, CommitTransactionCounts, LoadAndExecuteTransactionsOutput, TransactionBalances, + TransactionBalancesSet, TransactionExecutionResult, + }, + bank_utils, + cost_model::{CostModel, TransactionCost}, + transaction_batch::TransactionBatch, + vote_sender_types::ReplayVoteSender, + }, + solana_sdk::{ + bundle::{ + error::BundleExecutionError, sanitized::SanitizedBundle, + utils::check_bundle_lock_results, + }, + clock::{Slot, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE}, + hash::Hash, + pubkey::Pubkey, + saturating_add_assign, + transaction::{self, SanitizedTransaction, TransactionError, VersionedTransaction}, + }, + solana_transaction_status::token_balances::{ + TransactionTokenBalances, TransactionTokenBalancesSet, + }, + std::{ + collections::{HashMap, HashSet, VecDeque}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, RwLock, + }, + thread::{self, Builder, JoinHandle}, + time::{Duration, Instant}, + }, +}; + +const MAX_BUNDLE_RETRY_DURATION: Duration = Duration::from_millis(10); +const SLOT_BOUNDARY_CHECK_PERIOD: Duration = Duration::from_millis(10); + +type BundleStageResult = Result; + +// Stats emitted periodically +struct BundleStageLoopStats { + last_report: Instant, + + num_bundles_received: u64, + num_bundles_dropped: u64, + receive_and_buffer_bundles_elapsed_us: u64, + process_buffered_bundles_elapsed_us: u64, +} + +impl Default for BundleStageLoopStats { + fn default() -> Self { + BundleStageLoopStats { + last_report: Instant::now(), + num_bundles_received: 0, + num_bundles_dropped: 0, + receive_and_buffer_bundles_elapsed_us: 0, + process_buffered_bundles_elapsed_us: 0, + } + } +} + +impl BundleStageLoopStats { + fn maybe_report(&mut self, id: u32, period: Duration) { + if self.last_report.elapsed() > period { + datapoint_info!( + "bundle_stage-loop_stats", + ("id", id, i64), + ("num_bundles_received", self.num_bundles_received, i64), + ("num_bundles_dropped", self.num_bundles_dropped, i64), + ( + "receive_and_buffer_bundles_elapsed_us", + self.receive_and_buffer_bundles_elapsed_us, + i64 + ), + ( + "process_buffered_bundles_elapsed_us", + self.process_buffered_bundles_elapsed_us, + i64 + ), + ); + *self = BundleStageLoopStats::default(); + } + } +} + +struct AllExecutionResults { + pub load_and_execute_tx_output: LoadAndExecuteTransactionsOutput, + pub sanitized_txs: Vec, + pub pre_balances: (TransactionBalances, TransactionTokenBalances), + pub post_balances: (TransactionBalances, TransactionTokenBalances), +} + +pub struct BundleStage { + bundle_thread: JoinHandle<()>, +} + +impl BundleStage { + #[allow(clippy::new_ret_no_self)] + pub fn new( + cluster_info: &Arc, + poh_recorder: &Arc>, + transaction_status_sender: Option, + gossip_vote_sender: ReplayVoteSender, + cost_model: Arc>, + bundle_receiver: Receiver>, + exit: Arc, + tip_manager: TipManager, + bundle_account_locker: BundleAccountLocker, + ) -> Self { + Self::start_bundle_thread( + cluster_info, + poh_recorder, + transaction_status_sender, + gossip_vote_sender, + cost_model, + bundle_receiver, + exit, + tip_manager, + bundle_account_locker, + MAX_BUNDLE_RETRY_DURATION, + ) + } + + #[allow(clippy::too_many_arguments)] + fn start_bundle_thread( + cluster_info: &Arc, + poh_recorder: &Arc>, + transaction_status_sender: Option, + gossip_vote_sender: ReplayVoteSender, + cost_model: Arc>, + bundle_receiver: Receiver>, + exit: Arc, + tip_manager: TipManager, + bundle_account_locker: BundleAccountLocker, + max_bundle_retry_duration: Duration, + ) -> Self { + const BUNDLE_STAGE_ID: u32 = 10_000; + let poh_recorder = poh_recorder.clone(); + let cluster_info = cluster_info.clone(); + + let bundle_thread = Builder::new() + .name("solana-bundle-stage".to_string()) + .spawn(move || { + let transaction_status_sender = transaction_status_sender.clone(); + Self::process_loop( + cluster_info, + &poh_recorder, + transaction_status_sender, + bundle_receiver, + gossip_vote_sender, + BUNDLE_STAGE_ID, + cost_model, + exit, + tip_manager, + bundle_account_locker, + max_bundle_retry_duration, + ); + }) + .unwrap(); + + Self { bundle_thread } + } + + // rollup transaction cost details, eg signature_cost, write_lock_cost, data_bytes_cost and + // execution_cost from the batch of transactions selected for block. + fn accumulate_batched_transaction_costs<'a>( + transactions_costs: impl Iterator, + transaction_results: impl Iterator>, + ) -> BatchedTransactionDetails { + let mut batched_transaction_details = BatchedTransactionDetails::default(); + transactions_costs + .zip(transaction_results) + .for_each(|(cost, result)| match result { + Ok(_) => { + saturating_add_assign!( + batched_transaction_details.costs.batched_signature_cost, + cost.signature_cost + ); + saturating_add_assign!( + batched_transaction_details.costs.batched_write_lock_cost, + cost.write_lock_cost + ); + saturating_add_assign!( + batched_transaction_details.costs.batched_data_bytes_cost, + cost.data_bytes_cost + ); + saturating_add_assign!( + batched_transaction_details + .costs + .batched_builtins_execute_cost, + cost.builtins_execution_cost + ); + saturating_add_assign!( + batched_transaction_details.costs.batched_bpf_execute_cost, + cost.bpf_execution_cost + ); + } + Err(transaction_error) => match transaction_error { + TransactionError::WouldExceedMaxBlockCostLimit => { + saturating_add_assign!( + batched_transaction_details + .errors + .batched_retried_txs_per_block_limit_count, + 1 + ); + } + TransactionError::WouldExceedMaxVoteCostLimit => { + saturating_add_assign!( + batched_transaction_details + .errors + .batched_retried_txs_per_vote_limit_count, + 1 + ); + } + TransactionError::WouldExceedMaxAccountCostLimit => { + saturating_add_assign!( + batched_transaction_details + .errors + .batched_retried_txs_per_account_limit_count, + 1 + ); + } + TransactionError::WouldExceedAccountDataBlockLimit => { + saturating_add_assign!( + batched_transaction_details + .errors + .batched_retried_txs_per_account_data_block_limit_count, + 1 + ); + } + TransactionError::WouldExceedAccountDataTotalLimit => { + saturating_add_assign!( + batched_transaction_details + .errors + .batched_dropped_txs_per_account_data_total_limit_count, + 1 + ); + } + _ => {} + }, + }); + batched_transaction_details + } + + /// Calculates QoS and reserves compute space for the bundle. If the bundle succeeds, commits + /// the results to the cost tracker. If the bundle fails, rolls back any QoS changes made. + /// Ensure that SanitizedBundle was returned by BundleAccountLocker to avoid parallelism issues + /// with banking stage + fn update_qos_and_execute_record_commit_bundle( + sanitized_bundle: &SanitizedBundle, + recorder: &TransactionRecorder, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + qos_service: &QosService, + bank_start: &BankStart, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + max_bundle_retry_duration: &Duration, + ) -> BundleStageResult<()> { + if sanitized_bundle.transactions.is_empty() { + return Ok(()); + } + + let tx_costs = qos_service.compute_transaction_costs(sanitized_bundle.transactions.iter()); + let (transactions_qos_results, num_included) = qos_service.select_transactions_per_cost( + sanitized_bundle.transactions.iter(), + tx_costs.iter(), + &bank_start.working_bank, + ); + + // qos rate-limited a tx in here, drop the bundle + if sanitized_bundle.transactions.len() != num_included { + QosService::remove_transaction_costs( + tx_costs.iter(), + transactions_qos_results.iter(), + &bank_start.working_bank, + ); + return Err(BundleExecutionError::ExceedsCostModel); + } + + // accumulates QoS to metrics + qos_service.accumulate_estimated_transaction_costs( + &Self::accumulate_batched_transaction_costs( + tx_costs.iter(), + transactions_qos_results.iter(), + ), + ); + + match Self::execute_record_commit_bundle( + sanitized_bundle, + recorder, + transaction_status_sender, + gossip_vote_sender, + bank_start, + bundle_stage_leader_stats, + max_bundle_retry_duration, + ) { + Ok(commit_transaction_details) => { + // NOTE: Assumptions made on the QoS transaction costs: + // - commit_transaction_details are returned in the same ordering as the transactions + // in the sanitized_bundle, which is the same ordering as tx_costs. + // - all contents in the bundle are committed (it's executed all or nothing). + // When fancier execution algorithms are made that may execute transactions out of + // order (but resulting in same result as if they were executed sequentially), or + // allow failures in bundles, one should revisit this and the code that returns + // commit_transaction_details. + QosService::update_or_remove_transaction_costs( + tx_costs.iter(), + transactions_qos_results.iter(), + Some(&commit_transaction_details), + &bank_start.working_bank, + ); + let (cu, us) = Self::accumulate_execute_units_and_time( + &bundle_stage_leader_stats + .execute_and_commit_timings() + .execute_timings, + ); + qos_service.accumulate_actual_execute_cu(cu); + qos_service.accumulate_actual_execute_time(us); + qos_service.report_metrics(bank_start.working_bank.clone()); + Ok(()) + } + Err(e) => { + QosService::remove_transaction_costs( + tx_costs.iter(), + transactions_qos_results.iter(), + &bank_start.working_bank, + ); + qos_service.report_metrics(bank_start.working_bank.clone()); + Err(e) + } + } + } + + fn execute_bundle( + sanitized_bundle: &SanitizedBundle, + transaction_status_sender: &Option, + bank_start: &BankStart, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + max_bundle_retry_duration: &Duration, + ) -> BundleStageResult> { + let mut account_overrides = AccountOverrides::default(); + + let mut execution_results = Vec::new(); + let mut mint_decimals: HashMap = HashMap::new(); + + let BankStart { + working_bank: bank, + bank_creation_time, + } = bank_start; + + let mut chunk_start = 0; + let start_time = Instant::now(); + while chunk_start != sanitized_bundle.transactions.len() { + if !Bank::should_bank_still_be_processing_txs(bank_creation_time, bank.ns_per_slot) { + return Err(BundleExecutionError::PohMaxHeightError); + } + + // ************************************************************************ + // Build a TransactionBatch that ensures transactions in the bundle + // are executed sequentially. + // NOTE: The TransactionBatch is dropped before the results are committed, which + // would normally open up race conditions between this stage and BankingStage where + // a transaction here could read and execute state on a transaction and BankingStage + // could read-execute-store, invaliding the state produced by the bundle. + // Assuming the SanitizedBundle was locked with the BundleAccountLocker, that race + // condition shall be prevented as it holds an extra set of locks until the entire + // bundle is processed. + // ************************************************************************ + let chunk_end = std::cmp::min(sanitized_bundle.transactions.len(), chunk_start + 128); + let chunk = &sanitized_bundle.transactions[chunk_start..chunk_end]; + let batch = bank.prepare_sequential_sanitized_batch_with_results(chunk, None); + + // Ensures that bundle lock results only return either: + // Ok(()) + // Err(TransactionError::AccountInUse) + // Err(TransactionError::BundleNotContinuous) + // if unexpected failure case, the bundle can't be executed + // NOTE: previous logging around batch here caused issues with + // unit tests failing due to PoH hitting max height. Unknown why. Be advised. + if let Some((e, _)) = check_bundle_lock_results(batch.lock_results()) { + return Err(e.into()); + } + + let ((pre_balances, pre_token_balances), collect_balances_elapsed) = measure!( + Self::collect_balances( + bank, + &batch, + &account_overrides, + transaction_status_sender, + &mut mint_decimals, + ), + "collect_balances", + ); + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .collect_balances_us, + collect_balances_elapsed.as_us() + ); + + let (mut load_and_execute_transactions_output, load_execute_time) = measure!( + bank.load_and_execute_transactions( + &batch, + MAX_PROCESSING_AGE, + transaction_status_sender.is_some(), + transaction_status_sender.is_some(), + transaction_status_sender.is_some(), + &mut bundle_stage_leader_stats + .execute_and_commit_timings() + .execute_timings, + Some(&account_overrides), + None, + ), + "load_execute", + ); + + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .load_execute_us, + load_execute_time.as_us() + ); + bundle_stage_leader_stats + .transaction_errors() + .accumulate(&load_and_execute_transactions_output.error_counters); + + debug!( + "execution results: {:?}", + load_and_execute_transactions_output.execution_results + ); + // Return error if executed and failed or didn't execute because of an unexpected reason. + // The only acceptable reasons for not executing would be failure to lock errors from: + // Ok(()) + // Err(TransactionError::AccountInUse) + // Err(TransactionError::BundleNotContinuous) + // If there's another error (AlreadyProcessed, InsufficientFundsForFee, etc.), bail out + if let Err((e, _)) = TransactionExecutionResult::check_bundle_execution_results( + load_and_execute_transactions_output + .execution_results + .as_slice(), + batch.sanitized_transactions(), + ) { + debug!("execution error"); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_execution_failures(1); + + return Err(e); + } + + // The errors have been checked above, now check to see if any were executed at all + // If none were executed, check to see if the bundle timed out and if so, return timeout + // error + if !load_and_execute_transactions_output + .execution_results + .iter() + .any(|r| r.was_executed()) + { + debug!("retrying bundle"); + + let bundle_execution_elapsed = start_time.elapsed(); + if bundle_execution_elapsed >= *max_bundle_retry_duration { + warn!("bundle timed out: {:?}", sanitized_bundle); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_execution_timeouts(1); + return Err(BundleExecutionError::MaxRetriesExceeded( + bundle_execution_elapsed, + )); + } + + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_execution_retries(1); + continue; + } + + // ********************************************************************************* + // Cache results so next iterations of bundle execution can load cached state + // instead of using AccountsDB which contains stale execution data. + // ********************************************************************************* + Self::cache_accounts( + bank, + batch.sanitized_transactions(), + &load_and_execute_transactions_output.execution_results, + &mut load_and_execute_transactions_output.loaded_transactions, + &mut account_overrides, + ); + + let ((post_balances, post_token_balances), collect_balances_elapsed) = measure!( + Self::collect_balances( + bank, + &batch, + &account_overrides, + transaction_status_sender, + &mut mint_decimals, + ), + "collect_balances", + ); + + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .collect_balances_us, + collect_balances_elapsed.as_us() + ); + + execution_results.push(AllExecutionResults { + load_and_execute_tx_output: load_and_execute_transactions_output, + sanitized_txs: batch.sanitized_transactions().to_vec(), + pre_balances: (pre_balances, pre_token_balances), + post_balances: (post_balances, post_token_balances), + }); + + // start at the next available transaction in the batch that threw an error + let processing_end = batch.lock_results().iter().position(|lr| lr.is_err()); + if let Some(end) = processing_end { + chunk_start += end; + } else { + chunk_start = chunk_end; + } + + drop(batch); + } + Ok(execution_results) + } + + /// Executes a bundle, where all transactions in the bundle are executed all-or-nothing. + /// Executes all transactions until the end or the first failure. The account state between + /// iterations is cached to a temporary HashMap to be used on successive runs + #[allow(clippy::too_many_arguments)] + fn execute_record_commit_bundle( + sanitized_bundle: &SanitizedBundle, + recorder: &TransactionRecorder, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + bank_start: &BankStart, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + max_bundle_retry_duration: &Duration, + ) -> BundleStageResult> { + let execution_results = Self::execute_bundle( + sanitized_bundle, + transaction_status_sender, + bank_start, + bundle_stage_leader_stats, + max_bundle_retry_duration, + )?; + // in order for bundle to succeed, it most have something to record + commit + assert!(!execution_results.is_empty()); + + Self::record_commit_bundle( + execution_results, + &bank_start.working_bank, + recorder, + bundle_stage_leader_stats, + transaction_status_sender, + gossip_vote_sender, + ) + } + + /// Records the entire bundle to PoH and if successful, commits all transactions to the Bank + /// Note that the BundleAccountLocker still has a lock on these accounts in the bank + fn record_commit_bundle( + execution_results: Vec, + bank: &Arc, + recorder: &TransactionRecorder, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + ) -> BundleStageResult> { + // ********************************************************************************* + // All transactions are executed in the bundle. + // Record to PoH and send the saved execution results to the Bank. + // Note: Ensure that bank.commit_transactions is called on a per-batch basis and + // not all together + // ********************************************************************************* + debug!("grabbing freeze lock"); + let (_freeze_lock, freeze_lock_time) = measure!(bank.freeze_lock(), "freeze_lock"); + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .freeze_lock_us, + freeze_lock_time.as_us() + ); + + let (slot, mixins) = Self::prepare_poh_record_bundle( + &bank.slot(), + &execution_results, + &mut bundle_stage_leader_stats + .execute_and_commit_timings() + .record_transactions_timings, + ); + + debug!("recording bundle"); + let (mut transaction_index, record_elapsed) = measure!( + Self::try_record(recorder, slot, mixins) + .map_err(|e| { + error!("error recording bundle: {:?}", e); + e + })? + .unwrap_or_default(), + "record_elapsed" + ); + debug!("bundle recorded"); + + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .record_us, + record_elapsed.as_us() + ); + bundle_stage_leader_stats + .execute_and_commit_timings() + .record_transactions_timings + .accumulate(&RecordTransactionsTimings { + execution_results_to_transactions_us: 0, + hash_us: 0, + poh_record_us: record_elapsed.as_us(), + }); + + let mut commit_transaction_details = Vec::new(); + for r in execution_results { + let mut output = r.load_and_execute_tx_output; + let sanitized_txs = r.sanitized_txs; + + let (last_blockhash, lamports_per_signature) = + bank.last_blockhash_and_lamports_per_signature(); + + let (transaction_results, commit_elapsed) = measure!( + bank.commit_transactions( + &sanitized_txs, + &mut output.loaded_transactions, + output.execution_results.clone(), + last_blockhash, + lamports_per_signature, + CommitTransactionCounts { + committed_transactions_count: output.executed_transactions_count as u64, + committed_with_failure_result_count: output + .executed_transactions_count + .saturating_sub(output.executed_with_successful_result_count) + as u64, + signature_count: output.signature_count, + }, + &mut bundle_stage_leader_stats + .execute_and_commit_timings() + .execute_timings, + ), + "commit_elapsed" + ); + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .commit_us, + commit_elapsed.as_us() + ); + + let (_, find_and_send_votes_elapsed) = measure!( + { + bank_utils::find_and_send_votes( + &sanitized_txs, + &transaction_results, + Some(gossip_vote_sender), + ); + if let Some(transaction_status_sender) = transaction_status_sender { + let batch_transaction_indexes: Vec<_> = transaction_results + .execution_results + .iter() + .map(|result| { + if result.was_executed() { + let this_transaction_index = transaction_index; + saturating_add_assign!(transaction_index, 1); + this_transaction_index + } else { + 0 + } + }) + .collect(); + transaction_status_sender.send_transaction_status_batch( + bank.clone(), + sanitized_txs, + output.execution_results, + TransactionBalancesSet::new(r.pre_balances.0, r.post_balances.0), + TransactionTokenBalancesSet::new(r.pre_balances.1, r.post_balances.1), + transaction_results.rent_debits.clone(), + batch_transaction_indexes, + ); + } + }, + "find_and_send_votes", + ); + + saturating_add_assign!( + bundle_stage_leader_stats + .execute_and_commit_timings() + .find_and_send_votes_us, + find_and_send_votes_elapsed.as_us() + ); + + for tx_results in transaction_results.execution_results { + if let Some(details) = tx_results.details() { + commit_transaction_details.push(CommitTransactionDetails::Committed { + compute_units: details.executed_units, + }); + } + } + } + Ok(commit_transaction_details) + } + + /// Returns true if any of the transactions in a bundle mention one of the tip PDAs + fn bundle_touches_tip_pdas( + transactions: &[SanitizedTransaction], + tip_pdas: &HashSet, + ) -> bool { + let mut bundle_touches_tip_pdas = false; + for tx in transactions { + if tx + .message() + .account_keys() + .iter() + .any(|a| tip_pdas.contains(a)) + { + bundle_touches_tip_pdas = true; + break; + } + } + bundle_touches_tip_pdas + } + + fn accumulate_execute_units_and_time(execute_timings: &ExecuteTimings) -> (u64, u64) { + let (units, times): (Vec<_>, Vec<_>) = execute_timings + .details + .per_program_timings + .iter() + .map(|(_program_id, program_timings)| { + ( + program_timings.accumulated_units, + program_timings.accumulated_us, + ) + }) + .unzip(); + (units.iter().sum(), times.iter().sum()) + } + + fn cache_accounts( + bank: &Arc, + txs: &[SanitizedTransaction], + res: &[TransactionExecutionResult], + loaded: &mut [TransactionLoadResult], + cached_accounts: &mut AccountOverrides, + ) { + let accounts = bank.collect_accounts_to_store(txs, res, loaded); + for (pubkey, data) in accounts { + cached_accounts.set_account(pubkey, Some(data.clone())); + } + } + + fn collect_balances( + bank: &Arc, + batch: &TransactionBatch, + cached_accounts: &AccountOverrides, + transaction_status_sender: &Option, + mint_decimals: &mut HashMap, + ) -> (TransactionBalances, TransactionTokenBalances) { + if transaction_status_sender.is_some() { + let balances = bank.collect_balances_with_cache(batch, Some(cached_accounts)); + let token_balances = + collect_token_balances(bank, batch, mint_decimals, Some(cached_accounts)); + (balances, token_balances) + } else { + (vec![], vec![]) + } + } + + /// When executed the first time, there's some accounts that need to be initialized. + /// This is only helpful for local testing, on testnet and mainnet these will never be executed. + /// TODO (LB): consider removing this for mainnet/testnet and move to program deployment? + fn get_initialize_tip_accounts_transactions( + bank: &Bank, + tip_manager: &TipManager, + cluster_info: &Arc, + ) -> BundleStageResult> { + let maybe_init_tip_payment_config_tx = + if tip_manager.should_initialize_tip_payment_program(bank) { + info!("building initialize_tip_payment_program_tx"); + Some(tip_manager.initialize_tip_payment_program_tx( + bank.last_blockhash(), + &cluster_info.keypair(), + )) + } else { + None + }; + + let maybe_init_tip_distro_config_tx = + if tip_manager.should_initialize_tip_distribution_config(bank) { + info!("building initialize_tip_distribution_config_tx"); + Some(tip_manager.initialize_tip_distribution_config_tx( + bank.last_blockhash(), + &cluster_info.keypair(), + )) + } else { + None + }; + + let maybe_init_tip_distro_account_tx = if tip_manager + .should_init_tip_distribution_account(bank) + { + info!("building init_tip_distribution_account_tx"); + Some(tip_manager.init_tip_distribution_account_tx(bank.last_blockhash(), bank.epoch())) + } else { + None + }; + + let transactions = [ + maybe_init_tip_payment_config_tx, + maybe_init_tip_distro_config_tx, + maybe_init_tip_distro_account_tx, + ] + .into_iter() + .flatten() + .collect::>(); + + Ok(transactions) + } + + /// Execute all unprocessed bundles until no more left or POH max tick height is reached. + /// For any bundles that didn't execute due to POH max tick height reached, add them + /// back onto the front of unprocessed_bundles in reverse order to preserve original ordering + #[allow(clippy::too_many_arguments)] + fn execute_bundles_until_empty_or_end_of_slot( + bundle_account_locker: &BundleAccountLocker, + unprocessed_bundles: &mut VecDeque, + blacklisted_accounts: &HashSet, + bank_start: &BankStart, + consensus_accounts_cache: &HashSet, + cluster_info: &Arc, + recorder: &TransactionRecorder, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + qos_service: &QosService, + tip_manager: &TipManager, + max_bundle_retry_duration: &Duration, + last_tip_update_slot: &mut Slot, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + ) { + let (sanitized_bundles, sanitized_bundle_elapsed) = measure!( + unprocessed_bundles + .drain(..) + .into_iter() + .filter_map(|packet_bundle| { + match get_sanitized_bundle( + &packet_bundle, + &bank_start.working_bank, + consensus_accounts_cache, + blacklisted_accounts, + bundle_stage_leader_stats.transaction_errors(), + ) { + Ok(sanitized_bundle) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_ok(1); + Some((packet_bundle, sanitized_bundle)) + } + Err(BundleSanitizerError::VoteOnlyMode) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_vote_only_mode(1); + None + } + Err(BundleSanitizerError::FailedPacketBatchPreCheck) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_failed_precheck(1); + None + } + Err(BundleSanitizerError::BlacklistedAccount) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_blacklisted_account(1); + None + } + Err(BundleSanitizerError::FailedToSerializeTransaction) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_failed_to_serialize(1); + None + } + Err(BundleSanitizerError::DuplicateTransaction) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_duplicate_transaction(1); + None + } + Err(BundleSanitizerError::FailedCheckTransactions) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_transaction_failed_check(1); + None + } + } + }) + .collect::>(), + "sanitized_bundle_elapsed" + ); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_sanitize_bundle_elapsed_us(sanitized_bundle_elapsed.as_us()); + + // Prepare locked bundles, which will RW lock accounts in sanitized_bundles so + // BankingStage can't lock them. This adds a layer of protection since a transaction in a bundle + // will not hold the AccountLocks through TransactionBatch across load-execute-commit cycle. + // We collect here to ensure that all of the bundles are locked ahead of time for priority over + // BankingStage + #[allow(clippy::needless_collect)] + let (locked_bundles, locked_bundles_elapsed) = measure!( + sanitized_bundles + .iter() + .map(|(_, sanitized_bundle)| { + bundle_account_locker + .prepare_locked_bundle(sanitized_bundle, &bank_start.working_bank) + }) + .collect::>>(), + "locked_bundles_elapsed" + ); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_locked_bundle_elapsed_us(locked_bundles_elapsed.as_us()); + + let (execution_results, execute_locked_bundles_elapsed) = measure!( + Self::execute_locked_bundles( + bundle_account_locker, + locked_bundles, + bank_start, + cluster_info, + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + tip_manager, + max_bundle_retry_duration, + last_tip_update_slot, + bundle_stage_leader_stats, + ), + "execute_locked_bundles_elapsed" + ); + + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execute_locked_bundles_elapsed_us(execute_locked_bundles_elapsed.as_us()); + + execution_results + .into_iter() + .zip(sanitized_bundles.into_iter()) + .for_each( + |(bundle_execution_result, (packet_bundle, _))| match bundle_execution_result { + Ok(_) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_ok(1); + } + Err(BundleExecutionError::PohMaxHeightError) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_poh_max_height(1); + // retry the bundle + unprocessed_bundles.push_back(packet_bundle); + } + Err(BundleExecutionError::TransactionFailure(_)) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_transaction_failures(1); + } + Err(BundleExecutionError::ExceedsCostModel) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_exceeds_cost_model(1); + } + Err(BundleExecutionError::TipError(_)) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_tip_errors(1); + } + Err(BundleExecutionError::Shutdown) => {} + Err(BundleExecutionError::MaxRetriesExceeded(_)) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_max_retries(1); + } + Err(BundleExecutionError::LockError) => { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_execution_results_lock_errors(1); + } + }, + ); + } + + /// This only needs to be done once on program initialization + /// TODO (LB): may make sense to remove this and move to program deployment instead, but helpful + /// during development + #[allow(clippy::too_many_arguments)] + fn maybe_initialize_tip_accounts( + bundle_account_locker: &BundleAccountLocker, + bank_start: &BankStart, + cluster_info: &Arc, + recorder: &TransactionRecorder, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + qos_service: &QosService, + tip_manager: &TipManager, + max_bundle_retry_duration: &Duration, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + ) -> BundleStageResult<()> { + let initialize_tip_accounts_bundle = SanitizedBundle { + transactions: Self::get_initialize_tip_accounts_transactions( + &bank_start.working_bank, + tip_manager, + cluster_info, + )?, + }; + if !initialize_tip_accounts_bundle.transactions.is_empty() { + debug!("initialize tip account"); + + let locked_init_tip_bundle = bundle_account_locker + .prepare_locked_bundle(&initialize_tip_accounts_bundle, &bank_start.working_bank) + .map_err(|_| BundleExecutionError::LockError)?; + let result = Self::update_qos_and_execute_record_commit_bundle( + locked_init_tip_bundle.sanitized_bundle(), + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + bank_start, + bundle_stage_leader_stats, + max_bundle_retry_duration, + ); + + match &result { + Ok(_) => { + debug!("initialize tip account: success"); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_init_tip_account_ok(1); + } + Err(e) => { + error!("initialize tip account error: {:?}", e); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_init_tip_account_errors(1); + } + } + result + } else { + Ok(()) + } + } + + /// change tip receiver, draining tips to the previous tip_receiver in the process + /// note that this needs to happen after the above tip-related bundle initializes + /// config accounts because get_configured_tip_receiver relies on an account + /// existing in the bank + #[allow(clippy::too_many_arguments)] + fn maybe_change_tip_receiver( + bundle_account_locker: &BundleAccountLocker, + bank_start: &BankStart, + cluster_info: &Arc, + recorder: &TransactionRecorder, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + qos_service: &QosService, + tip_manager: &TipManager, + max_bundle_retry_duration: &Duration, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + ) -> BundleStageResult<()> { + let start_handle_tips = Instant::now(); + + let configured_tip_receiver = + tip_manager.get_configured_tip_receiver(&bank_start.working_bank)?; + let my_tip_distribution_pda = + tip_manager.get_my_tip_distribution_pda(bank_start.working_bank.epoch()); + if configured_tip_receiver != my_tip_distribution_pda { + info!( + "changing tip receiver from {} to {}", + configured_tip_receiver, my_tip_distribution_pda + ); + + let change_tip_receiver_tx = tip_manager.change_tip_receiver_tx( + &my_tip_distribution_pda, + &bank_start.working_bank, + &cluster_info.keypair(), + )?; + + let change_tip_receiver_bundle = SanitizedBundle { + transactions: vec![change_tip_receiver_tx], + }; + let locked_change_tip_receiver_bundle = bundle_account_locker + .prepare_locked_bundle(&change_tip_receiver_bundle, &bank_start.working_bank) + .map_err(|_| BundleExecutionError::LockError)?; + let result = Self::update_qos_and_execute_record_commit_bundle( + locked_change_tip_receiver_bundle.sanitized_bundle(), + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + bank_start, + bundle_stage_leader_stats, + max_bundle_retry_duration, + ); + + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_change_tip_receiver_elapsed_us( + start_handle_tips.elapsed().as_micros() as u64 + ); + + match &result { + Ok(_) => { + debug!("change tip receiver: success"); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_change_tip_receiver_ok(1); + } + Err(e) => { + error!("change tip receiver: error {:?}", e); + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_change_tip_receiver_errors(1); + } + } + result + } else { + Ok(()) + } + } + + #[allow(clippy::too_many_arguments)] + fn execute_locked_bundles( + bundle_account_locker: &BundleAccountLocker, + locked_bundles: Vec>, + bank_start: &BankStart, + cluster_info: &Arc, + recorder: &TransactionRecorder, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + qos_service: &QosService, + tip_manager: &TipManager, + max_bundle_retry_duration: &Duration, + last_tip_update_slot: &mut Slot, + bundle_stage_leader_stats: &mut BundleStageLeaderStats, + ) -> Vec> { + let tip_pdas = tip_manager.get_tip_accounts(); + + // make sure each locked_bundle is dropped after processing to unlock BankingStage + locked_bundles + .into_iter() + .map(|maybe_locked_bundle| { + let locked_bundle = maybe_locked_bundle.as_ref().map_err(|_| { + bundle_stage_leader_stats + .bundle_stage_stats() + .increment_num_lock_errors(1); + + BundleExecutionError::LockError + })?; + + if !Bank::should_bank_still_be_processing_txs( + &bank_start.bank_creation_time, + bank_start.working_bank.ns_per_slot, + ) { + Err(BundleExecutionError::PohMaxHeightError) + } else { + let sanitized_bundle = locked_bundle.sanitized_bundle(); + + if Self::bundle_touches_tip_pdas(&sanitized_bundle.transactions, &tip_pdas) + && bank_start.working_bank.slot() != *last_tip_update_slot + { + Self::maybe_initialize_tip_accounts( + bundle_account_locker, + bank_start, + cluster_info, + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + tip_manager, + max_bundle_retry_duration, + bundle_stage_leader_stats, + )?; + + Self::maybe_change_tip_receiver( + bundle_account_locker, + bank_start, + cluster_info, + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + tip_manager, + max_bundle_retry_duration, + bundle_stage_leader_stats, + )?; + + *last_tip_update_slot = bank_start.working_bank.slot(); + } + + Self::update_qos_and_execute_record_commit_bundle( + sanitized_bundle, + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + bank_start, + bundle_stage_leader_stats, + max_bundle_retry_duration, + ) + } + }) + .collect() + } + + fn receive_and_buffer_bundles( + bundle_receiver: &Receiver>, + unprocessed_bundles: &mut VecDeque, + timeout: Duration, + ) -> Result { + let bundles = bundle_receiver.recv_timeout(timeout)?; + let num_bundles_before = unprocessed_bundles.len(); + unprocessed_bundles.extend(bundles); + unprocessed_bundles.extend(bundle_receiver.try_iter().flatten()); + let num_bundles_after = unprocessed_bundles.len(); + Ok(num_bundles_after - num_bundles_before) + } + + #[allow(clippy::too_many_arguments)] + fn process_buffered_bundles( + bundle_account_locker: &BundleAccountLocker, + unprocessed_bundles: &mut VecDeque, + blacklisted_accounts: &HashSet, + consensus_cache_updater: &mut ConsensusCacheUpdater, + cluster_info: &Arc, + recorder: &TransactionRecorder, + poh_recorder: &Arc>, + transaction_status_sender: &Option, + gossip_vote_sender: &ReplayVoteSender, + qos_service: &QosService, + tip_manager: &TipManager, + max_bundle_retry_duration: &Duration, + last_tip_update_slot: &mut u64, + bundle_stage_leader_stats: &mut BundleStageLeaderSlotTrackingMetrics, + bundle_stage_stats: &mut BundleStageLoopStats, + id: u32, + ) { + const DROP_BUNDLE_SLOT_OFFSET: u64 = 4; + + let r_poh_recorder = poh_recorder.read().unwrap(); + let poh_recorder_bank = r_poh_recorder.get_poh_recorder_bank(); + let working_bank_start = poh_recorder_bank.working_bank_start(); + let would_be_leader_soon = + r_poh_recorder.would_be_leader(DROP_BUNDLE_SLOT_OFFSET * DEFAULT_TICKS_PER_SLOT); + drop(r_poh_recorder); + + bundle_stage_leader_stats.maybe_report(id, &working_bank_start); + + match (working_bank_start, would_be_leader_soon) { + // leader now, insert new read bundles + as many as can read then return bank + (Some(bank_start), _) => { + consensus_cache_updater.maybe_update(&bank_start.working_bank); + + Self::execute_bundles_until_empty_or_end_of_slot( + bundle_account_locker, + unprocessed_bundles, + blacklisted_accounts, + bank_start, + consensus_cache_updater.consensus_accounts_cache(), + cluster_info, + recorder, + transaction_status_sender, + gossip_vote_sender, + qos_service, + tip_manager, + max_bundle_retry_duration, + last_tip_update_slot, + bundle_stage_leader_stats.bundle_stage_leader_stats(), + ); + } + // not leader now and not soon, clear bundles + (None, false) => { + saturating_add_assign!( + bundle_stage_stats.num_bundles_dropped, + unprocessed_bundles.len() as u64 + ); + + unprocessed_bundles.clear(); + } + _ => {} + } + } + + #[allow(clippy::too_many_arguments)] + fn process_loop( + cluster_info: Arc, + poh_recorder: &Arc>, + transaction_status_sender: Option, + bundle_receiver: Receiver>, + gossip_vote_sender: ReplayVoteSender, + id: u32, + cost_model: Arc>, + exit: Arc, + tip_manager: TipManager, + bundle_account_locker: BundleAccountLocker, + max_bundle_retry_duration: Duration, + ) { + const LOOP_STATS_METRICS_PERIOD: Duration = Duration::from_secs(1); + + let recorder = poh_recorder.read().unwrap().recorder(); + let qos_service = QosService::new(cost_model, id); + + // Bundles can't mention any accounts related to consensus + let mut consensus_cache_updater = ConsensusCacheUpdater::default(); + let mut last_tip_update_slot = Slot::default(); + + let mut last_leader_slots_update_time = Instant::now(); + let mut bundle_stage_leader_stats = BundleStageLeaderSlotTrackingMetrics::default(); + let mut bundle_stage_stats = BundleStageLoopStats::default(); + + // Bundles can't mention the tip payment program to ensure that a malicious entity doesn't + // steal tips mid-slot + let blacklisted_accounts = HashSet::from_iter([tip_manager.tip_payment_program_id()]); + + let mut unprocessed_bundles: VecDeque = VecDeque::with_capacity(1000); + while !exit.load(Ordering::Relaxed) { + if !unprocessed_bundles.is_empty() + || last_leader_slots_update_time.elapsed() >= SLOT_BOUNDARY_CHECK_PERIOD + { + let (_, process_buffered_bundles_elapsed) = measure!( + Self::process_buffered_bundles( + &bundle_account_locker, + &mut unprocessed_bundles, + &blacklisted_accounts, + &mut consensus_cache_updater, + &cluster_info, + &recorder, + poh_recorder, + &transaction_status_sender, + &gossip_vote_sender, + &qos_service, + &tip_manager, + &max_bundle_retry_duration, + &mut last_tip_update_slot, + &mut bundle_stage_leader_stats, + &mut bundle_stage_stats, + id, + ), + "process_buffered_bundles_elapsed" + ); + + saturating_add_assign!( + bundle_stage_stats.process_buffered_bundles_elapsed_us, + process_buffered_bundles_elapsed.as_us() + ); + last_leader_slots_update_time = Instant::now(); + } + + bundle_stage_stats.maybe_report(id, LOOP_STATS_METRICS_PERIOD); + + // ensure bundle stage can run immediately if bundles to process, otherwise okay + // chilling for a few + let sleep_time = if !unprocessed_bundles.is_empty() { + Duration::from_millis(0) + } else { + Duration::from_millis(10) + }; + + let (res, receive_and_buffer_elapsed) = measure!( + Self::receive_and_buffer_bundles( + &bundle_receiver, + &mut unprocessed_bundles, + sleep_time, + ), + "receive_and_buffer_elapsed" + ); + saturating_add_assign!( + bundle_stage_stats.receive_and_buffer_bundles_elapsed_us, + receive_and_buffer_elapsed.as_us() + ); + + match res { + Ok(num_bundles_received) => { + saturating_add_assign!( + bundle_stage_stats.num_bundles_received, + num_bundles_received as u64 + ); + } + Err(RecvTimeoutError::Timeout) => {} + Err(RecvTimeoutError::Disconnected) => { + break; + } + } + } + } + + fn prepare_poh_record_bundle( + bank_slot: &Slot, + execution_results_txs: &[AllExecutionResults], + record_transactions_timings: &mut RecordTransactionsTimings, + ) -> (Slot, Vec<(Hash, Vec)>) { + let mut new_record_transaction_timings = RecordTransactionsTimings::default(); + + let mixins_txs = execution_results_txs + .iter() + .map(|r| { + let (processed_transactions, results_to_transactions_elapsed) = measure!( + { + r.load_and_execute_tx_output + .execution_results + .iter() + .zip(r.sanitized_txs.iter()) + .filter_map(|(execution_result, tx)| { + if execution_result.was_executed() { + Some(tx.to_versioned_transaction()) + } else { + None + } + }) + .collect::>() + }, + "results_to_transactions_elapsed" + ); + + let (hash, hash_elapsed) = measure!( + hash_transactions(&processed_transactions[..]), + "hash_elapsed" + ); + + saturating_add_assign!( + new_record_transaction_timings.execution_results_to_transactions_us, + results_to_transactions_elapsed.as_us() + ); + saturating_add_assign!( + new_record_transaction_timings.hash_us, + hash_elapsed.as_us() + ); + + (hash, processed_transactions) + }) + .collect(); + + record_transactions_timings.accumulate(&new_record_transaction_timings); + + (*bank_slot, mixins_txs) + } + + pub fn join(self) -> thread::Result<()> { + self.bundle_thread.join() + } + + fn try_record( + recorder: &TransactionRecorder, + bank_slot: Slot, + mixins_txs: Vec<(Hash, Vec)>, + ) -> BundleStageResult> { + match recorder.record(bank_slot, mixins_txs) { + Ok(maybe_tx_index) => Ok(maybe_tx_index), + Err(PohRecorderError::MaxHeightReached) => Err(BundleExecutionError::PohMaxHeightError), + Err(e) => panic!("Poh recorder returned unexpected error: {:?}", e), + } + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::bundle_stage::tests::TestOption::{ + AssertDuplicateInBundleDropped, AssertNonZeroCostModel, AssertZeroedCostModel, + LowComputeBudget, + }, + crossbeam_channel::unbounded, + solana_ledger::{ + blockstore::Blockstore, + genesis_utils::{create_genesis_config, GenesisConfigInfo}, + get_tmp_ledger_path_auto_delete, + }, + solana_perf::packet::PacketBatch, + solana_poh::poh_recorder::create_test_recorder, + solana_sdk::{ + bundle::error::BundleExecutionError::{ + ExceedsCostModel, PohMaxHeightError, TransactionFailure, + }, + compute_budget::ComputeBudgetInstruction, + genesis_config::GenesisConfig, + instruction::InstructionError, + message::Message, + packet::Packet, + poh_config::PohConfig, + signature::{Keypair, Signer}, + system_instruction, + system_transaction::{self, transfer}, + transaction::{ + Transaction, + TransactionError::{self, AccountNotFound}, + }, + }, + std::{collections::HashSet, sync::atomic::Ordering}, + uuid::Uuid, + }; + + const TEST_MAX_RETRY_DURATION: Duration = Duration::from_millis(500); + + enum TestOption { + LowComputeBudget, + AssertZeroedCostModel, + AssertNonZeroCostModel, + AssertDuplicateInBundleDropped, + } + + #[cfg(test)] + fn test_single_bundle( + genesis_config: GenesisConfig, + bundle: PacketBundle, + options: Option>, + ) -> Result<(), BundleExecutionError> { + solana_logger::setup(); + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + // start a banking_stage to eat verified receiver + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + if options.is_some() + && options + .as_ref() + .unwrap() + .iter() + .any(|option| matches!(option, LowComputeBudget)) + { + bank.write_cost_tracker().unwrap().set_limits(1, 1, 1); + } + let blockstore = Arc::new( + Blockstore::open(ledger_path.path()) + .expect("Expected to be able to open database ledger"), + ); + let poh_config = PohConfig { + // limit tick count to avoid clearing working_bank at + // PohRecord then PohRecorderError(MaxHeightReached) at BankingStage + target_tick_count: Some(bank.max_tick_height() - 1), // == 1, only enough for ticks, not txs + ..PohConfig::default() + }; + let (exit, poh_recorder, poh_service, _entry_receiver) = + create_test_recorder(&bank, &blockstore, Some(poh_config), None); + let recorder = poh_recorder.read().unwrap().recorder(); + let cost_model = Arc::new(RwLock::new(CostModel::default())); + let qos_service = QosService::new(cost_model, 0); + let mut bundle_stage_leader_stats = BundleStageLeaderStats::default(); + let bank_start = poh_recorder.read().unwrap().bank_start().unwrap(); + let sanitized_bundle = get_sanitized_bundle( + &bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + bundle_stage_leader_stats.transaction_errors(), + ) + .unwrap(); + + let results = BundleStage::update_qos_and_execute_record_commit_bundle( + &sanitized_bundle, + &recorder, + &None, + &gossip_vote_sender, + &qos_service, + &bank_start, + &mut bundle_stage_leader_stats, + &TEST_MAX_RETRY_DURATION, + ); + + // This is ugly, not really an option for testing but a test itself. + // Still preferable to duplicating the entirety of this method + // just to test duplicate txs are dropped. + if options.is_some() + && options + .as_ref() + .unwrap() + .iter() + .any(|option| matches!(option, AssertDuplicateInBundleDropped)) + { + assert_eq!(results, Ok(())); + assert!(get_sanitized_bundle( + &bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + bundle_stage_leader_stats.transaction_errors(), + ) + .is_err()); + } + + // Transaction rolled back successfully if + // cost tracker has 0 transaction count + // cost tracker as 0 block cost + if options.is_some() + && options + .as_ref() + .unwrap() + .iter() + .any(|option| matches!(option, AssertZeroedCostModel)) + { + assert_eq!(bank.read_cost_tracker().unwrap().transaction_count(), 0); + assert_eq!(bank.read_cost_tracker().unwrap().block_cost(), 0); + } + + if options.is_some() + && options + .as_ref() + .unwrap() + .iter() + .any(|option| matches!(option, AssertNonZeroCostModel)) + { + assert_ne!(bank.read_cost_tracker().unwrap().transaction_count(), 0); + assert_ne!(bank.read_cost_tracker().unwrap().block_cost(), 0); + } + + exit.store(true, Ordering::Relaxed); + poh_service.join().unwrap(); + results + } + + #[test] + fn test_successful_bundle() { + let (genesis_config, bundle) = setup_successful_tx(); + assert_eq!( + test_single_bundle(genesis_config, bundle, Some(vec![AssertNonZeroCostModel])), + Ok(()) + ); + } + + #[test] + fn test_bundle_contains_processed_transaction() { + let (genesis_config, bundle) = setup_successful_tx(); + assert_eq!( + test_single_bundle( + genesis_config, + bundle, + Some(vec![AssertDuplicateInBundleDropped]), + ), + Ok(()) + ); + } + + #[cfg(test)] + fn setup_successful_tx() -> (GenesisConfig, PacketBundle) { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(5); + + let kp_a = Keypair::new(); + let kp_b = Keypair::new(); + let ix_mint_a = system_instruction::transfer(&mint_keypair.pubkey(), &kp_a.pubkey(), 1); + let ix_mint_b = system_instruction::transfer(&mint_keypair.pubkey(), &kp_b.pubkey(), 1); + let message = Message::new(&[ix_mint_a, ix_mint_b], Some(&mint_keypair.pubkey())); + let tx = Transaction::new(&[&mint_keypair], message, genesis_config.hash()); + let packet = Packet::from_data(None, tx).unwrap(); + + ( + genesis_config, + PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }, + ) + } + + #[test] + fn test_txs_exceed_cost_model() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(5); + + let kp = Keypair::new(); + let instruction = system_instruction::transfer(&mint_keypair.pubkey(), &kp.pubkey(), 1); + let message = Message::new( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1), + instruction, + ], + Some(&mint_keypair.pubkey()), + ); + let tx = Transaction::new(&[&mint_keypair], message, genesis_config.hash()); + let packet = Packet::from_data(None, tx).unwrap(); + + let bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + assert_eq!( + test_single_bundle(genesis_config, bundle, Some(vec![LowComputeBudget])), + Err(ExceedsCostModel) + ); + } + + #[test] + fn test_nonce_tx_failure() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(4); + + let kp_a = Keypair::new(); + let kp_nonce = Keypair::new(); + let kp_nonce_authority = Keypair::new(); + let packet = Packet::from_data( + None, + system_transaction::nonced_transfer( + &mint_keypair, + &kp_a.pubkey(), + 1, + &kp_nonce.pubkey(), + &kp_nonce_authority, + genesis_config.hash(), + ), + ) + .unwrap(); + let bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + assert_eq!( + test_single_bundle(genesis_config, bundle, None), + Err(TransactionFailure(TransactionError::InstructionError( + 0, + InstructionError::InvalidAccountData, + ))) + ); + } + + #[test] + fn test_qos_rollback() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(4); + + let kp_a = Keypair::new(); + let kp_b = Keypair::new(); + let successful_packet = Packet::from_data( + None, + system_transaction::transfer(&mint_keypair, &kp_b.pubkey(), 1, genesis_config.hash()), + ) + .unwrap(); + let failed_packet = Packet::from_data( + None, + system_transaction::transfer(&kp_a, &kp_b.pubkey(), 1, genesis_config.hash()), + ) + .unwrap(); + let bundle = PacketBundle { + batch: PacketBatch::new(vec![successful_packet, failed_packet]), + uuid: Uuid::new_v4(), + }; + + assert_eq!( + test_single_bundle(genesis_config, bundle, Some(vec![AssertZeroedCostModel])), + Err(TransactionFailure(AccountNotFound)) + ); + } + + #[test] + fn test_zero_balance_account() { + let GenesisConfigInfo { + genesis_config, + mint_keypair: _, + .. + } = create_genesis_config(4); + + let kp_a = Keypair::new(); + let kp_b = Keypair::new(); + let packet = Packet::from_data( + None, + system_transaction::transfer(&kp_a, &kp_b.pubkey(), 1, genesis_config.hash()), + ) + .unwrap(); + let bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + + assert_eq!( + test_single_bundle(genesis_config, bundle, None), + Err(TransactionFailure(AccountNotFound)) + ); + } + + #[test] + fn test_bundle_fails_poh_record() { + solana_logger::setup(); + let GenesisConfigInfo { + mut genesis_config, + mint_keypair, + .. + } = create_genesis_config(4); + genesis_config.ticks_per_slot = 1; // Reduce ticks so that POH fails + + let kp_b = Keypair::new(); + let packet = Packet::from_data( + None, + system_transaction::transfer(&mint_keypair, &kp_b.pubkey(), 1, genesis_config.hash()), + ) + .unwrap(); + let bundle = PacketBundle { + batch: PacketBatch::new(vec![packet]), + uuid: Uuid::new_v4(), + }; + assert_eq!( + test_single_bundle(genesis_config, bundle, None), + Err(PohMaxHeightError) + ); + } + + #[test] + fn test_bundle_max_retries() { + solana_logger::setup_with_default("INFO"); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(100_000_000); + let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config)); + bank.write_cost_tracker() + .unwrap() + .set_limits(u64::MAX, u64::MAX, u64::MAX); + + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Arc::new( + Blockstore::open(ledger_path.path()) + .expect("Expected to be able to open database ledger"), + ); + let poh_config = PohConfig { + // limit tick count to avoid clearing working_bank at + // PohRecord then PohRecorderError(MaxHeightReached) at BankingStage + target_tick_count: Some(bank.max_tick_height() - 1), // == 1, only enough for ticks, not txs + ..PohConfig::default() + }; + let (exit, poh_recorder, poh_service, _entry_receiver) = + create_test_recorder(&bank, &blockstore, Some(poh_config), None); + let recorder = poh_recorder.read().unwrap().recorder(); + let (gossip_vote_sender, _gossip_vote_receiver) = unbounded(); + let cost_model = Arc::new(RwLock::new(CostModel::default())); + let qos_service = QosService::new(cost_model, 0); + let mut bundle_stage_leader_stats = BundleStageLeaderStats::default(); + let bank_start = poh_recorder.read().unwrap().bank_start().unwrap(); + + // Create two transfers + // 0. mint_keypair -> keypair0 + // 1. keypair0 -> keypair 1 + // Lock the accounts through the bank for tx1 and try to process tx0. + // It should timeout because BundleStage will continue to fail to get locks on keypair0. + + let keypair0 = Keypair::new(); + let keypair1 = Keypair::new(); + + let tx0 = VersionedTransaction::from(transfer( + &mint_keypair, + &keypair0.pubkey(), + 100_000, + genesis_config.hash(), + )); + + let tx1 = transfer(&keypair0, &keypair1.pubkey(), 50_000, genesis_config.hash()); + let sanitized_txs_1 = vec![SanitizedTransaction::from_transaction_for_tests(tx1)]; + + // grab lock on tx1 + let _batch = bank.prepare_sanitized_batch(&sanitized_txs_1); + + // push and pop tx0 + let bundle = PacketBundle { + batch: PacketBatch::new(vec![Packet::from_data(None, tx0).unwrap()]), + uuid: Uuid::new_v4(), + }; + info!("test_bundle_max_retries uuid: {:?}", bundle.uuid); + + let sanitized_bundle = get_sanitized_bundle( + &bundle, + &bank, + &HashSet::default(), + &HashSet::default(), + bundle_stage_leader_stats.transaction_errors(), + ) + .unwrap(); + + let result = BundleStage::update_qos_and_execute_record_commit_bundle( + &sanitized_bundle, + &recorder, + &None, + &gossip_vote_sender, + &qos_service, + &bank_start, + &mut bundle_stage_leader_stats, + &TEST_MAX_RETRY_DURATION, + ); + info!("test_bundle_max_retries result: {:?}", result); + assert!(matches!( + result, + Err(BundleExecutionError::MaxRetriesExceeded(_)) + )); + + exit.store(true, Ordering::Relaxed); + poh_service.join().unwrap(); + } +} diff --git a/core/src/bundle_stage_leader_stats.rs b/core/src/bundle_stage_leader_stats.rs new file mode 100644 index 0000000000..1381140707 --- /dev/null +++ b/core/src/bundle_stage_leader_stats.rs @@ -0,0 +1,326 @@ +use { + crate::leader_slot_banking_stage_timing_metrics::LeaderExecuteAndCommitTimings, + solana_poh::poh_recorder::BankStart, + solana_runtime::transaction_error_metrics::TransactionErrorMetrics, + solana_sdk::{clock::Slot, saturating_add_assign}, +}; + +// Stats emitted only during leader slots +#[derive(Default)] +pub struct BundleStageLeaderSlotTrackingMetrics { + current_slot: Option, + bundle_stage_leader_stats: BundleStageLeaderStats, +} + +impl BundleStageLeaderSlotTrackingMetrics { + pub fn maybe_report(&mut self, id: u32, bank_start: &Option<&BankStart>) { + match (self.current_slot, bank_start) { + // not was leader, not is leader + (None, None) => {} + // was leader, not leader anymore + (Some(current_slot), None) => { + self.bundle_stage_leader_stats.report(id, current_slot); + self.bundle_stage_leader_stats = BundleStageLeaderStats::default(); + } + // was leader, is leader + (Some(current_slot), Some(bank_start)) => { + if current_slot != bank_start.working_bank.slot() { + self.bundle_stage_leader_stats.report(id, current_slot); + self.bundle_stage_leader_stats = BundleStageLeaderStats::default(); + } + } + // not was leader, is leader + (None, Some(_)) => { + self.bundle_stage_leader_stats = BundleStageLeaderStats::default(); + } + } + + self.current_slot = bank_start + .as_ref() + .map(|bank_start| bank_start.working_bank.slot()); + } + + pub fn bundle_stage_leader_stats(&mut self) -> &mut BundleStageLeaderStats { + &mut self.bundle_stage_leader_stats + } +} + +#[derive(Default)] +pub struct BundleStageLeaderStats { + transaction_errors: TransactionErrorMetrics, + execute_and_commit_timings: LeaderExecuteAndCommitTimings, + bundle_stage_stats: BundleStageStats, +} + +impl BundleStageLeaderStats { + pub fn transaction_errors(&mut self) -> &mut TransactionErrorMetrics { + &mut self.transaction_errors + } + + pub fn execute_and_commit_timings(&mut self) -> &mut LeaderExecuteAndCommitTimings { + &mut self.execute_and_commit_timings + } + + pub fn bundle_stage_stats(&mut self) -> &mut BundleStageStats { + &mut self.bundle_stage_stats + } + + pub fn report(&self, id: u32, slot: Slot) { + self.transaction_errors.report(id, slot); + self.execute_and_commit_timings.report(id, slot); + self.bundle_stage_stats.report(id, slot); + } +} + +#[derive(Default)] +pub struct BundleStageStats { + sanitize_transaction_ok: u64, + sanitize_transaction_vote_only_mode: u64, + sanitize_transaction_failed_precheck: u64, + sanitize_transaction_blacklisted_account: u64, + sanitize_transaction_failed_to_serialize: u64, + sanitize_transaction_duplicate_transaction: u64, + sanitize_transaction_failed_check: u64, + sanitize_bundle_elapsed_us: u64, + + locked_bundle_elapsed_us: u64, + + num_lock_errors: u64, + + num_init_tip_account_errors: u64, + num_init_tip_account_ok: u64, + + num_change_tip_receiver_errors: u64, + num_change_tip_receiver_ok: u64, + change_tip_receiver_elapsed_us: u64, + + num_execution_failures: u64, + num_execution_timeouts: u64, + num_execution_retries: u64, + + execute_locked_bundles_elapsed_us: u64, + + execution_results_ok: u64, + execution_results_poh_max_height: u64, + execution_results_transaction_failures: u64, + execution_results_exceeds_cost_model: u64, + execution_results_tip_errors: u64, + execution_results_max_retries: u64, + execution_results_lock_errors: u64, +} + +impl BundleStageStats { + pub fn report(&self, id: u32, slot: Slot) { + datapoint_info!( + "bundle_stage-stats", + ("id", id, i64), + ("slot", slot, i64), + ("num_sanitized_ok", self.sanitize_transaction_ok, i64), + ( + "sanitize_transaction_vote_only_mode", + self.sanitize_transaction_vote_only_mode, + i64 + ), + ( + "sanitize_transaction_failed_precheck", + self.sanitize_transaction_failed_precheck, + i64 + ), + ( + "sanitize_transaction_blacklisted_account", + self.sanitize_transaction_blacklisted_account, + i64 + ), + ( + "sanitize_transaction_failed_to_serialize", + self.sanitize_transaction_failed_to_serialize, + i64 + ), + ( + "sanitize_transaction_duplicate_transaction", + self.sanitize_transaction_duplicate_transaction, + i64 + ), + ( + "sanitize_transaction_failed_check", + self.sanitize_transaction_failed_check, + i64 + ), + ( + "sanitize_bundle_elapsed_us", + self.sanitize_bundle_elapsed_us, + i64 + ), + ( + "locked_bundle_elapsed_us", + self.locked_bundle_elapsed_us, + i64 + ), + ("num_lock_errors", self.num_lock_errors, i64), + ( + "num_init_tip_account_errors", + self.num_init_tip_account_errors, + i64 + ), + ("num_init_tip_account_ok", self.num_init_tip_account_ok, i64), + ( + "num_change_tip_receiver_errors", + self.num_change_tip_receiver_errors, + i64 + ), + ( + "num_change_tip_receiver_ok", + self.num_change_tip_receiver_ok, + i64 + ), + ( + "change_tip_receiver_elapsed_us", + self.change_tip_receiver_elapsed_us, + i64 + ), + ("num_execution_failures", self.num_execution_failures, i64), + ("num_execution_timeouts", self.num_execution_timeouts, i64), + ("num_execution_retries", self.num_execution_retries, i64), + ( + "execute_locked_bundles_elapsed_us", + self.execute_locked_bundles_elapsed_us, + i64 + ), + ("execution_results_ok", self.execution_results_ok, i64), + ( + "execution_results_poh_max_height", + self.execution_results_poh_max_height, + i64 + ), + ( + "execution_results_transaction_failures", + self.execution_results_transaction_failures, + i64 + ), + ( + "execution_results_exceeds_cost_model", + self.execution_results_exceeds_cost_model, + i64 + ), + ( + "execution_results_tip_errors", + self.execution_results_tip_errors, + i64 + ), + ( + "execution_results_max_retries", + self.execution_results_max_retries, + i64 + ), + ( + "execution_results_lock_errors", + self.execution_results_lock_errors, + i64 + ), + ); + } + + pub fn increment_sanitize_transaction_ok(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_ok, num); + } + + pub fn increment_sanitize_transaction_vote_only_mode(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_vote_only_mode, num); + } + + pub fn increment_sanitize_transaction_failed_precheck(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_failed_precheck, num); + } + + pub fn increment_sanitize_transaction_blacklisted_account(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_blacklisted_account, num); + } + + pub fn increment_sanitize_transaction_failed_to_serialize(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_failed_to_serialize, num); + } + + pub fn increment_sanitize_transaction_duplicate_transaction(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_duplicate_transaction, num); + } + + pub fn increment_sanitize_transaction_failed_check(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_transaction_failed_check, num); + } + + pub fn increment_sanitize_bundle_elapsed_us(&mut self, num: u64) { + saturating_add_assign!(self.sanitize_bundle_elapsed_us, num); + } + + pub fn increment_locked_bundle_elapsed_us(&mut self, num: u64) { + saturating_add_assign!(self.locked_bundle_elapsed_us, num); + } + + pub fn increment_num_lock_errors(&mut self, num: u64) { + saturating_add_assign!(self.num_lock_errors, num); + } + + pub fn increment_num_init_tip_account_errors(&mut self, num: u64) { + saturating_add_assign!(self.num_init_tip_account_errors, num); + } + + pub fn increment_num_init_tip_account_ok(&mut self, num: u64) { + saturating_add_assign!(self.num_init_tip_account_ok, num); + } + + pub fn increment_num_change_tip_receiver_errors(&mut self, num: u64) { + saturating_add_assign!(self.num_change_tip_receiver_errors, num); + } + + pub fn increment_num_change_tip_receiver_ok(&mut self, num: u64) { + saturating_add_assign!(self.num_change_tip_receiver_ok, num); + } + + pub fn increment_change_tip_receiver_elapsed_us(&mut self, num: u64) { + saturating_add_assign!(self.change_tip_receiver_elapsed_us, num); + } + + pub fn increment_num_execution_failures(&mut self, num: u64) { + saturating_add_assign!(self.num_execution_failures, num); + } + + pub fn increment_num_execution_timeouts(&mut self, num: u64) { + saturating_add_assign!(self.num_execution_timeouts, num); + } + + pub fn increment_num_execution_retries(&mut self, num: u64) { + saturating_add_assign!(self.num_execution_retries, num); + } + + pub fn increment_execute_locked_bundles_elapsed_us(&mut self, num: u64) { + saturating_add_assign!(self.execute_locked_bundles_elapsed_us, num); + } + + pub fn increment_execution_results_ok(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_ok, num); + } + + pub fn increment_execution_results_poh_max_height(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_poh_max_height, num); + } + + pub fn increment_execution_results_transaction_failures(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_transaction_failures, num); + } + + pub fn increment_execution_results_exceeds_cost_model(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_exceeds_cost_model, num); + } + + pub fn increment_execution_results_tip_errors(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_tip_errors, num); + } + + pub fn increment_execution_results_max_retries(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_max_retries, num); + } + + pub fn increment_execution_results_lock_errors(&mut self, num: u64) { + saturating_add_assign!(self.execution_results_lock_errors, num); + } +} diff --git a/core/src/consensus_cache_updater.rs b/core/src/consensus_cache_updater.rs new file mode 100644 index 0000000000..0514f4133b --- /dev/null +++ b/core/src/consensus_cache_updater.rs @@ -0,0 +1,52 @@ +use { + solana_runtime::bank::Bank, + solana_sdk::{clock::Epoch, pubkey::Pubkey}, + std::collections::HashSet, +}; + +#[derive(Default)] +pub(crate) struct ConsensusCacheUpdater { + last_epoch_updated: Epoch, + consensus_accounts_cache: HashSet, +} + +impl ConsensusCacheUpdater { + pub(crate) fn consensus_accounts_cache(&self) -> &HashSet { + &self.consensus_accounts_cache + } + + /// Builds a HashSet of all consensus related accounts for the Bank's epoch + fn get_consensus_accounts(bank: &Bank) -> HashSet { + let mut consensus_accounts: HashSet = HashSet::new(); + if let Some(epoch_stakes) = bank.epoch_stakes(bank.epoch()) { + // votes use the following accounts: + // - vote_account pubkey: writeable + // - authorized_voter_pubkey: read-only + // - node_keypair pubkey: payer (writeable) + let node_id_vote_accounts = epoch_stakes.node_id_to_vote_accounts(); + + let vote_accounts = node_id_vote_accounts + .values() + .into_iter() + .flat_map(|v| v.vote_accounts.clone()); + + // vote_account + consensus_accounts.extend(vote_accounts.into_iter()); + // authorized_voter_pubkey + consensus_accounts.extend(epoch_stakes.epoch_authorized_voters().keys().into_iter()); + // node_keypair + consensus_accounts.extend(epoch_stakes.node_id_to_vote_accounts().keys().into_iter()); + } + consensus_accounts + } + + /// Updates consensus-related accounts on epoch boundaries + /// Bundles must not contain any consensus related accounts in order to prevent starvation + /// of voting related transactions + pub(crate) fn maybe_update(&mut self, bank: &Bank) { + if bank.epoch() > self.last_epoch_updated { + self.consensus_accounts_cache = Self::get_consensus_accounts(bank); + self.last_epoch_updated = bank.epoch(); + } + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index d521075f66..401bb519e0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -10,8 +10,13 @@ pub mod accounts_hash_verifier; pub mod ancestor_hashes_service; +mod backoff; pub mod banking_stage; pub mod broadcast_stage; +pub mod bundle_account_locker; +pub mod bundle_sanitizer; +pub mod bundle_stage; +mod bundle_stage_leader_stats; pub mod cache_block_meta_service; pub mod cluster_info_vote_listener; pub mod cluster_nodes; @@ -21,6 +26,7 @@ pub mod cluster_slots_service; pub mod commitment_service; pub mod completed_data_sets_service; pub mod consensus; +pub mod consensus_cache_updater; pub mod cost_update_service; pub mod drop_bank_service; pub mod duplicate_repair_status; @@ -39,12 +45,14 @@ pub mod ledger_cleanup_service; pub mod ledger_metric_report_service; pub mod optimistic_confirmation_verifier; pub mod outstanding_requests; +pub mod packet_bundle; pub mod packet_deserializer; mod packet_hasher; pub mod packet_threshold; pub mod poh_timing_report_service; pub mod poh_timing_reporter; pub mod progress_map; +pub mod proxy; pub mod qos_service; pub mod repair_generic_traversal; pub mod repair_response; @@ -67,6 +75,7 @@ pub mod snapshot_packager_service; pub mod staked_nodes_updater_service; pub mod stats_reporter_service; pub mod system_monitor_service; +pub mod tip_manager; mod tower1_7_14; pub mod tower_storage; pub mod tpu; @@ -102,3 +111,42 @@ extern crate solana_frozen_abi_macro; #[cfg(test)] #[macro_use] extern crate matches; + +use { + solana_sdk::packet::{Meta, Packet, PacketFlags, PACKET_DATA_SIZE}, + std::{ + cmp::min, + net::{IpAddr, Ipv4Addr}, + }, +}; + +const UNKNOWN_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + +// NOTE: last profiled at around 180ns +pub fn proto_packet_to_packet(p: jito_protos::proto::packet::Packet) -> Packet { + let mut data = [0; PACKET_DATA_SIZE]; + let copy_len = min(data.len(), p.data.len()); + data[..copy_len].copy_from_slice(&p.data[..copy_len]); + let mut packet = Packet::new(data, Meta::default()); + if let Some(meta) = p.meta { + packet.meta.size = meta.size as usize; + packet.meta.addr = meta.addr.parse().unwrap_or(UNKNOWN_IP); + packet.meta.port = meta.port as u16; + if let Some(flags) = meta.flags { + if flags.simple_vote_tx { + packet.meta.flags.insert(PacketFlags::SIMPLE_VOTE_TX); + } + if flags.forwarded { + packet.meta.flags.insert(PacketFlags::FORWARDED); + } + if flags.tracer_packet { + packet.meta.flags.insert(PacketFlags::TRACER_PACKET); + } + if flags.repair { + packet.meta.flags.insert(PacketFlags::REPAIR); + } + } + packet.meta.sender_stake = meta.sender_stake; + } + packet +} diff --git a/core/src/packet_bundle.rs b/core/src/packet_bundle.rs new file mode 100644 index 0000000000..f5a6a7ac6d --- /dev/null +++ b/core/src/packet_bundle.rs @@ -0,0 +1,7 @@ +use {solana_perf::packet::PacketBatch, uuid::Uuid}; + +#[derive(Clone, Debug)] +pub struct PacketBundle { + pub batch: PacketBatch, + pub uuid: Uuid, +} diff --git a/core/src/proxy/auth.rs b/core/src/proxy/auth.rs new file mode 100644 index 0000000000..b25edd4343 --- /dev/null +++ b/core/src/proxy/auth.rs @@ -0,0 +1,247 @@ +use { + chrono::Utc, + jito_protos::proto::auth::{ + auth_service_client::AuthServiceClient, GenerateAuthChallengeRequest, + GenerateAuthTokensRequest, RefreshAccessTokenRequest, Role, Token, + }, + solana_gossip::cluster_info::ClusterInfo, + solana_sdk::signature::{Keypair, Signer}, + std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, + time::Duration, + }, + tokio::time::sleep, + tonic::{service::Interceptor, transport::Channel, Request, Status}, +}; + +/// Interceptor responsible for adding the access token to request headers. +pub(crate) struct AuthInterceptor { + /// The token added to each request header. + access_token: Arc>, +} + +impl AuthInterceptor { + pub(crate) fn new(access_token: Arc>) -> Self { + Self { access_token } + } +} + +impl Interceptor for AuthInterceptor { + fn call(&mut self, mut request: Request<()>) -> Result, Status> { + request.metadata_mut().insert( + "authorization", + format!("Bearer {}", self.access_token.lock().unwrap().value) + .parse() + .unwrap(), + ); + + Ok(request) + } +} + +/// Contains collection of utility functions responsible for generating and refreshing new tokens. +pub(crate) mod token_manager { + use {super::*, crate::proxy::ProxyError, tonic::transport::Endpoint}; + + /// Control loop responsible for making sure access and refresh tokens are updated. + pub(crate) async fn auth_tokens_update_loop( + auth_service_endpoint: Endpoint, + access_token: Arc>, + cluster_info: Arc, + exit: Arc, + ) { + const RETRY_INTERVAL: Duration = Duration::from_secs(5); + const SLEEP_INTERVAL: Duration = Duration::from_secs(60); + + let mut num_refresh_loop_errors: u64 = 0; + let mut num_connect_errors: u64 = 0; + while !exit.load(Ordering::Relaxed) { + sleep(RETRY_INTERVAL).await; + + match auth_service_endpoint.connect().await { + Ok(channel) => { + if let Err(e) = auth_tokens_update_loop_helper( + AuthServiceClient::new(channel), + auth_service_endpoint.uri().to_string(), + (access_token.clone(), Token::default()), + cluster_info.clone(), + SLEEP_INTERVAL, + exit.clone(), + ) + .await + { + num_refresh_loop_errors += 1; + datapoint_error!( + "auth_tokens_update_loop-refresh_loop_error", + ("url", auth_service_endpoint.uri().to_string(), String), + ("count", num_refresh_loop_errors, i64), + ("error", e.to_string(), String) + ); + } + } + Err(e) => { + num_connect_errors += 1; + datapoint_error!( + "auth_tokens_update_loop-refresh_connect_error", + ("url", auth_service_endpoint.uri().to_string(), String), + ("count", num_connect_errors, i64), + ("error", e.to_string(), String) + ); + } + } + } + } + + /// Responsible for keeping generating and refreshing the access token. + async fn auth_tokens_update_loop_helper( + mut auth_service_client: AuthServiceClient, + url: String, + (access_token, mut refresh_token): (Arc>, Token), + cluster_info: Arc, + sleep_interval: Duration, + exit: Arc, + ) -> crate::proxy::Result<()> { + const REFRESH_WITHIN_SECS: i64 = 300; + let mut num_full_refreshes = 0; + let mut num_refresh_access_token = 0; + + while !exit.load(Ordering::Relaxed) { + let access_token_expiry: i64 = access_token + .lock() + .unwrap() + .expires_at_utc + .as_ref() + .map(|ts| ts.seconds) + .unwrap_or_default(); + let refresh_token_expiry = refresh_token + .expires_at_utc + .as_ref() + .map(|ts| ts.seconds) + .unwrap_or_default(); + + let now = Utc::now().timestamp(); + + let should_refresh_access = access_token_expiry.checked_sub(now).ok_or_else(|| { + ProxyError::InvalidData("Received invalid access_token expiration".to_string()) + })? <= REFRESH_WITHIN_SECS; + let should_generate_new_tokens = + refresh_token_expiry.checked_sub(now).ok_or_else(|| { + ProxyError::InvalidData("Received invalid refresh_token expiration".to_string()) + })? <= REFRESH_WITHIN_SECS; + + match (should_refresh_access, should_generate_new_tokens) { + // Generate new tokens if the refresh_token is close to being expired. + (_, true) => { + let kp = cluster_info.keypair().clone(); + + let (new_access_token, new_refresh_token) = + generate_auth_tokens(&mut auth_service_client, kp.as_ref()).await?; + + *access_token.lock().unwrap() = new_access_token.clone(); + refresh_token = new_refresh_token; + + num_full_refreshes += 1; + datapoint_info!( + "auth_tokens_update_loop-tokens_generated", + ("url", url, String), + ("count", num_full_refreshes, i64), + ); + } + // Invoke the refresh_access_token method if the access_token is close to being expired. + (true, _) => { + let new_access_token = + refresh_access_token(&mut auth_service_client, refresh_token.clone()) + .await?; + *access_token.lock().unwrap() = new_access_token; + + num_refresh_access_token += 1; + datapoint_info!( + "auth_tokens_update_loop-refresh_access_token", + ("url", url, String), + ("count", num_refresh_access_token, i64), + ); + } + // Sleep and do nothing if neither token is close to expired, + (false, false) => sleep(sleep_interval).await, + } + } + + Ok(()) + } + + /// Invokes the refresh_access_token gRPC method. + /// Returns a new access_token. + async fn refresh_access_token( + auth_service_client: &mut AuthServiceClient, + refresh_token: Token, + ) -> crate::proxy::Result { + match auth_service_client + .refresh_access_token(RefreshAccessTokenRequest { + refresh_token: refresh_token.value, + }) + .await + { + Ok(resp) => get_validated_token(resp.into_inner().access_token), + Err(e) => Err(ProxyError::GrpcError(e)), + } + } + + /// Generates an auth challenge then generates and returns validated auth tokens. + async fn generate_auth_tokens( + auth_service_client: &mut AuthServiceClient, + // used to sign challenges + keypair: &Keypair, + ) -> crate::proxy::Result<( + Token, /* access_token */ + Token, /* refresh_token */ + )> { + let challenge_response = auth_service_client + .generate_auth_challenge(GenerateAuthChallengeRequest { + role: Role::Validator as i32, + pubkey: keypair.pubkey().as_ref().to_vec(), + }) + .await?; + + let formatted_challenge = format!( + "{}-{}", + keypair.pubkey(), + challenge_response.into_inner().challenge + ); + let signed_challenge = keypair + .sign_message(formatted_challenge.as_bytes()) + .as_ref() + .to_vec(); + + let auth_tokens = auth_service_client + .generate_auth_tokens(GenerateAuthTokensRequest { + challenge: formatted_challenge, + client_pubkey: keypair.pubkey().as_ref().to_vec(), + signed_challenge, + }) + .await?; + + let inner = auth_tokens.into_inner(); + let access_token = get_validated_token(inner.access_token)?; + let refresh_token = get_validated_token(inner.refresh_token)?; + + Ok((access_token, refresh_token)) + } + + /// An invalid token is one where any of its fields are None or the token itself is None. + /// Performs the necessary validations on the auth tokens before returning, + /// i.e. it is safe to call .unwrap() on the token fields from the call-site. + fn get_validated_token(maybe_token: Option) -> crate::proxy::Result { + let token = maybe_token + .ok_or_else(|| ProxyError::InvalidData("received a null token".to_string()))?; + if token.expires_at_utc.is_none() { + Err(ProxyError::InvalidData( + "expires_at_utc field is null".to_string(), + )) + } else { + Ok(token) + } + } +} diff --git a/core/src/proxy/block_engine_stage.rs b/core/src/proxy/block_engine_stage.rs new file mode 100644 index 0000000000..ffa363aae1 --- /dev/null +++ b/core/src/proxy/block_engine_stage.rs @@ -0,0 +1,345 @@ +//! Maintains a connection to the Block Engine. +//! +//! The Block Engine is responsible for the following: +//! - Acts as a system that sends high profit bundles and transactions to a validator. +//! - Sends transactions and bundles to the validator. + +use { + crate::{ + backoff::BackoffStrategy, + packet_bundle::PacketBundle, + proto_packet_to_packet, + proxy::{ + auth::{token_manager::auth_tokens_update_loop, AuthInterceptor}, + ProxyError, + }, + sigverify::SigverifyTracerPacketStats, + }, + crossbeam_channel::Sender, + jito_protos::proto::{ + auth::Token, + block_engine::{self, block_engine_validator_client::BlockEngineValidatorClient}, + }, + solana_gossip::cluster_info::ClusterInfo, + solana_perf::packet::PacketBatch, + solana_sdk::saturating_add_assign, + std::{ + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, + thread::{self, Builder, JoinHandle}, + time::Duration, + }, + tokio::time::{interval, sleep}, + tonic::{ + codegen::InterceptedService, + transport::{Channel, Endpoint}, + Status, Streaming, + }, + uuid::Uuid, +}; + +#[derive(Default)] +struct BlockEngineStageStats { + num_bundles: u64, + num_bundle_packets: u64, + num_packets: u64, + num_empty_packets: u64, +} + +impl BlockEngineStageStats { + pub(crate) fn report(&self) { + datapoint_info!( + "block_engine_stage-stats", + ("num_bundles", self.num_bundles, i64), + ("num_bundle_packets", self.num_bundle_packets, i64), + ("num_packets", self.num_packets, i64), + ("num_empty_packets", self.num_empty_packets, i64) + ); + } +} + +#[derive(Clone, Debug)] +pub struct BlockEngineConfig { + /// Address to the external auth-service responsible for generating access tokens. + pub auth_service_endpoint: Endpoint, + + /// Primary backend endpoint. + pub backend_endpoint: Endpoint, + + /// If set then it will be assumed the backend verified packets so signature verification will be bypassed in the validator. + pub trust_packets: bool, +} + +pub struct BlockEngineStage { + t_hdls: Vec>, +} + +impl BlockEngineStage { + pub fn new( + block_engine_config: BlockEngineConfig, + // Channel that bundles get piped through. + bundle_tx: Sender>, + // The keypair stored here is used to sign auth challenges. + cluster_info: Arc, + // Channel that non-trusted packets get piped through. + packet_tx: Sender, + // Channel that trusted packets get piped through. + verified_packet_tx: Sender<(Vec, Option)>, + exit: Arc, + ) -> Self { + let BlockEngineConfig { + auth_service_endpoint, + backend_endpoint, + trust_packets, + } = block_engine_config; + + let access_token = Arc::new(Mutex::new(Token::default())); + let thread = Builder::new() + .name("block-engine-stage".into()) + .spawn(move || { + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + rt.spawn(auth_tokens_update_loop( + auth_service_endpoint, + access_token.clone(), + cluster_info.clone(), + exit.clone(), + )); + rt.block_on(Self::start( + access_token, + backend_endpoint, + bundle_tx, + packet_tx, + trust_packets, + verified_packet_tx, + exit, + )); + }) + .unwrap(); + + Self { + t_hdls: vec![thread], + } + } + + pub fn join(self) -> thread::Result<()> { + for t in self.t_hdls { + t.join()?; + } + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + async fn start( + access_token: Arc>, + block_engine_endpoint: Endpoint, + bundle_tx: Sender>, + packet_tx: Sender, + trust_packets: bool, + verified_packet_tx: Sender<(Vec, Option)>, + exit: Arc, + ) { + const WAIT_FOR_FIRST_AUTH: Duration = Duration::from_secs(5); + + let mut num_wait_for_auth: usize = 0; + let mut num_stream_errors: usize = 0; + let mut num_connect_errors: usize = 0; + + while access_token.lock().unwrap().value.is_empty() { + if exit.load(Ordering::Relaxed) { + return; + } + num_wait_for_auth += 1; + datapoint_info!( + "block_engine_stage-wait_for_auth", + ("wait_count", num_wait_for_auth, i64) + ); + sleep(WAIT_FOR_FIRST_AUTH).await; + } + + let mut backoff = BackoffStrategy::new(); + while !exit.load(Ordering::Relaxed) { + match block_engine_endpoint.connect().await { + Ok(channel) => { + match Self::start_consuming_block_engine_bundles_and_packets( + &mut backoff, + &bundle_tx, + BlockEngineValidatorClient::with_interceptor( + channel, + AuthInterceptor::new(access_token.clone()), + ), + &packet_tx, + trust_packets, + &verified_packet_tx, + &exit, + ) + .await + { + Ok(_) => {} + Err(e) => { + num_stream_errors += 1; + datapoint_error!( + "block_engine_stage-stream_error", + ("count", num_stream_errors, i64), + ("error", e.to_string(), String), + ); + } + } + } + Err(e) => { + num_connect_errors += 1; + datapoint_error!( + "block_engine_stage-connect_error", + ("count", num_connect_errors, i64), + ("error", e.to_string(), String), + ); + } + } + + sleep(Duration::from_millis(backoff.next_wait())).await; + } + } + + async fn start_consuming_block_engine_bundles_and_packets( + backoff: &mut BackoffStrategy, + bundle_tx: &Sender>, + mut client: BlockEngineValidatorClient>, + packet_tx: &Sender, + trust_packets: bool, + verified_packet_tx: &Sender<(Vec, Option)>, + exit: &Arc, + ) -> crate::proxy::Result<()> { + let subscribe_packets_stream = client + .subscribe_packets(block_engine::SubscribePacketsRequest {}) + .await? + .into_inner(); + let subscribe_bundles_stream = client + .subscribe_bundles(block_engine::SubscribeBundlesRequest {}) + .await? + .into_inner(); + + backoff.reset(); + + Self::consume_bundle_and_packet_stream( + (subscribe_bundles_stream, subscribe_packets_stream), + bundle_tx, + packet_tx, + trust_packets, + verified_packet_tx, + exit, + ) + .await + } + + async fn consume_bundle_and_packet_stream( + (mut bundle_stream, mut packet_stream): ( + Streaming, + Streaming, + ), + bundle_tx: &Sender>, + packet_tx: &Sender, + trust_packets: bool, + verified_packet_tx: &Sender<(Vec, Option)>, + exit: &Arc, + ) -> crate::proxy::Result<()> { + const METRICS_TICK: Duration = Duration::from_secs(1); + + let mut block_engine_stats = BlockEngineStageStats::default(); + let mut metrics_tick = interval(METRICS_TICK); + + info!("connected to packet and bundle stream"); + + while !exit.load(Ordering::Relaxed) { + tokio::select! { + maybe_msg = packet_stream.message() => { + let resp = maybe_msg?.ok_or(ProxyError::GrpcStreamDisconnected)?; + Self::handle_block_engine_packets(resp, packet_tx, verified_packet_tx, trust_packets, &mut block_engine_stats)?; + } + maybe_bundles = bundle_stream.message() => { + Self::handle_block_engine_maybe_bundles(maybe_bundles, bundle_tx, &mut block_engine_stats)?; + } + _ = metrics_tick.tick() => { + block_engine_stats.report(); + block_engine_stats = BlockEngineStageStats::default(); + } + } + } + + Ok(()) + } + + fn handle_block_engine_maybe_bundles( + maybe_bundles_response: Result, Status>, + bundle_sender: &Sender>, + block_engine_stats: &mut BlockEngineStageStats, + ) -> crate::proxy::Result<()> { + let bundles_response = maybe_bundles_response?.ok_or(ProxyError::GrpcStreamDisconnected)?; + let bundles: Vec = bundles_response + .bundles + .into_iter() + .filter_map(|bundle| { + Some(PacketBundle { + batch: PacketBatch::new( + bundle + .bundle? + .packets + .into_iter() + .map(proto_packet_to_packet) + .collect(), + ), + uuid: Uuid::from_str(&bundle.uuid).ok()?, + }) + }) + .collect(); + + saturating_add_assign!(block_engine_stats.num_bundles, bundles.len() as u64); + saturating_add_assign!( + block_engine_stats.num_bundle_packets, + bundles.iter().map(|bundle| bundle.batch.len() as u64).sum() + ); + + // NOTE: bundles are sanitized in bundle_sanitizer module + bundle_sender + .send(bundles) + .map_err(|_| ProxyError::PacketForwardError) + } + + fn handle_block_engine_packets( + resp: block_engine::SubscribePacketsResponse, + packet_tx: &Sender, + verified_packet_tx: &Sender<(Vec, Option)>, + trust_packets: bool, + block_engine_stats: &mut BlockEngineStageStats, + ) -> crate::proxy::Result<()> { + if let Some(batch) = resp.batch { + let packet_batch = PacketBatch::new( + batch + .packets + .into_iter() + .map(proto_packet_to_packet) + .collect(), + ); + + saturating_add_assign!(block_engine_stats.num_packets, packet_batch.len() as u64); + + if trust_packets { + verified_packet_tx + .send((vec![packet_batch], None)) + .map_err(|_| ProxyError::PacketForwardError)?; + } else { + packet_tx + .send(packet_batch) + .map_err(|_| ProxyError::PacketForwardError)?; + } + } else { + saturating_add_assign!(block_engine_stats.num_empty_packets, 1); + } + + Ok(()) + } +} diff --git a/core/src/proxy/fetch_stage_manager.rs b/core/src/proxy/fetch_stage_manager.rs new file mode 100644 index 0000000000..8f5b95bc2c --- /dev/null +++ b/core/src/proxy/fetch_stage_manager.rs @@ -0,0 +1,161 @@ +use { + crate::proxy::{HeartbeatEvent, ProxyError}, + crossbeam_channel::{select, tick, Receiver, Sender}, + solana_gossip::cluster_info::ClusterInfo, + solana_perf::packet::PacketBatch, + std::{ + net::SocketAddr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread::{self, Builder, JoinHandle}, + time::{Duration, Instant}, + }, +}; + +const HEARTBEAT_TIMEOUT: Duration = Duration::from_millis(1500); // Empirically determined from load testing +const DISCONNECT_DELAY: Duration = Duration::from_secs(60); +const METRICS_CADENCE: Duration = Duration::from_secs(1); + +/// Manages switching between the validator's tpu ports and that of the proxy's. +/// Switch-overs are triggered by late and missed heartbeats. +pub struct FetchStageManager { + t_hdl: JoinHandle<()>, +} + +impl FetchStageManager { + pub fn new( + // ClusterInfo is used to switch between advertising the proxy's TPU ports and that of this validator's. + cluster_info: Arc, + // Channel that heartbeats are received from. Entirely responsible for triggering switch-overs. + heartbeat_rx: Receiver, + // Channel that packets from FetchStage are intercepted from. + packet_intercept_rx: Receiver, + // Intercepted packets get piped through here. + packet_tx: Sender, + exit: Arc, + ) -> Self { + let t_hdl = Self::start( + cluster_info, + heartbeat_rx, + packet_intercept_rx, + packet_tx, + exit, + ); + + Self { t_hdl } + } + + /// Disconnect fetch behaviour + /// Starts connected + /// When connected and a packet is received, forward it + /// When disconnected, packet is dropped + /// When receiving heartbeat while connected and not pending disconnect + /// Sets pending_disconnect to true and records time + /// When receiving heartbeat while connected, and pending for > DISCONNECT_DELAY_SEC + /// Sets fetch_connected to false, pending_disconnect to false + /// Advertises TPU ports sent in heartbeat + /// When tick is received without heartbeat_received + /// Sets fetch_connected to true, pending_disconnect to false + /// Advertises saved contact info + fn start( + cluster_info: Arc, + heartbeat_rx: Receiver, + packet_intercept_rx: Receiver, + packet_tx: Sender, + exit: Arc, + ) -> JoinHandle<()> { + Builder::new().name("fetch-stage-manager".into()).spawn(move || { + let my_fallback_contact_info = cluster_info.my_contact_info(); + + let mut fetch_connected = true; + let mut heartbeat_received = false; + let mut pending_disconnect = false; + + let mut pending_disconnect_ts = Instant::now(); + + let heartbeat_tick = tick(HEARTBEAT_TIMEOUT); + let metrics_tick = tick(METRICS_CADENCE); + let mut packets_forwarded = 0; + let mut heartbeats_received = 0; + loop { + select! { + recv(packet_intercept_rx) -> pkt => { + match pkt { + Ok(pkt) => { + if fetch_connected { + if packet_tx.send(pkt).is_err() { + error!("{:?}", ProxyError::PacketForwardError); + return; + } + packets_forwarded += 1; + } + } + Err(_) => { + warn!("packet intercept receiver disconnected, shutting down"); + return; + } + } + } + recv(heartbeat_tick) -> _ => { + if exit.load(Ordering::Relaxed) { + break; + } + if !heartbeat_received && (!fetch_connected || pending_disconnect) { + warn!("heartbeat late, reconnecting fetch stage"); + fetch_connected = true; + pending_disconnect = false; + Self::set_tpu_addresses(&cluster_info, my_fallback_contact_info.tpu, my_fallback_contact_info.tpu_forwards); + heartbeats_received = 0; + } + heartbeat_received = false; + } + recv(heartbeat_rx) -> tpu_info => { + if let Ok((tpu_addr, tpu_forward_addr)) = tpu_info { + heartbeats_received += 1; + heartbeat_received = true; + if fetch_connected && !pending_disconnect { + info!("received heartbeat while fetch stage connected, pending disconnect after delay"); + pending_disconnect_ts = Instant::now(); + pending_disconnect = true; + } + if fetch_connected && pending_disconnect && pending_disconnect_ts.elapsed() > DISCONNECT_DELAY { + info!("disconnecting fetch stage"); + fetch_connected = false; + pending_disconnect = false; + Self::set_tpu_addresses(&cluster_info, tpu_addr, tpu_forward_addr); + } + } else { + // see comment on heartbeat_sender clone in new() + unreachable!(); + } + } + recv(metrics_tick) -> _ => { + datapoint_info!( + "relayer-heartbeat", + ("fetch_stage_packets_forwarded", packets_forwarded, i64), + ("heartbeats_received", heartbeats_received, i64), + ); + + } + } + } + }).unwrap() + } + + fn set_tpu_addresses( + cluster_info: &Arc, + tpu_address: SocketAddr, + tpu_forward_address: SocketAddr, + ) { + let mut new_contact_info = cluster_info.my_contact_info(); + new_contact_info.tpu = tpu_address; + new_contact_info.tpu_forwards = tpu_forward_address; + cluster_info.set_my_contact_info(new_contact_info); + } + + pub fn join(self) -> thread::Result<()> { + self.t_hdl.join() + } +} diff --git a/core/src/proxy/mod.rs b/core/src/proxy/mod.rs new file mode 100644 index 0000000000..a5fcf9cdb5 --- /dev/null +++ b/core/src/proxy/mod.rs @@ -0,0 +1,55 @@ +//! This module contains logic for connecting to an external Relayer and Block Engine. +//! The Relayer acts as an external TPU and TPU Forward socket while the Block Engine +//! is tasked with streaming high value bundles to the validator. The validator can run +//! in one of 3 modes: +//! 1. Connected to Relayer and Block Engine. +//! - This is the ideal mode as it increases the probability of building the most profitable blocks. +//! 2. Connected only to Relayer. +//! - A validator may choose to run in this mode if the main concern is to offload ingress traffic deduplication and sig-verification. +//! 3. Connected only to Block Engine. +//! - Running in this mode means pending transactions are not exposed to external actors. This mode is ideal if the validator wishes +//! to accept bundles while maintaining some level of privacy for in-flight transactions. + +mod auth; +pub mod block_engine_stage; +pub mod fetch_stage_manager; +pub mod relayer_stage; + +use { + std::{ + net::{AddrParseError, SocketAddr}, + result, + }, + thiserror::Error, + tonic::Status, +}; + +type Result = result::Result; +type HeartbeatEvent = (SocketAddr, SocketAddr); + +#[derive(Error, Debug)] +pub enum ProxyError { + #[error("grpc error: {0}")] + GrpcError(#[from] Status), + + #[error("stream disconnected")] + GrpcStreamDisconnected, + + #[error("heartbeat error")] + HeartbeatChannelError, + + #[error("heartbeat expired")] + HeartbeatExpired, + + #[error("error forwarding packet to banking stage")] + PacketForwardError, + + #[error("missing tpu config: {0:?}")] + MissingTpuSocket(String), + + #[error("invalid socket address: {0:?}")] + InvalidSocketAddress(#[from] AddrParseError), + + #[error("invalid gRPC data: {0:?}")] + InvalidData(String), +} diff --git a/core/src/proxy/relayer_stage.rs b/core/src/proxy/relayer_stage.rs new file mode 100644 index 0000000000..7f4961d5eb --- /dev/null +++ b/core/src/proxy/relayer_stage.rs @@ -0,0 +1,362 @@ +//! Maintains a connection to the Relayer. +//! +//! The external Relayer is responsible for the following: +//! - Acts as a TPU proxy. +//! - Sends transactions to the validator. +//! - Does not bundles to avoid DOS vector. +//! - When validator connects, it changes its TPU and TPU forward address to the relayer. +//! - Expected to send heartbeat to validator as watchdog. If watchdog times out, the validator +//! disconnects and reverts the TPU and TPU forward settings. + +use { + crate::{ + backoff::BackoffStrategy, + proto_packet_to_packet, + proxy::{ + auth::{token_manager::auth_tokens_update_loop, AuthInterceptor}, + HeartbeatEvent, ProxyError, + }, + sigverify::SigverifyTracerPacketStats, + }, + crossbeam_channel::Sender, + jito_protos::proto::{ + auth::Token, + relayer::{self, relayer_client::RelayerClient}, + }, + solana_gossip::cluster_info::ClusterInfo, + solana_perf::packet::PacketBatch, + solana_sdk::saturating_add_assign, + std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, + thread::{self, Builder, JoinHandle}, + time::{Duration, Instant}, + }, + tokio::time::{interval, sleep}, + tonic::{ + codegen::InterceptedService, + transport::{Channel, Endpoint}, + Streaming, + }, +}; + +#[derive(Default)] +struct RelayerStageStats { + num_empty_messages: u64, + num_packets: u64, + num_heartbeats: u64, +} + +impl RelayerStageStats { + pub(crate) fn report(&self) { + datapoint_info!( + "relayer_stage-stats", + ("num_empty_messages", self.num_empty_messages, i64), + ("num_packets", self.num_packets, i64), + ("num_heartbeats", self.num_heartbeats, i64), + ); + } +} + +#[derive(Clone, Debug)] +pub struct RelayerConfig { + /// Address to the external auth-service responsible for generating access tokens. + pub auth_service_endpoint: Endpoint, + + /// Primary backend endpoint. + pub backend_endpoint: Endpoint, + + /// Interval at which heartbeats are expected. + pub expected_heartbeat_interval: Duration, + + /// The max tolerable age of the last heartbeat. + pub oldest_allowed_heartbeat: Duration, + + /// If set then it will be assumed the backend verified packets so signature verification will be bypassed in the validator. + pub trust_packets: bool, +} + +pub struct RelayerStage { + t_hdls: Vec>, +} + +impl RelayerStage { + pub fn new( + relayer_config: RelayerConfig, + // The keypair stored here is used to sign auth challenges. + cluster_info: Arc, + // Channel that server-sent heartbeats are piped through. + heartbeat_tx: Sender, + // Channel that non-trusted streamed packets are piped through. + packet_tx: Sender, + // Channel that trusted streamed packets are piped through. + verified_packet_tx: Sender<(Vec, Option)>, + exit: Arc, + ) -> Self { + let RelayerConfig { + auth_service_endpoint, + backend_endpoint, + expected_heartbeat_interval, + oldest_allowed_heartbeat, + trust_packets, + } = relayer_config; + + let access_token = Arc::new(Mutex::new(Token::default())); + let thread = Builder::new() + .name("relayer-stage".into()) + .spawn(move || { + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + rt.spawn(auth_tokens_update_loop( + auth_service_endpoint, + access_token.clone(), + cluster_info.clone(), + exit.clone(), + )); + rt.block_on(Self::start( + access_token, + heartbeat_tx, + expected_heartbeat_interval, + oldest_allowed_heartbeat, + packet_tx, + backend_endpoint, + verified_packet_tx, + trust_packets, + exit, + )); + }) + .unwrap(); + + Self { + t_hdls: vec![thread], + } + } + + pub fn join(self) -> thread::Result<()> { + for t in self.t_hdls { + t.join()?; + } + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + async fn start( + access_token: Arc>, + heartbeat_tx: Sender, + expected_heartbeat_interval: Duration, + oldest_allowed_heartbeat: Duration, + packet_tx: Sender, + relayer_endpoint: Endpoint, + verified_packet_tx: Sender<(Vec, Option)>, + trust_packets: bool, + exit: Arc, + ) { + const WAIT_FOR_FIRST_AUTH: Duration = Duration::from_secs(5); + + let mut wait_count: usize = 0; + let mut stream_error_count: usize = 0; + let mut connect_error_count: usize = 0; + while access_token.lock().unwrap().value.is_empty() { + if exit.load(Ordering::Relaxed) { + return; + } + wait_count += 1; + datapoint_info!( + "relayer_stage-wait_for_auth", + ("wait_count", wait_count, i64) + ); + sleep(WAIT_FOR_FIRST_AUTH).await; + } + + let mut backoff = BackoffStrategy::new(); + while !exit.load(Ordering::Relaxed) { + match relayer_endpoint.connect().await { + Ok(channel) => { + match Self::start_consuming_relayer_packets( + &mut backoff, + RelayerClient::with_interceptor( + channel, + AuthInterceptor::new(access_token.clone()), + ), + &heartbeat_tx, + expected_heartbeat_interval, + oldest_allowed_heartbeat, + &packet_tx, + &verified_packet_tx, + trust_packets, + &exit, + ) + .await + { + Ok(_) => {} + Err(e) => { + stream_error_count += 1; + datapoint_error!( + "relayer_stage-stream_error", + ("count", stream_error_count, i64), + ("error", e.to_string(), String), + ); + } + } + } + Err(e) => { + connect_error_count += 1; + datapoint_error!( + "relayer_stage-connect_error", + ("count", connect_error_count, i64), + ("error", e.to_string(), String), + ); + } + } + sleep(Duration::from_millis(backoff.next_wait())).await; + } + } + + async fn start_consuming_relayer_packets( + backoff: &mut BackoffStrategy, + mut client: RelayerClient>, + heartbeat_tx: &Sender, + expected_heartbeat_interval: Duration, + oldest_allowed_heartbeat: Duration, + packet_tx: &Sender, + verified_packet_tx: &Sender<(Vec, Option)>, + trust_packets: bool, + exit: &Arc, + ) -> crate::proxy::Result<()> { + let heartbeat_event: HeartbeatEvent = { + let tpu_config = client + .get_tpu_configs(relayer::GetTpuConfigsRequest {}) + .await? + .into_inner(); + let tpu_addr = tpu_config + .tpu + .ok_or_else(|| ProxyError::MissingTpuSocket("tpu".into()))?; + let tpu_forward_addr = tpu_config + .tpu_forward + .ok_or_else(|| ProxyError::MissingTpuSocket("tpu_fwd".into()))?; + + let tpu_ip = IpAddr::from(tpu_addr.ip.parse::()?); + let tpu_forward_ip = IpAddr::from(tpu_forward_addr.ip.parse::()?); + + let tpu_socket = SocketAddr::new(tpu_ip, tpu_addr.port as u16); + let tpu_forward_socket = SocketAddr::new(tpu_forward_ip, tpu_forward_addr.port as u16); + (tpu_socket, tpu_forward_socket) + }; + + let packet_stream = client + .subscribe_packets(relayer::SubscribePacketsRequest {}) + .await? + .into_inner(); + + // assume it's all good here + backoff.reset(); + + Self::consume_packet_stream( + heartbeat_event, + heartbeat_tx, + expected_heartbeat_interval, + oldest_allowed_heartbeat, + packet_stream, + packet_tx, + trust_packets, + verified_packet_tx, + exit, + ) + .await + } + + async fn consume_packet_stream( + heartbeat_event: HeartbeatEvent, + heartbeat_tx: &Sender, + expected_heartbeat_interval: Duration, + oldest_allowed_heartbeat: Duration, + mut packet_stream: Streaming, + packet_tx: &Sender, + trust_packets: bool, + verified_packet_tx: &Sender<(Vec, Option)>, + exit: &Arc, + ) -> crate::proxy::Result<()> { + const METRICS_TICK: Duration = Duration::from_secs(1); + + let mut relayer_stats = RelayerStageStats::default(); + let mut metrics_tick = interval(METRICS_TICK); + + let mut heartbeat_check_interval = interval(expected_heartbeat_interval); + let mut last_heartbeat_ts = Instant::now(); + + info!("connected to packet stream"); + + while !exit.load(Ordering::Relaxed) { + tokio::select! { + maybe_msg = packet_stream.message() => { + let resp = maybe_msg?.ok_or(ProxyError::GrpcStreamDisconnected)?; + Self::handle_relayer_packets(resp, heartbeat_event, heartbeat_tx, &mut last_heartbeat_ts, packet_tx, trust_packets, verified_packet_tx, &mut relayer_stats)?; + } + _ = heartbeat_check_interval.tick() => { + if last_heartbeat_ts.elapsed() > oldest_allowed_heartbeat { + return Err(ProxyError::HeartbeatExpired); + } + } + _ = metrics_tick.tick() => { + relayer_stats.report(); + relayer_stats = RelayerStageStats::default(); + } + } + } + + Ok(()) + } + + fn handle_relayer_packets( + subscribe_packets_resp: relayer::SubscribePacketsResponse, + heartbeat_event: HeartbeatEvent, + heartbeat_tx: &Sender, + last_heartbeat_ts: &mut Instant, + packet_tx: &Sender, + trust_packets: bool, + verified_packet_tx: &Sender<(Vec, Option)>, + relayer_stats: &mut RelayerStageStats, + ) -> crate::proxy::Result<()> { + match subscribe_packets_resp.msg { + None => { + saturating_add_assign!(relayer_stats.num_empty_messages, 1); + } + Some(relayer::subscribe_packets_response::Msg::Batch(proto_batch)) => { + let packet_batch = PacketBatch::new( + proto_batch + .packets + .into_iter() + .map(proto_packet_to_packet) + .collect(), + ); + + saturating_add_assign!(relayer_stats.num_packets, packet_batch.len() as u64); + + if trust_packets { + verified_packet_tx + .send((vec![packet_batch], None)) + .map_err(|_| ProxyError::PacketForwardError)?; + } else { + packet_tx + .send(packet_batch) + .map_err(|_| ProxyError::PacketForwardError)?; + } + } + Some(relayer::subscribe_packets_response::Msg::Heartbeat(_)) => { + saturating_add_assign!(relayer_stats.num_heartbeats, 1); + + *last_heartbeat_ts = Instant::now(); + heartbeat_tx + .send(heartbeat_event) + .map_err(|_| ProxyError::HeartbeatChannelError)?; + } + } + Ok(()) + } +} diff --git a/core/src/qos_service.rs b/core/src/qos_service.rs index 9b54e2a302..7affbc271c 100644 --- a/core/src/qos_service.rs +++ b/core/src/qos_service.rs @@ -219,7 +219,7 @@ impl QosService { ); } - fn remove_transaction_costs<'a>( + pub fn remove_transaction_costs<'a>( transaction_costs: impl Iterator, transaction_qos_results: impl Iterator>, bank: &Arc, diff --git a/core/src/retransmit_stage.rs b/core/src/retransmit_stage.rs index b0ebaad70b..4b8fadd4c4 100644 --- a/core/src/retransmit_stage.rs +++ b/core/src/retransmit_stage.rs @@ -31,7 +31,7 @@ use { std::{ collections::HashMap, iter::repeat, - net::UdpSocket, + net::{SocketAddr, UdpSocket}, ops::AddAssign, sync::{ atomic::{AtomicU64, AtomicUsize, Ordering}, @@ -178,6 +178,7 @@ fn retransmit( packet_hasher: &mut PacketHasher, max_slots: &MaxSlots, rpc_subscriptions: Option<&RpcSubscriptions>, + shred_receiver_addr: Option, ) -> Result<(), RecvTimeoutError> { const RECV_TIMEOUT: Duration = Duration::from_secs(1); let mut shreds = shreds_receiver.recv_timeout(RECV_TIMEOUT)?; @@ -256,6 +257,7 @@ fn retransmit( socket_addr_space, &sockets[index % sockets.len()], stats, + shred_receiver_addr, ); (key.slot(), root_distance, num_nodes) }) @@ -275,6 +277,7 @@ fn retransmit( socket_addr_space, &sockets[index % sockets.len()], stats, + shred_receiver_addr, ); (key.slot(), root_distance, num_nodes) }) @@ -298,10 +301,16 @@ fn retransmit_shred( socket_addr_space: &SocketAddrSpace, socket: &UdpSocket, stats: &RetransmitStats, + shred_receiver_addr: Option, ) -> (/*root_distance:*/ usize, /*num_nodes:*/ usize) { let mut compute_turbine_peers = Measure::start("turbine_start"); - let (root_distance, addrs) = + let (root_distance, mut addrs) = cluster_nodes.get_retransmit_addrs(slot_leader, key, root_bank, DATA_PLANE_FANOUT); + + if let Some(addr) = shred_receiver_addr { + addrs.push(addr); + } + let addrs: Vec<_> = addrs .into_iter() .filter(|addr| ContactInfo::is_valid_address(addr, socket_addr_space)) @@ -351,6 +360,7 @@ pub fn retransmitter( shreds_receiver: Receiver>>, max_slots: Arc, rpc_subscriptions: Option>, + shred_receiver_addr: Option, ) -> JoinHandle<()> { let cluster_nodes_cache = ClusterNodesCache::::new( CLUSTER_NODES_CACHE_NUM_EPOCH_CAP, @@ -383,6 +393,7 @@ pub fn retransmitter( &mut packet_hasher, &max_slots, rpc_subscriptions.as_deref(), + shred_receiver_addr, ) { Ok(()) => (), Err(RecvTimeoutError::Timeout) => (), @@ -405,6 +416,7 @@ impl RetransmitStage { retransmit_receiver: Receiver>>, max_slots: Arc, rpc_subscriptions: Option>, + shred_receiver_addr: Option, ) -> Self { let retransmit_thread_handle = retransmitter( retransmit_sockets, @@ -414,6 +426,7 @@ impl RetransmitStage { retransmit_receiver, max_slots, rpc_subscriptions, + shred_receiver_addr, ); Self { diff --git a/core/src/tip_manager.rs b/core/src/tip_manager.rs new file mode 100644 index 0000000000..9477abd0de --- /dev/null +++ b/core/src/tip_manager.rs @@ -0,0 +1,452 @@ +use { + anchor_lang::{ + solana_program::hash::Hash, AccountDeserialize, InstructionData, ToAccountMetas, + }, + log::warn, + solana_runtime::bank::Bank, + solana_sdk::{ + account::ReadableAccount, + bundle::error::TipPaymentError, + compute_budget::ComputeBudgetInstruction, + instruction::Instruction, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, + stake_history::Epoch, + system_program, + transaction::{SanitizedTransaction, Transaction}, + }, + std::{ + collections::HashSet, + sync::{Arc, Mutex, MutexGuard}, + }, + tip_distribution::sdk::{ + derive_config_account_address, derive_tip_distribution_account_address, + instruction::{ + init_tip_distribution_account_ix, initialize_ix, InitTipDistributionAccountAccounts, + InitTipDistributionAccountArgs, InitializeAccounts, InitializeArgs, + }, + }, + tip_payment::{ + Config, InitBumps, TipPaymentAccount, CONFIG_ACCOUNT_SEED, TIP_ACCOUNT_SEED_0, + TIP_ACCOUNT_SEED_1, TIP_ACCOUNT_SEED_2, TIP_ACCOUNT_SEED_3, TIP_ACCOUNT_SEED_4, + TIP_ACCOUNT_SEED_5, TIP_ACCOUNT_SEED_6, TIP_ACCOUNT_SEED_7, + }, +}; + +pub type Result = std::result::Result; + +#[derive(Debug, Clone)] +struct TipPaymentProgramInfo { + program_id: Pubkey, + + config_pda_bump: (Pubkey, u8), + tip_pda_0: (Pubkey, u8), + tip_pda_1: (Pubkey, u8), + tip_pda_2: (Pubkey, u8), + tip_pda_3: (Pubkey, u8), + tip_pda_4: (Pubkey, u8), + tip_pda_5: (Pubkey, u8), + tip_pda_6: (Pubkey, u8), + tip_pda_7: (Pubkey, u8), +} + +/// Contains metadata regarding the tip-distribution account. +/// The PDAs contained in this struct are presumed to be owned by the program. +#[derive(Debug, Clone)] +struct TipDistributionProgramInfo { + /// The tip-distribution program_id. + program_id: Pubkey, + + /// Singleton [Config] PDA and bump tuple. + config_pda_and_bump: (Pubkey, u8), +} + +/// This config is used on each invocation to the `init_tip_distribution_account` instruction. +#[derive(Debug, Clone)] +pub struct TipDistributionAccountConfig { + /// The keypair paying and signing each init tx. + pub payer: Arc, + + /// The account with authority to upload merkle-roots to this validator's [TipDistributionAccount]. + pub merkle_root_upload_authority: Pubkey, + + /// This validator's vote account. + pub vote_account: Pubkey, + + /// This validator's commission rate BPS for tips in the [TipDistributionAccount]. + pub commission_bps: u16, +} + +impl Default for TipDistributionAccountConfig { + fn default() -> Self { + Self { + payer: Arc::new(Keypair::new()), + merkle_root_upload_authority: Pubkey::new_unique(), + vote_account: Pubkey::new_unique(), + commission_bps: 0, + } + } +} + +#[derive(Debug, Clone)] +pub struct TipManager { + tip_payment_program_info: TipPaymentProgramInfo, + tip_distribution_program_info: TipDistributionProgramInfo, + tip_distribution_account_config: TipDistributionAccountConfig, + lock: Arc>, +} + +#[derive(Clone)] +pub struct TipManagerConfig { + pub tip_payment_program_id: Pubkey, + pub tip_distribution_program_id: Pubkey, + pub tip_distribution_account_config: TipDistributionAccountConfig, +} + +impl Default for TipManagerConfig { + fn default() -> Self { + TipManagerConfig { + tip_payment_program_id: Pubkey::new_unique(), + tip_distribution_program_id: Pubkey::new_unique(), + tip_distribution_account_config: TipDistributionAccountConfig::default(), + } + } +} + +impl TipManager { + pub fn new(config: TipManagerConfig) -> TipManager { + let TipManagerConfig { + tip_payment_program_id, + tip_distribution_program_id, + tip_distribution_account_config, + } = config; + + let config_pda_bump = + Pubkey::find_program_address(&[CONFIG_ACCOUNT_SEED], &tip_payment_program_id); + + let tip_pda_0 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_0], &tip_payment_program_id); + let tip_pda_1 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_1], &tip_payment_program_id); + let tip_pda_2 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_2], &tip_payment_program_id); + let tip_pda_3 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_3], &tip_payment_program_id); + let tip_pda_4 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_4], &tip_payment_program_id); + let tip_pda_5 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_5], &tip_payment_program_id); + let tip_pda_6 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_6], &tip_payment_program_id); + let tip_pda_7 = + Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_7], &tip_payment_program_id); + + let config_pda_and_bump = derive_config_account_address(&tip_distribution_program_id); + + TipManager { + tip_payment_program_info: TipPaymentProgramInfo { + program_id: tip_payment_program_id, + config_pda_bump, + tip_pda_0, + tip_pda_1, + tip_pda_2, + tip_pda_3, + tip_pda_4, + tip_pda_5, + tip_pda_6, + tip_pda_7, + }, + tip_distribution_program_info: TipDistributionProgramInfo { + program_id: tip_distribution_program_id, + config_pda_and_bump, + }, + tip_distribution_account_config, + lock: Arc::new(Mutex::new(())), + } + } + + pub fn tip_payment_program_id(&self) -> Pubkey { + self.tip_payment_program_info.program_id + } + + /// Returns the [Config] account owned by the tip-payment program. + pub fn tip_payment_config_pubkey(&self) -> Pubkey { + self.tip_payment_program_info.config_pda_bump.0 + } + + /// Returns the [Config] account owned by the tip-distribution program. + pub fn tip_distribution_config_pubkey(&self) -> Pubkey { + self.tip_distribution_program_info.config_pda_and_bump.0 + } + + /// Given a bank, returns the current `tip_receiver` configured with the tip-payment program. + pub fn get_configured_tip_receiver(&self, bank: &Bank) -> Result { + Ok(self.get_tip_payment_config_account(bank)?.tip_receiver) + } + + pub fn get_tip_accounts(&self) -> HashSet { + HashSet::from([ + self.tip_payment_program_info.tip_pda_0.0, + self.tip_payment_program_info.tip_pda_1.0, + self.tip_payment_program_info.tip_pda_2.0, + self.tip_payment_program_info.tip_pda_3.0, + self.tip_payment_program_info.tip_pda_4.0, + self.tip_payment_program_info.tip_pda_5.0, + self.tip_payment_program_info.tip_pda_6.0, + self.tip_payment_program_info.tip_pda_7.0, + ]) + } + + pub fn get_tip_payment_config_account(&self, bank: &Bank) -> Result { + let config_data = bank + .get_account(&self.tip_payment_program_info.config_pda_bump.0) + .ok_or(TipPaymentError::AccountMissing( + self.tip_payment_program_info.config_pda_bump.0, + ))?; + + Ok(Config::try_deserialize(&mut config_data.data())?) + } + + /// Only called once during contract creation. + pub fn initialize_tip_payment_program_tx( + &self, + recent_blockhash: Hash, + keypair: &Keypair, + ) -> SanitizedTransaction { + let init_ix = Instruction { + program_id: self.tip_payment_program_info.program_id, + data: tip_payment::instruction::Initialize { + _bumps: InitBumps { + config: self.tip_payment_program_info.config_pda_bump.1, + tip_payment_account_0: self.tip_payment_program_info.tip_pda_0.1, + tip_payment_account_1: self.tip_payment_program_info.tip_pda_1.1, + tip_payment_account_2: self.tip_payment_program_info.tip_pda_2.1, + tip_payment_account_3: self.tip_payment_program_info.tip_pda_3.1, + tip_payment_account_4: self.tip_payment_program_info.tip_pda_4.1, + tip_payment_account_5: self.tip_payment_program_info.tip_pda_5.1, + tip_payment_account_6: self.tip_payment_program_info.tip_pda_6.1, + tip_payment_account_7: self.tip_payment_program_info.tip_pda_7.1, + }, + } + .data(), + accounts: tip_payment::accounts::Initialize { + config: self.tip_payment_program_info.config_pda_bump.0, + tip_payment_account_0: self.tip_payment_program_info.tip_pda_0.0, + tip_payment_account_1: self.tip_payment_program_info.tip_pda_1.0, + tip_payment_account_2: self.tip_payment_program_info.tip_pda_2.0, + tip_payment_account_3: self.tip_payment_program_info.tip_pda_3.0, + tip_payment_account_4: self.tip_payment_program_info.tip_pda_4.0, + tip_payment_account_5: self.tip_payment_program_info.tip_pda_5.0, + tip_payment_account_6: self.tip_payment_program_info.tip_pda_6.0, + tip_payment_account_7: self.tip_payment_program_info.tip_pda_7.0, + system_program: system_program::id(), + payer: keypair.pubkey(), + } + .to_account_metas(None), + }; + SanitizedTransaction::try_from_legacy_transaction(Transaction::new_signed_with_payer( + &[init_ix], + Some(&keypair.pubkey()), + &[keypair], + recent_blockhash, + )) + .unwrap() + } + + pub fn lock(&self) -> MutexGuard<()> { + self.lock.lock().unwrap() + } + + /// Returns this validator's [TipDistributionAccount] PDA derived from the provided epoch. + pub fn get_my_tip_distribution_pda(&self, epoch: Epoch) -> Pubkey { + derive_tip_distribution_account_address( + &self.tip_distribution_program_info.program_id, + &self.tip_distribution_account_config.vote_account, + epoch, + ) + .0 + } + + /// Returns whether or not the tip-payment program should be initialized. + pub fn should_initialize_tip_payment_program(&self, bank: &Bank) -> bool { + match bank.get_account(&self.tip_payment_config_pubkey()) { + None => true, + Some(account) => account.owner() != &self.tip_payment_program_info.program_id, + } + } + + /// Returns whether or not the tip-distribution program's [Config] PDA should be initialized. + pub fn should_initialize_tip_distribution_config(&self, bank: &Bank) -> bool { + match bank.get_account(&self.tip_distribution_config_pubkey()) { + None => true, + Some(account) => account.owner() != &self.tip_distribution_program_info.program_id, + } + } + + /// Returns whether or not the current [TipDistributionAccount] PDA should be initialized for this epoch. + pub fn should_init_tip_distribution_account(&self, bank: &Bank) -> bool { + let pda = derive_tip_distribution_account_address( + &self.tip_distribution_program_info.program_id, + &self.tip_distribution_account_config.vote_account, + bank.epoch(), + ) + .0; + match bank.get_account(&pda) { + None => true, + // Since anyone can derive the PDA and send it lamports we must also check the owner is the program. + Some(account) => account.owner() != &self.tip_distribution_program_info.program_id, + } + } + + /// Creates an [Initialize] transaction object. + pub fn initialize_tip_distribution_config_tx( + &self, + recent_blockhash: Hash, + my_keypair: &Keypair, + ) -> SanitizedTransaction { + let ix = initialize_ix( + self.tip_distribution_program_info.program_id, + InitializeArgs { + authority: my_keypair.pubkey(), + expired_funds_account: my_keypair.pubkey(), + num_epochs_valid: 3, + max_validator_commission_bps: 1000, + bump: self.tip_distribution_program_info.config_pda_and_bump.1, + }, + InitializeAccounts { + config: self.tip_distribution_program_info.config_pda_and_bump.0, + system_program: system_program::id(), + initializer: my_keypair.pubkey(), + }, + ); + + SanitizedTransaction::try_from_legacy_transaction(Transaction::new_signed_with_payer( + &[ix], + Some(&my_keypair.pubkey()), + &[my_keypair], + recent_blockhash, + )) + .unwrap() + } + + /// Creates an [InitTipDistributionAccount] transaction object using the provided Epoch. + pub fn init_tip_distribution_account_tx( + &self, + recent_blockhash: Hash, + epoch: Epoch, + ) -> SanitizedTransaction { + let (tip_distribution_account, bump) = derive_tip_distribution_account_address( + &self.tip_distribution_program_info.program_id, + &self.tip_distribution_account_config.vote_account, + epoch, + ); + + let ix = init_tip_distribution_account_ix( + self.tip_distribution_program_info.program_id, + InitTipDistributionAccountArgs { + validator_vote_account: self.tip_distribution_account_config.vote_account, + merkle_root_upload_authority: self + .tip_distribution_account_config + .merkle_root_upload_authority, + validator_commission_bps: self.tip_distribution_account_config.commission_bps, + bump, + }, + InitTipDistributionAccountAccounts { + config: self.tip_distribution_program_info.config_pda_and_bump.0, + tip_distribution_account, + payer: self.tip_distribution_account_config.payer.pubkey(), + system_program: system_program::id(), + }, + ); + + SanitizedTransaction::try_from_legacy_transaction(Transaction::new_signed_with_payer( + &[ix], + Some(&self.tip_distribution_account_config.payer.pubkey()), + &[self.tip_distribution_account_config.payer.as_ref()], + recent_blockhash, + )) + .unwrap() + } + + /// Builds a transaction that changes the current tip receiver to new_tip_receiver. + /// The on-chain program will transfer tips sitting in the tip accounts to the tip receiver + /// before changing ownership. + pub fn change_tip_receiver_tx( + &self, + new_tip_receiver: &Pubkey, + bank: &Bank, + keypair: &Keypair, + ) -> Result { + let config = self.get_tip_payment_config_account(bank)?; + + let change_tip_ix = Instruction { + program_id: self.tip_payment_program_info.program_id, + data: tip_payment::instruction::ChangeTipReceiver {}.data(), + accounts: tip_payment::accounts::ChangeTipReceiver { + config: self.tip_payment_program_info.config_pda_bump.0, + old_tip_receiver: config.tip_receiver, + new_tip_receiver: *new_tip_receiver, + tip_payment_account_0: self.tip_payment_program_info.tip_pda_0.0, + tip_payment_account_1: self.tip_payment_program_info.tip_pda_1.0, + tip_payment_account_2: self.tip_payment_program_info.tip_pda_2.0, + tip_payment_account_3: self.tip_payment_program_info.tip_pda_3.0, + tip_payment_account_4: self.tip_payment_program_info.tip_pda_4.0, + tip_payment_account_5: self.tip_payment_program_info.tip_pda_5.0, + tip_payment_account_6: self.tip_payment_program_info.tip_pda_6.0, + tip_payment_account_7: self.tip_payment_program_info.tip_pda_7.0, + signer: keypair.pubkey(), + } + .to_account_metas(None), + }; + Ok( + SanitizedTransaction::try_from_legacy_transaction(Transaction::new_signed_with_payer( + &[ + // TODO (LB): make the on-chain program more efficient and remove this + ComputeBudgetInstruction::set_compute_unit_limit(1_000_000), + change_tip_ix, + ], + Some(&keypair.pubkey()), + &[keypair], + bank.last_blockhash(), + )) + .unwrap(), + ) + } + + /// Returns the balance of all the MEV tip accounts + pub fn get_tip_account_balances(&self, bank: &Arc) -> Vec<(Pubkey, u64)> { + let accounts = self.get_tip_accounts(); + accounts + .into_iter() + .map(|account| { + let balance = bank.get_balance(&account); + (account, balance) + }) + .collect() + } + + /// Returns the balance of all the MEV tip accounts above the rent-exempt amount. + /// NOTE: the on-chain program has rent_exempt = force + pub fn get_tip_account_balances_above_rent_exempt( + &self, + bank: &Arc, + ) -> Vec<(Pubkey, u64)> { + let accounts = self.get_tip_accounts(); + accounts + .into_iter() + .map(|account| { + let account_data = bank.get_account(&account).unwrap_or_default(); + let balance = bank.get_balance(&account); + let rent_exempt = + bank.get_minimum_balance_for_rent_exemption(account_data.data().len()); + // NOTE: don't unwrap here in case bug in on-chain program, don't want all validators to crash + // if program gets stuck in bad state + (account, balance.checked_sub(rent_exempt).unwrap_or_else(|| { + warn!("balance is below rent exempt amount. balance: {} rent_exempt: {} acc size: {}", balance, rent_exempt, TipPaymentAccount::SIZE); + 0 + })) + }) + .collect() + } +} diff --git a/core/src/tpu.rs b/core/src/tpu.rs index 27c7a7c6b2..a314f3b75f 100644 --- a/core/src/tpu.rs +++ b/core/src/tpu.rs @@ -5,15 +5,23 @@ use { crate::{ banking_stage::BankingStage, broadcast_stage::{BroadcastStage, BroadcastStageType, RetransmitSlotsReceiver}, + bundle_account_locker::BundleAccountLocker, + bundle_stage::BundleStage, cluster_info_vote_listener::{ ClusterInfoVoteListener, GossipDuplicateConfirmedSlotsSender, GossipVerifiedVoteHashSender, VerifiedVoteSender, VoteTracker, }, fetch_stage::FetchStage, find_packet_sender_stake_stage::FindPacketSenderStakeStage, + proxy::{ + block_engine_stage::{BlockEngineConfig, BlockEngineStage}, + fetch_stage_manager::FetchStageManager, + relayer_stage::{RelayerConfig, RelayerStage}, + }, sigverify::TransactionSigVerifier, sigverify_stage::SigVerifyStage, staked_nodes_updater_service::StakedNodesUpdaterService, + tip_manager::{TipManager, TipManagerConfig}, }, crossbeam_channel::{unbounded, Receiver}, solana_gossip::cluster_info::ClusterInfo, @@ -35,8 +43,8 @@ use { }, solana_tpu_client::connection_cache::ConnectionCache, std::{ - collections::HashMap, - net::UdpSocket, + collections::{HashMap, HashSet}, + net::{SocketAddr, UdpSocket}, sync::{atomic::AtomicBool, Arc, RwLock}, thread, }, @@ -60,6 +68,9 @@ pub struct Tpu { fetch_stage: FetchStage, sigverify_stage: SigVerifyStage, vote_sigverify_stage: SigVerifyStage, + maybe_relayer_stage: Option, + maybe_block_engine_stage: Option, + maybe_fetch_stage_manager: Option, banking_stage: BankingStage, cluster_info_vote_listener: ClusterInfoVoteListener, broadcast_stage: BroadcastStage, @@ -68,6 +79,7 @@ pub struct Tpu { find_packet_sender_stake_stage: FindPacketSenderStakeStage, vote_find_packet_sender_stake_stage: FindPacketSenderStakeStage, staked_nodes_updater_service: StakedNodesUpdaterService, + bundle_stage: BundleStage, } impl Tpu { @@ -98,6 +110,10 @@ impl Tpu { keypair: &Keypair, log_messages_bytes_limit: Option, staked_nodes: &Arc>, + maybe_block_engine_config: Option, + maybe_relayer_config: Option, + tip_manager_config: TipManagerConfig, + shred_receiver_address: Option, shared_staked_nodes_overrides: Arc>>, tpu_enable_udp: bool, ) -> Self { @@ -110,7 +126,17 @@ impl Tpu { transactions_forwards_quic: transactions_forwards_quic_sockets, } = sockets; + let (packet_intercept_sender, packet_intercept_receiver) = unbounded(); let (packet_sender, packet_receiver) = unbounded(); + + // If there's a relayer, we need to redirect packets to the interceptor + // If not, they can flow straight through + let packet_send_channel = if maybe_relayer_config.is_some() { + packet_intercept_sender + } else { + packet_sender.clone() + }; + let (vote_packet_sender, vote_packet_receiver) = unbounded(); let (forwarded_packet_sender, forwarded_packet_receiver) = unbounded(); let fetch_stage = FetchStage::new_with_sender( @@ -118,7 +144,7 @@ impl Tpu { tpu_forwards_sockets, tpu_vote_sockets, exit, - &packet_sender, + &packet_send_channel, &vote_packet_sender, &forwarded_packet_sender, forwarded_packet_receiver, @@ -162,7 +188,7 @@ impl Tpu { transactions_quic_sockets, keypair, cluster_info.my_contact_info().tpu.ip(), - packet_sender, + packet_send_channel, exit.clone(), MAX_QUIC_CONNECTIONS_PER_PEER, staked_nodes.clone(), @@ -187,7 +213,7 @@ impl Tpu { .unwrap(); let sigverify_stage = { - let verifier = TransactionSigVerifier::new(verified_sender); + let verifier = TransactionSigVerifier::new(verified_sender.clone()); SigVerifyStage::new(find_packet_sender_stake_receiver, verifier, "tpu-verifier") }; @@ -203,6 +229,39 @@ impl Tpu { ) }; + let (bundle_sender, bundle_receiver) = unbounded(); + let maybe_block_engine_stage = maybe_block_engine_config.map(|block_engine_config| { + BlockEngineStage::new( + block_engine_config, + bundle_sender, + cluster_info.clone(), + packet_sender.clone(), + verified_sender.clone(), + exit.clone(), + ) + }); + + let (heartbeat_tx, heartbeat_rx) = unbounded(); + let maybe_fetch_stage_manager = maybe_relayer_config.as_ref().map(|_| { + FetchStageManager::new( + cluster_info.clone(), + heartbeat_rx, + packet_intercept_receiver, + packet_sender.clone(), + exit.clone(), + ) + }); + let maybe_relayer_stage = maybe_relayer_config.map(|relayer_config| { + RelayerStage::new( + relayer_config, + cluster_info.clone(), + heartbeat_tx, + packet_sender, + verified_sender, + exit.clone(), + ) + }); + let (verified_gossip_vote_packets_sender, verified_gossip_vote_packets_receiver) = unbounded(); let cluster_info_vote_listener = ClusterInfoVoteListener::new( @@ -221,18 +280,41 @@ impl Tpu { cluster_confirmed_slot_sender, ); + let tip_manager = TipManager::new(tip_manager_config); + + let bundle_account_locker = BundleAccountLocker::default(); + + // tip accounts can't be used in BankingStage to avoid someone from stealing tips mid-slot. + // it also helps reduce surface area for potential account contention + let mut blacklisted_accounts = HashSet::new(); + blacklisted_accounts.insert(tip_manager.tip_payment_config_pubkey()); + blacklisted_accounts.extend(tip_manager.get_tip_accounts()); let banking_stage = BankingStage::new( cluster_info, poh_recorder, verified_receiver, verified_tpu_vote_packets_receiver, verified_gossip_vote_packets_receiver, - transaction_status_sender, - replay_vote_sender, + transaction_status_sender.clone(), + replay_vote_sender.clone(), cost_model.clone(), log_messages_bytes_limit, connection_cache.clone(), bank_forks.clone(), + blacklisted_accounts, + bundle_account_locker.clone(), + ); + + let bundle_stage = BundleStage::new( + cluster_info, + poh_recorder, + transaction_status_sender, + replay_vote_sender, + cost_model.clone(), + bundle_receiver, + exit.clone(), + tip_manager, + bundle_account_locker, ); let broadcast_stage = broadcast_type.new_broadcast_stage( @@ -244,12 +326,16 @@ impl Tpu { blockstore.clone(), bank_forks, shred_version, + shred_receiver_address, ); Self { fetch_stage, sigverify_stage, vote_sigverify_stage, + maybe_block_engine_stage, + maybe_relayer_stage, + maybe_fetch_stage_manager, banking_stage, cluster_info_vote_listener, broadcast_stage, @@ -258,6 +344,7 @@ impl Tpu { find_packet_sender_stake_stage, vote_find_packet_sender_stake_stage, staked_nodes_updater_service, + bundle_stage, } } @@ -273,7 +360,19 @@ impl Tpu { self.staked_nodes_updater_service.join(), self.tpu_quic_t.join(), self.tpu_forwards_quic_t.join(), + self.bundle_stage.join(), ]; + + if let Some(relayer_stage) = self.maybe_relayer_stage { + relayer_stage.join()?; + } + if let Some(block_engine_stage) = self.maybe_block_engine_stage { + block_engine_stage.join()?; + } + if let Some(fetch_stage_manager) = self.maybe_fetch_stage_manager { + fetch_stage_manager.join()?; + } + let broadcast_result = self.broadcast_stage.join(); for result in results { result?; diff --git a/core/src/tvu.rs b/core/src/tvu.rs index 5e4adce7a3..0b0ded03ac 100644 --- a/core/src/tvu.rs +++ b/core/src/tvu.rs @@ -48,7 +48,7 @@ use { solana_tpu_client::connection_cache::ConnectionCache, std::{ collections::HashSet, - net::UdpSocket, + net::{SocketAddr, UdpSocket}, sync::{atomic::AtomicBool, Arc, RwLock}, thread::{self, JoinHandle}, }, @@ -131,6 +131,7 @@ impl Tvu { log_messages_bytes_limit: Option, connection_cache: &Arc, prioritization_fee_cache: &Arc, + shred_receiver_addr: Option, ) -> Result { let TvuSockets { repair: repair_socket, @@ -178,6 +179,7 @@ impl Tvu { retransmit_receiver, max_slots.clone(), Some(rpc_subscriptions.clone()), + shred_receiver_addr, ); let cluster_slots = Arc::new(ClusterSlots::default()); @@ -456,6 +458,7 @@ pub mod tests { None, &Arc::new(ConnectionCache::default()), &_ignored_prioritization_fee_cache, + None, ) .expect("assume success"); exit.store(true, Ordering::Relaxed); diff --git a/core/src/validator.rs b/core/src/validator.rs index f99b244485..10547c5f3d 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -11,6 +11,7 @@ use { consensus::{reconcile_blockstore_roots_with_external_source, ExternalRootSource, Tower}, ledger_metric_report_service::LedgerMetricReportService, poh_timing_report_service::PohTimingReportService, + proxy::{block_engine_stage::BlockEngineConfig, relayer_stage::RelayerConfig}, rewards_recorder_service::{RewardsRecorderSender, RewardsRecorderService}, sample_performance_service::SamplePerformanceService, serve_repair::ServeRepair, @@ -19,6 +20,7 @@ use { snapshot_packager_service::SnapshotPackagerService, stats_reporter_service::StatsReporterService, system_monitor_service::{verify_net_stats_access, SystemMonitorService}, + tip_manager::TipManagerConfig, tower_storage::TowerStorage, tpu::{Tpu, TpuSockets, DEFAULT_TPU_COALESCE_MS}, tvu::{Tvu, TvuConfig, TvuSockets}, @@ -178,6 +180,10 @@ pub struct ValidatorConfig { pub ledger_column_options: LedgerColumnOptions, pub runtime_config: RuntimeConfig, pub replay_slots_concurrently: bool, + pub maybe_relayer_config: Option, + pub maybe_block_engine_config: Option, + pub shred_receiver_address: Option, + pub tip_manager_config: TipManagerConfig, } impl Default for ValidatorConfig { @@ -243,6 +249,10 @@ impl Default for ValidatorConfig { ledger_column_options: LedgerColumnOptions::default(), runtime_config: RuntimeConfig::default(), replay_slots_concurrently: false, + maybe_relayer_config: None, + maybe_block_engine_config: None, + shred_receiver_address: None, + tip_manager_config: TipManagerConfig::default(), } } } @@ -990,6 +1000,7 @@ impl Validator { config.runtime_config.log_messages_bytes_limit, &connection_cache, &prioritization_fee_cache, + config.shred_receiver_address, )?; let tpu = Tpu::new( @@ -1025,6 +1036,10 @@ impl Validator { &identity_keypair, config.runtime_config.log_messages_bytes_limit, &staked_nodes, + config.maybe_block_engine_config.clone(), + config.maybe_relayer_config.clone(), + config.tip_manager_config.clone(), + config.shred_receiver_address, config.staked_nodes_overrides.clone(), tpu_enable_udp, ); @@ -1032,7 +1047,11 @@ impl Validator { datapoint_info!( "validator-new", ("id", id.to_string(), String), - ("version", solana_version::version!(), String) + ( + "version", + format!("jito-{}", solana_version::version!()), + String + ) ); *start_progress.write().unwrap() = ValidatorStartProgress::Running; @@ -2187,7 +2206,7 @@ mod tests { Arc::new(validator_keypair), &validator_ledger_path, &voting_keypair.pubkey(), - Arc::new(RwLock::new(vec![voting_keypair.clone()])), + Arc::new(RwLock::new(vec![voting_keypair])), vec![leader_node.info], &config, true, // should_check_duplicate_instance diff --git a/core/tests/epoch_accounts_hash.rs b/core/tests/epoch_accounts_hash.rs index 569ed3c173..5563a66bd2 100755 --- a/core/tests/epoch_accounts_hash.rs +++ b/core/tests/epoch_accounts_hash.rs @@ -428,6 +428,7 @@ fn test_snapshots_have_expected_epoch_accounts_hash() { if let Some(full_snapshot_archive_info) = snapshot_utils::get_highest_full_snapshot_archive_info( &snapshot_config.full_snapshot_archives_dir, + None, ) { if full_snapshot_archive_info.slot() == bank.slot() { diff --git a/core/tests/snapshots.rs b/core/tests/snapshots.rs index 21b169a84b..fadc9ba5b8 100644 --- a/core/tests/snapshots.rs +++ b/core/tests/snapshots.rs @@ -903,6 +903,7 @@ fn restore_from_snapshots_and_check_banks_are_equal( Some(ACCOUNTS_DB_CONFIG_FOR_TESTING), None, &Arc::default(), + None, )?; assert_eq!(bank, &deserialized_bank); @@ -1095,6 +1096,7 @@ fn test_snapshots_with_background_services( Some(ACCOUNTS_DB_CONFIG_FOR_TESTING), None, &Arc::default(), + None, ) .unwrap(); diff --git a/deploy_programs b/deploy_programs new file mode 100755 index 0000000000..cbdf837e92 --- /dev/null +++ b/deploy_programs @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +# Deploys the tip payment and tip distribution programs on local validator at predetermined address +set -eux + +WALLET_LOCATION=~/.config/solana/id.json + +# build this solana binary to ensure we're using a version compatible with the validator +cargo b --release --bin solana + +./target/release/solana airdrop -ul 1000 $WALLET_LOCATION + +(cd jito-programs/tip-payment && anchor build) + +# NOTE: make sure the declare_id! is set correctly in the programs +# Also, || true to make sure if fails the first time around, tip_payment can still be deployed +RUST_INFO=trace ./target/release/solana deploy --keypair $WALLET_LOCATION -ul ./jito-programs/tip-payment/target/deploy/tip_distribution.so ./jito-programs/tip-payment/dev/dev_tip_distribution.json || true +RUST_INFO=trace ./target/release/solana deploy --keypair $WALLET_LOCATION -ul ./jito-programs/tip-payment/target/deploy/tip_payment.so ./jito-programs/tip-payment/dev/dev_tip_payment.json diff --git a/dev/Dockerfile b/dev/Dockerfile new file mode 100644 index 0000000000..a3223390b2 --- /dev/null +++ b/dev/Dockerfile @@ -0,0 +1,43 @@ +FROM rust:1.63.0 + +# Add Google Protocol Buffers for Libra's metrics library. +ENV PROTOC_VERSION 3.8.0 +ENV PROTOC_ZIP protoc-$PROTOC_VERSION-linux-x86_64.zip + +RUN set -x \ + && apt update \ + && apt install -y \ + clang \ + cmake \ + libudev-dev \ + make \ + unzip \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + make \ + && rustup component add rustfmt \ + && rustup component add clippy \ + && rustc --version \ + && cargo --version \ + && curl -OL https://github.com/google/protobuf/releases/download/v$PROTOC_VERSION/$PROTOC_ZIP \ + && unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \ + && unzip -o $PROTOC_ZIP -d /usr/local include/* \ + && rm -f $PROTOC_ZIP + + +WORKDIR /solana +COPY . . +RUN mkdir -p docker-output + +ARG ci_commit +ENV CI_COMMIT=$ci_commit + +# Uses docker buildkit to cache the image. +# /usr/local/cargo/git needed for crossbeam patch +RUN --mount=type=cache,mode=0777,target=/solana/target \ + --mount=type=cache,mode=0777,target=/usr/local/cargo/registry \ + --mount=type=cache,mode=0777,target=/usr/local/cargo/git \ + cargo build --verbose --release && cp target/release/solana* ./docker-output \ + && cd jito-programs/cli/claim-mev; cargo build --verbose --release \ + && cp target/release/claim-mev ../../../docker-output/ diff --git a/entry/src/entry.rs b/entry/src/entry.rs index 225e93b17f..ff72d35eb1 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -203,7 +203,7 @@ pub fn hash_transactions(transactions: &[VersionedTransaction]) -> Hash { .iter() .flat_map(|tx| tx.signatures.iter()) .collect(); - let merkle_tree = MerkleTree::new(&signatures); + let merkle_tree = MerkleTree::new(&signatures, false); if let Some(root_hash) = merkle_tree.get_root() { *root_hash } else { diff --git a/entry/src/poh.rs b/entry/src/poh.rs index 8a27a3ac1b..f017b7e5d6 100644 --- a/entry/src/poh.rs +++ b/entry/src/poh.rs @@ -73,19 +73,30 @@ impl Poh { } pub fn record(&mut self, mixin: Hash) -> Option { - if self.remaining_hashes == 1 { + let entries = self.record_bundle(&[mixin]); + entries.unwrap_or_default().pop() + } + + pub fn record_bundle(&mut self, mixins: &[Hash]) -> Option> { + if self.remaining_hashes <= mixins.len() as u64 { return None; // Caller needs to `tick()` first } - self.hash = hashv(&[self.hash.as_ref(), mixin.as_ref()]); - let num_hashes = self.num_hashes + 1; - self.num_hashes = 0; - self.remaining_hashes -= 1; + let entries = mixins + .iter() + .map(|m| { + self.hash = hashv(&[self.hash.as_ref(), m.as_ref()]); + let num_hashes = self.num_hashes + 1; + self.num_hashes = 0; + self.remaining_hashes -= 1; + PohEntry { + num_hashes, + hash: self.hash, + } + }) + .collect(); - Some(PohEntry { - num_hashes, - hash: self.hash, - }) + Some(entries) } pub fn tick(&mut self) -> Option { diff --git a/f b/f new file mode 100755 index 0000000000..9f276a138f --- /dev/null +++ b/f @@ -0,0 +1,25 @@ +#!/usr/bin/env sh +# Builds jito-solana in a docker container. +# Useful for running on machines that might not have cargo installed but can run docker (Flatcar Linux). +set -eux + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" + +GIT_SHA="$(git describe --always --dirty)" + +echo $GIT_SHA + +DOCKER_BUILDKIT=1 docker build \ + --build-arg ci_commit=$GIT_SHA \ + -t jitolabs/build-solana \ + -f dev/Dockerfile . \ + --progress=plain + +# Creates a temporary container, copies solana-validator built inside container there and +# removes the temporary container. +docker rm temp || true +docker container create --name temp jitolabs/build-solana +mkdir -p $SCRIPT_DIR/docker-output +# Outputs the solana-validator binary to $SOLANA/docker-output/solana-validator +docker container cp temp:/solana/docker-output $SCRIPT_DIR/ +docker rm temp diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs index 81d83ec917..023491f6a4 100644 --- a/gossip/src/cluster_info.rs +++ b/gossip/src/cluster_info.rs @@ -513,6 +513,10 @@ impl ClusterInfo { *self.entrypoints.write().unwrap() = entrypoints; } + pub fn set_my_contact_info(&self, my_contact_info: ContactInfo) { + *self.my_contact_info.write().unwrap() = my_contact_info; + } + pub fn save_contact_info(&self) { let nodes = { let entrypoint_gossip_addrs = self diff --git a/jito-programs b/jito-programs new file mode 160000 index 0000000000..3bd3ab41bf --- /dev/null +++ b/jito-programs @@ -0,0 +1 @@ +Subproject commit 3bd3ab41bf82474cc0eaeb0ea2bab677f7a841e4 diff --git a/jito-protos/Cargo.toml b/jito-protos/Cargo.toml new file mode 100644 index 0000000000..0646342afb --- /dev/null +++ b/jito-protos/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "jito-protos" +version = "1.15.0" +edition = "2021" +publish = false + +[dependencies] +bytes = "1.1.0" +prost = "0.8.0" +prost-types = "0.8.0" +tonic = "0.5.2" + +[build-dependencies] +tonic-build = "0.5.2" diff --git a/jito-protos/build.rs b/jito-protos/build.rs new file mode 100644 index 0000000000..54c8b8d5e7 --- /dev/null +++ b/jito-protos/build.rs @@ -0,0 +1,17 @@ +use tonic_build::configure; + +fn main() { + configure() + .compile( + &[ + "protos/auth.proto", + "protos/block_engine.proto", + "protos/bundle.proto", + "protos/packet.proto", + "protos/relayer.proto", + "protos/shared.proto", + ], + &["protos"], + ) + .unwrap(); +} diff --git a/jito-protos/protos b/jito-protos/protos new file mode 160000 index 0000000000..1c4600d267 --- /dev/null +++ b/jito-protos/protos @@ -0,0 +1 @@ +Subproject commit 1c4600d267afa75ec16ce90daffb4dd2203ed74e diff --git a/jito-protos/src/lib.rs b/jito-protos/src/lib.rs new file mode 100644 index 0000000000..cf630c53d2 --- /dev/null +++ b/jito-protos/src/lib.rs @@ -0,0 +1,25 @@ +pub mod proto { + pub mod auth { + tonic::include_proto!("auth"); + } + + pub mod block_engine { + tonic::include_proto!("block_engine"); + } + + pub mod bundle { + tonic::include_proto!("bundle"); + } + + pub mod packet { + tonic::include_proto!("packet"); + } + + pub mod relayer { + tonic::include_proto!("relayer"); + } + + pub mod shared { + tonic::include_proto!("shared"); + } +} diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index ce982512f4..d769f40caf 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -1042,13 +1042,15 @@ fn load_bank_forks( snapshot_archive_path.unwrap_or_else(|| blockstore.ledger_path().to_path_buf()); let incremental_snapshot_archives_dir = incremental_snapshot_archive_path.unwrap_or_else(|| full_snapshot_archives_dir.clone()); - if let Some(full_snapshot_slot) = - snapshot_utils::get_highest_full_snapshot_archive_slot(&full_snapshot_archives_dir) - { + if let Some(full_snapshot_slot) = snapshot_utils::get_highest_full_snapshot_archive_slot( + &full_snapshot_archives_dir, + process_options.halt_at_slot, + ) { let incremental_snapshot_slot = snapshot_utils::get_highest_incremental_snapshot_archive_slot( &incremental_snapshot_archives_dir, full_snapshot_slot, + process_options.halt_at_slot, ) .unwrap_or_default(); starting_slot = std::cmp::max(full_snapshot_slot, incremental_snapshot_slot); @@ -1067,8 +1069,8 @@ fn load_bank_forks( // - This will not catch the case when loading from genesis without a full slot 0. if !blockstore.slot_range_connected(starting_slot, halt_slot) { eprintln!( - "Unable to load bank forks at slot {} due to disconnected blocks.", - halt_slot, + "Unable to load bank forks [start {} end {}] due to disconnected blocks.", + starting_slot, halt_slot, ); exit(1); } @@ -2981,6 +2983,21 @@ fn main() { "snapshot slot doesn't exist" ); + if let Ok(metas) = blockstore.slot_meta_iterator(0) { + let slots: Vec<_> = metas.map(|(slot, _)| slot).collect(); + if slots.is_empty() { + eprintln!("Ledger is empty, can't create snapshot"); + exit(1); + } else { + let first = slots.first().unwrap(); + let last = slots.last().unwrap_or(first); + if first > &snapshot_slot || &snapshot_slot > last { + eprintln!("Slot {} is out of bounds of ledger [{}, {}], cannot create snapshot", &snapshot_slot, first, last); + exit(1); + } + } + } + let ending_slot = if is_minimized { let ending_slot = value_t_or_exit!(arg_matches, "ending_slot", Slot); if ending_slot <= snapshot_slot { diff --git a/ledger/src/bank_forks_utils.rs b/ledger/src/bank_forks_utils.rs index 499bbe15ce..20f2aad546 100644 --- a/ledger/src/bank_forks_utils.rs +++ b/ledger/src/bank_forks_utils.rs @@ -103,6 +103,7 @@ pub fn load_bank_forks( if snapshot_utils::get_highest_full_snapshot_archive_info( &snapshot_config.full_snapshot_archives_dir, + process_options.halt_at_slot, ) .is_some() { @@ -184,7 +185,7 @@ pub fn load_bank_forks( } #[allow(clippy::too_many_arguments)] -fn bank_forks_from_snapshot( +pub fn bank_forks_from_snapshot( genesis_config: &GenesisConfig, account_paths: Vec, shrink_paths: Option>, @@ -221,6 +222,7 @@ fn bank_forks_from_snapshot( process_options.accounts_db_config.clone(), accounts_update_notifier, exit, + process_options.halt_at_slot, ) .expect("Load from snapshot failed"); diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index fc3ae1d523..3c9a4a245d 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -186,7 +186,7 @@ fn execute_batch( let mut mint_decimals: HashMap = HashMap::new(); let pre_token_balances = if record_token_balances { - collect_token_balances(bank, batch, &mut mint_decimals) + collect_token_balances(bank, batch, &mut mint_decimals, None) } else { vec![] }; @@ -244,7 +244,7 @@ fn execute_batch( if let Some(transaction_status_sender) = transaction_status_sender { let transactions = batch.sanitized_transactions().to_vec(); let post_token_balances = if record_token_balances { - collect_token_balances(bank, batch, &mut mint_decimals) + collect_token_balances(bank, batch, &mut mint_decimals, None) } else { vec![] }; diff --git a/ledger/src/token_balances.rs b/ledger/src/token_balances.rs index 673cc5556b..586bbd8526 100644 --- a/ledger/src/token_balances.rs +++ b/ledger/src/token_balances.rs @@ -5,7 +5,9 @@ use { }, solana_measure::measure::Measure, solana_metrics::datapoint_debug, - solana_runtime::{bank::Bank, transaction_batch::TransactionBatch}, + solana_runtime::{ + account_overrides::AccountOverrides, bank::Bank, transaction_batch::TransactionBatch, + }, solana_sdk::{account::ReadableAccount, pubkey::Pubkey}, solana_transaction_status::{ token_balances::TransactionTokenBalances, TransactionTokenBalance, @@ -39,6 +41,7 @@ pub fn collect_token_balances( bank: &Bank, batch: &TransactionBatch, mint_decimals: &mut HashMap, + cached_accounts: Option<&AccountOverrides>, ) -> TransactionTokenBalances { let mut balances: TransactionTokenBalances = vec![]; let mut collect_time = Measure::start("collect_token_balances"); @@ -59,8 +62,12 @@ pub fn collect_token_balances( ui_token_amount, owner, program_id, - }) = collect_token_balance_from_account(bank, account_id, mint_decimals) - { + }) = collect_token_balance_from_account( + bank, + account_id, + mint_decimals, + cached_accounts, + ) { transaction_balances.push(TransactionTokenBalance { account_index: index as u8, mint, @@ -93,8 +100,17 @@ fn collect_token_balance_from_account( bank: &Bank, account_id: &Pubkey, mint_decimals: &mut HashMap, + account_overrides: Option<&AccountOverrides>, ) -> Option { - let account = bank.get_account(account_id)?; + let account = { + if let Some(account_override) = + account_overrides.and_then(|overrides| overrides.get(account_id)) + { + Some(account_override.clone()) + } else { + bank.get_account(account_id) + } + }?; if !is_known_spl_token_id(account.owner()) { return None; @@ -237,13 +253,13 @@ mod test { // Account is not owned by spl_token (nor does it have TokenAccount state) assert_eq!( - collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals), + collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals, None), None ); // Mint does not have TokenAccount state assert_eq!( - collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals), + collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals, None), None ); @@ -252,7 +268,8 @@ mod test { collect_token_balance_from_account( &bank, &spl_token_account_pubkey, - &mut mint_decimals + &mut mint_decimals, + None ), Some(TokenBalanceData { mint: mint_pubkey.to_string(), @@ -269,7 +286,12 @@ mod test { // TokenAccount is not owned by known spl-token program_id assert_eq!( - collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals), + collect_token_balance_from_account( + &bank, + &other_account_pubkey, + &mut mint_decimals, + None + ), None ); @@ -278,7 +300,8 @@ mod test { collect_token_balance_from_account( &bank, &other_mint_account_pubkey, - &mut mint_decimals + &mut mint_decimals, + None ), None ); @@ -428,13 +451,13 @@ mod test { // Account is not owned by spl_token (nor does it have TokenAccount state) assert_eq!( - collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals), + collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals, None), None ); // Mint does not have TokenAccount state assert_eq!( - collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals), + collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals, None), None ); @@ -443,7 +466,8 @@ mod test { collect_token_balance_from_account( &bank, &spl_token_account_pubkey, - &mut mint_decimals + &mut mint_decimals, + None ), Some(TokenBalanceData { mint: mint_pubkey.to_string(), @@ -460,7 +484,12 @@ mod test { // TokenAccount is not owned by known spl-token program_id assert_eq!( - collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals), + collect_token_balance_from_account( + &bank, + &other_account_pubkey, + &mut mint_decimals, + None + ), None ); @@ -469,7 +498,8 @@ mod test { collect_token_balance_from_account( &bank, &other_mint_account_pubkey, - &mut mint_decimals + &mut mint_decimals, + None ), None ); diff --git a/local-cluster/src/local_cluster_snapshot_utils.rs b/local-cluster/src/local_cluster_snapshot_utils.rs index bd005622c7..f7161165f5 100644 --- a/local-cluster/src/local_cluster_snapshot_utils.rs +++ b/local-cluster/src/local_cluster_snapshot_utils.rs @@ -90,7 +90,10 @@ impl LocalCluster { let timer = Instant::now(); let next_snapshot = loop { if let Some(full_snapshot_archive_info) = - snapshot_utils::get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir) + snapshot_utils::get_highest_full_snapshot_archive_info( + &full_snapshot_archives_dir, + None, + ) { match next_snapshot_type { NextSnapshotType::FullSnapshot => { @@ -103,6 +106,7 @@ impl LocalCluster { snapshot_utils::get_highest_incremental_snapshot_archive_info( incremental_snapshot_archives_dir.as_ref().unwrap(), full_snapshot_archive_info.slot(), + None, ) { if incremental_snapshot_archive_info.slot() >= last_slot { diff --git a/local-cluster/src/validator_configs.rs b/local-cluster/src/validator_configs.rs index 88c744fae6..f930554838 100644 --- a/local-cluster/src/validator_configs.rs +++ b/local-cluster/src/validator_configs.rs @@ -67,6 +67,10 @@ pub fn safe_clone_config(config: &ValidatorConfig) -> ValidatorConfig { ledger_column_options: config.ledger_column_options.clone(), runtime_config: config.runtime_config.clone(), replay_slots_concurrently: config.replay_slots_concurrently, + maybe_relayer_config: config.maybe_relayer_config.clone(), + maybe_block_engine_config: config.maybe_block_engine_config.clone(), + shred_receiver_address: config.shred_receiver_address, + tip_manager_config: config.tip_manager_config.clone(), } } diff --git a/local-cluster/tests/local_cluster.rs b/local-cluster/tests/local_cluster.rs index 0670a231a0..702874c7c6 100644 --- a/local-cluster/tests/local_cluster.rs +++ b/local-cluster/tests/local_cluster.rs @@ -841,6 +841,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st validator_snapshot_test_config .full_snapshot_archives_dir .path(), + None, ) .unwrap(); info!( @@ -884,6 +885,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st .incremental_snapshot_archives_dir .path(), full_snapshot_archive.slot(), + None, ) .unwrap(); info!( @@ -1026,6 +1028,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st validator_snapshot_test_config .full_snapshot_archives_dir .path(), + None, ) .unwrap(); @@ -1087,6 +1090,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st validator_snapshot_test_config .full_snapshot_archives_dir .path(), + None, ) .unwrap(); @@ -1116,6 +1120,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st validator_snapshot_test_config .full_snapshot_archives_dir .path(), + None, ) { if full_snapshot_slot >= validator_next_full_snapshot_slot { if let Some(incremental_snapshot_slot) = @@ -1124,6 +1129,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st .incremental_snapshot_archives_dir .path(), full_snapshot_slot, + None, ) { if incremental_snapshot_slot >= validator_next_incremental_snapshot_slot { @@ -1332,8 +1338,10 @@ fn test_snapshots_blockstore_floor() { trace!("Waiting for snapshot tar to be generated with slot",); let archive_info = loop { - let archive = - snapshot_utils::get_highest_full_snapshot_archive_info(full_snapshot_archives_dir); + let archive = snapshot_utils::get_highest_full_snapshot_archive_info( + full_snapshot_archives_dir, + None, + ); if archive.is_some() { trace!("snapshot exists"); break archive.unwrap(); diff --git a/logger/Cargo.toml b/logger/Cargo.toml index 7823bda055..210ac94905 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-logger" edition = "2021" [dependencies] -env_logger = "0.9.0" +env_logger = "=0.9.0" lazy_static = "1.4.0" log = "0.4.17" diff --git a/merkle-tree/src/merkle_tree.rs b/merkle-tree/src/merkle_tree.rs index d08e111d4e..2ee984ec46 100644 --- a/merkle-tree/src/merkle_tree.rs +++ b/merkle-tree/src/merkle_tree.rs @@ -18,7 +18,7 @@ macro_rules! hash_intermediate { } } -#[derive(Debug)] +#[derive(Default, Debug, Eq, Hash, PartialEq)] pub struct MerkleTree { leaf_count: usize, nodes: Vec, @@ -36,6 +36,14 @@ impl<'a> ProofEntry<'a> { assert!(left_sibling.is_none() ^ right_sibling.is_none()); Self(target, left_sibling, right_sibling) } + + pub fn get_left_sibling(&self) -> Option<&'a Hash> { + self.1 + } + + pub fn get_right_sibling(&self) -> Option<&'a Hash> { + self.2 + } } #[derive(Debug, Default, PartialEq, Eq)] @@ -60,6 +68,10 @@ impl<'a> Proof<'a> { }); matches!(result, Some(_)) } + + pub fn get_proof_entries(self) -> Vec> { + self.0 + } } impl MerkleTree { @@ -95,7 +107,7 @@ impl MerkleTree { } } - pub fn new>(items: &[T]) -> Self { + pub fn new>(items: &[T], sorted_hashes: bool) -> Self { let cap = MerkleTree::calculate_vec_capacity(items.len()); let mut mt = MerkleTree { leaf_count: items.len(), @@ -123,8 +135,20 @@ impl MerkleTree { &mt.nodes[prev_level_start + prev_level_idx] }; - let hash = hash_intermediate!(lsib, rsib); - mt.nodes.push(hash); + // tip-distribution verification uses sorted hashing + if sorted_hashes { + if lsib <= rsib { + let hash = hash_intermediate!(lsib, rsib); + mt.nodes.push(hash); + } else { + let hash = hash_intermediate!(rsib, lsib); + mt.nodes.push(hash); + } + } else { + // hashing for solana internals + let hash = hash_intermediate!(lsib, rsib); + mt.nodes.push(hash); + } } prev_level_start = level_start; prev_level_len = level_len; @@ -189,21 +213,21 @@ mod tests { #[test] fn test_tree_from_empty() { - let mt = MerkleTree::new::<[u8; 0]>(&[]); + let mt = MerkleTree::new::<[u8; 0]>(&[], false); assert_eq!(mt.get_root(), None); } #[test] fn test_tree_from_one() { let input = b"test"; - let mt = MerkleTree::new(&[input]); + let mt = MerkleTree::new(&[input], false); let expected = hash_leaf!(input); assert_eq!(mt.get_root(), Some(&expected)); } #[test] fn test_tree_from_many() { - let mt = MerkleTree::new(TEST); + let mt = MerkleTree::new(TEST, false); // This golden hash will need to be updated whenever the contents of `TEST` change in any // way, including addition, removal and reordering or any of the tree calculation algo // changes @@ -215,7 +239,7 @@ mod tests { #[test] fn test_path_creation() { - let mt = MerkleTree::new(TEST); + let mt = MerkleTree::new(TEST, false); for (i, _s) in TEST.iter().enumerate() { let _path = mt.find_path(i).unwrap(); } @@ -223,13 +247,13 @@ mod tests { #[test] fn test_path_creation_bad_index() { - let mt = MerkleTree::new(TEST); + let mt = MerkleTree::new(TEST, false); assert_eq!(mt.find_path(TEST.len()), None); } #[test] fn test_path_verify_good() { - let mt = MerkleTree::new(TEST); + let mt = MerkleTree::new(TEST, false); for (i, s) in TEST.iter().enumerate() { let hash = hash_leaf!(s); let path = mt.find_path(i).unwrap(); @@ -239,7 +263,7 @@ mod tests { #[test] fn test_path_verify_bad() { - let mt = MerkleTree::new(TEST); + let mt = MerkleTree::new(TEST, false); for (i, s) in BAD.iter().enumerate() { let hash = hash_leaf!(s); let path = mt.find_path(i).unwrap(); diff --git a/multinode-demo/bootstrap-validator.sh b/multinode-demo/bootstrap-validator.sh index fb6353f76b..fb2264ac3c 100755 --- a/multinode-demo/bootstrap-validator.sh +++ b/multinode-demo/bootstrap-validator.sh @@ -106,6 +106,36 @@ while [[ -n $1 ]]; do elif [[ $1 == --skip-require-tower ]]; then maybeRequireTower=false shift + elif [[ $1 == --trust-relayer-packets ]]; then + args+=("$1") + shift + elif [[ $1 == --trust-block-engine-packets ]]; then + args+=("$1") + shift + elif [[ $1 == --relayer-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --block-engine-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --block-engine-auth-service-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --relayer-auth-service-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --tip-payment-program-pubkey ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --tip-distribution-program-pubkey ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --commission-bps ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --shred-receiver-address ]]; then + args+=("$1" "$2") + shift 2 elif [[ $1 = --log-messages-bytes-limit ]]; then args+=("$1" "$2") shift 2 @@ -123,6 +153,7 @@ done # These keypairs are created by ./setup.sh and included in the genesis config identity=$SOLANA_CONFIG_DIR/bootstrap-validator/identity.json +tip_distribution_account_payer=$SOLANA_CONFIG_DIR/bootstrap-validator/identity.json vote_account="$SOLANA_CONFIG_DIR"/bootstrap-validator/vote-account.json ledger_dir="$SOLANA_CONFIG_DIR"/bootstrap-validator @@ -144,6 +175,8 @@ args+=( --no-incremental-snapshots --identity "$identity" --vote-account "$vote_account" + --tip-distribution-account-payer "$tip_distribution_account_payer" + --merkle-root-upload-authority "$identity" --rpc-faucet-address 127.0.0.1:9900 --no-poh-speed-test --no-os-network-limits-test @@ -152,6 +185,9 @@ args+=( ) default_arg --gossip-port 8001 default_arg --log - +default_arg --tip-payment-program-pubkey "DThZmRNNXh7kvTQW9hXeGoWGPKktK8pgVAyoTLjH7UrT" +default_arg --tip-distribution-program-pubkey "FjrdANjvo76aCYQ4kf9FM1R8aESUcEE6F8V7qyoVUQcM" +default_arg --commission-bps 0 pid= diff --git a/multinode-demo/validator.sh b/multinode-demo/validator.sh index f339726b5b..8d5589afc7 100755 --- a/multinode-demo/validator.sh +++ b/multinode-demo/validator.sh @@ -86,6 +86,33 @@ while [[ -n $1 ]]; do vote_account=$2 args+=("$1" "$2") shift 2 + elif [[ $1 == --relayer-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --block-engine-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --block-engine-auth-service-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --relayer-auth-service-address ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 = --tip-distribution-account-payer ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 = --merkle-root-upload-authority ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --tip-payment-program-pubkey ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --tip-distribution-program-pubkey ]]; then + args+=("$1" "$2") + shift 2 + elif [[ $1 == --commission-bps ]]; then + args+=("$1" "$2") + shift 2 elif [[ $1 = --init-complete-file ]]; then args+=("$1" "$2") shift 2 @@ -261,6 +288,11 @@ fi default_arg --identity "$identity" default_arg --vote-account "$vote_account" +default_arg --merkle-root-upload-authority "$identity" +default_arg --tip-distribution-account-payer "$identity" +default_arg --tip-payment-program-pubkey "DThZmRNNXh7kvTQW9hXeGoWGPKktK8pgVAyoTLjH7UrT" +default_arg --tip-distribution-program-pubkey "FjrdANjvo76aCYQ4kf9FM1R8aESUcEE6F8V7qyoVUQcM" +default_arg --commission-bps 0 default_arg --ledger "$ledger_dir" default_arg --log - default_arg --full-rpc-api diff --git a/perf/src/sigverify.rs b/perf/src/sigverify.rs index da8bd27ae0..0b0fdb0857 100644 --- a/perf/src/sigverify.rs +++ b/perf/src/sigverify.rs @@ -119,7 +119,7 @@ pub fn init() { /// Returns true if the signatrue on the packet verifies. /// Caller must do packet.set_discard(true) if this returns false. #[must_use] -fn verify_packet(packet: &mut Packet, reject_non_vote: bool) -> bool { +pub fn verify_packet(packet: &mut Packet, reject_non_vote: bool) -> bool { // If this packet was already marked as discard, drop it if packet.meta.discard() { return false; diff --git a/poh/src/poh_recorder.rs b/poh/src/poh_recorder.rs index d6c85c3fdf..ffa051e1e2 100644 --- a/poh/src/poh_recorder.rs +++ b/poh/src/poh_recorder.rs @@ -57,9 +57,14 @@ pub enum PohRecorderError { SendError(#[from] SendError), } -type Result = std::result::Result; +pub type Result = std::result::Result; -pub type WorkingBankEntry = (Arc, (Entry, u64)); +#[derive(Clone, Debug)] +pub struct WorkingBankEntry { + pub bank: Arc, + // normal entries have len == 1, bundles have len > 1 + pub entries_ticks: Vec<(Entry, u64)>, +} #[derive(Clone)] pub struct BankStart { @@ -85,21 +90,19 @@ impl BankStart { type RecordResultSender = Sender>>; pub struct Record { - pub mixin: Hash, - pub transactions: Vec, + // non-bundles shall have mixins_txs.len() == 1, bundles shall have mixins_txs.len() > 1 + pub mixins_txs: Vec<(Hash, Vec)>, pub slot: Slot, pub sender: RecordResultSender, } impl Record { pub fn new( - mixin: Hash, - transactions: Vec, + mixins_txs: Vec<(Hash, Vec)>, slot: Slot, sender: RecordResultSender, ) -> Self { Self { - mixin, - transactions, + mixins_txs, slot, sender, } @@ -127,18 +130,18 @@ impl TransactionRecorder { is_exited, } } + // Returns the index of `transactions.first()` in the slot, if being tracked by WorkingBank pub fn record( &self, bank_slot: Slot, - mixin: Hash, - transactions: Vec, + mixins_txs: Vec<(Hash, Vec)>, ) -> Result> { // create a new channel so that there is only 1 sender and when it goes out of scope, the receiver fails let (result_sender, result_receiver) = unbounded(); - let res = - self.record_sender - .send(Record::new(mixin, transactions, bank_slot, result_sender)); + let res = self + .record_sender + .send(Record::new(mixins_txs, bank_slot, result_sender)); if res.is_err() { // If the channel is dropped, then the validator is shutting down so return that we are hitting // the max tick height to stop transaction processing and flush any transactions in the pipeline. @@ -566,7 +569,10 @@ impl PohRecorder { for tick in &self.tick_cache[..entry_count] { working_bank.bank.register_tick(&tick.0.hash); - send_result = self.sender.send((working_bank.bank.clone(), tick.clone())); + send_result = self.sender.send(WorkingBankEntry { + bank: working_bank.bank.clone(), + entries_ticks: vec![tick.clone()], + }); if send_result.is_err() { break; } @@ -746,16 +752,23 @@ impl PohRecorder { pub fn record( &mut self, bank_slot: Slot, - mixin: Hash, - transactions: Vec, + mixins_txs: &[(Hash, Vec)], ) -> Result> { // Entries without transactions are used to track real-time passing in the ledger and // cannot be generated by `record()` - assert!(!transactions.is_empty(), "No transactions provided"); + assert!(!mixins_txs.is_empty(), "No transactions provided"); + assert!( + !mixins_txs.iter().any(|(_, txs)| txs.is_empty()), + "One of mixins is missing txs" + ); let ((), report_metrics_time) = measure!(self.report_metrics(bank_slot), "report_metrics"); self.report_metrics_us += report_metrics_time.as_us(); + let mixins: Vec = mixins_txs.iter().map(|(m, _)| *m).collect(); + let transactions: Vec> = + mixins_txs.iter().map(|(_, tx)| tx.clone()).collect(); + loop { let (flush_cache_res, flush_cache_time) = measure!(self.flush_cache(false), "flush_cache"); @@ -773,23 +786,36 @@ impl PohRecorder { let (mut poh_lock, poh_lock_time) = measure!(self.poh.lock().unwrap(), "poh_lock"); self.record_lock_contention_us += poh_lock_time.as_us(); - let (record_mixin_res, record_mixin_time) = - measure!(poh_lock.record(mixin), "record_mixin"); + let (maybe_entries, record_mixin_time) = + measure!(poh_lock.record_bundle(&mixins), "record_mixin"); self.record_us += record_mixin_time.as_us(); drop(poh_lock); - if let Some(poh_entry) = record_mixin_res { - let num_transactions = transactions.len(); + if let Some(entries) = maybe_entries { + assert_eq!(entries.len(), transactions.len()); + let num_transactions = transactions.iter().map(|txs| txs.len()).sum(); let (send_entry_res, send_entry_time) = measure!( { - let entry = Entry { - num_hashes: poh_entry.num_hashes, - hash: poh_entry.hash, - transactions, - }; + let entries_tick_heights: Vec<(Entry, u64)> = entries + .into_iter() + .zip(transactions.into_iter()) + .map(|(poh_entry, transactions)| { + ( + Entry { + num_hashes: poh_entry.num_hashes, + hash: poh_entry.hash, + transactions, + }, + self.tick_height, + ) + }) + .collect(); let bank_clone = working_bank.bank.clone(); - self.sender.send((bank_clone, (entry, self.tick_height))) + self.sender.send(WorkingBankEntry { + bank: bank_clone, + entries_ticks: entries_tick_heights, + }) }, "send_poh_entry", ); @@ -1167,13 +1193,17 @@ mod tests { assert_eq!(poh_recorder.tick_height, tick_height_before + 1); assert_eq!(poh_recorder.tick_cache.len(), 0); let mut num_entries = 0; - while let Ok((wbank, (_entry, _tick_height))) = entry_receiver.try_recv() { + while let Ok(WorkingBankEntry { + bank: wbank, + entries_ticks, + }) = entry_receiver.try_recv() + { assert_eq!(wbank.slot(), bank1.slot()); - num_entries += 1; + num_entries += entries_ticks.len(); } // All the cached ticks, plus the new tick above should have been flushed - assert_eq!(num_entries, num_new_ticks + 1); + assert_eq!(num_entries as u64, num_new_ticks + 1); } Blockstore::destroy(&ledger_path).unwrap(); } @@ -1262,7 +1292,7 @@ mod tests { // We haven't yet reached the minimum tick height for the working bank, // so record should fail assert_matches!( - poh_recorder.record(bank1.slot(), h1, vec![tx.into()]), + poh_recorder.record(bank1.slot(), &[(h1, vec![tx.into()])]), Err(PohRecorderError::MinHeightNotReached) ); assert!(entry_receiver.try_recv().is_err()); @@ -1305,7 +1335,7 @@ mod tests { // However we hand over a bad slot so record fails let bad_slot = bank.slot() + 1; assert_matches!( - poh_recorder.record(bad_slot, h1, vec![tx.into()]), + poh_recorder.record(bad_slot, &[(h1, vec![tx.into()])]), Err(PohRecorderError::MaxHeightReached) ); } @@ -1352,17 +1382,27 @@ mod tests { let tx = test_tx(); let h1 = hash(b"hello world!"); assert!(poh_recorder - .record(bank1.slot(), h1, vec![tx.into()]) + .record(bank1.slot(), &[(h1, vec![tx.into()])]) .is_ok()); assert_eq!(poh_recorder.tick_cache.len(), 0); //tick in the cache + entry for _ in 0..min_tick_height { - let (_bank, (e, _tick_height)) = entry_receiver.recv().unwrap(); + let WorkingBankEntry { + bank: _, + entries_ticks, + } = entry_receiver.recv().unwrap(); + assert_eq!(entries_ticks.len(), 1); + let e = entries_ticks.get(0).unwrap().0.clone(); assert!(e.is_tick()); } - let (_bank, (e, _tick_height)) = entry_receiver.recv().unwrap(); + let WorkingBankEntry { + bank: _, + entries_ticks, + } = entry_receiver.recv().unwrap(); + assert_eq!(entries_ticks.len(), 1); + let e = entries_ticks.get(0).unwrap().0.clone(); assert!(!e.is_tick()); } Blockstore::destroy(&ledger_path).unwrap(); @@ -1398,10 +1438,16 @@ mod tests { let tx = test_tx(); let h1 = hash(b"hello world!"); assert!(poh_recorder - .record(bank.slot(), h1, vec![tx.into()]) + .record(bank.slot(), &[(h1, vec![tx.into()])]) .is_err()); + for _ in 0..num_ticks_to_max { - let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap(); + let WorkingBankEntry { + bank: _, + entries_ticks, + } = entry_receiver.recv().unwrap(); + assert_eq!(entries_ticks.len(), 1); + let entry = entries_ticks.get(0).unwrap().0.clone(); assert!(entry.is_tick()); } } @@ -1446,7 +1492,7 @@ mod tests { let tx1 = test_tx(); let h1 = hash(b"hello world!"); let record_result = poh_recorder - .record(bank.slot(), h1, vec![tx0.into(), tx1.into()]) + .record(bank.slot(), &[(h1, vec![tx0.into(), tx1.into()])]) .unwrap() .unwrap(); assert_eq!(record_result, 0); @@ -1463,7 +1509,7 @@ mod tests { let tx = test_tx(); let h2 = hash(b"foobar"); let record_result = poh_recorder - .record(bank.slot(), h2, vec![tx.into()]) + .record(bank.slot(), &[(h2, vec![tx.into()])]) .unwrap() .unwrap(); assert_eq!(record_result, 2); @@ -1730,7 +1776,7 @@ mod tests { let tx = test_tx(); let h1 = hash(b"hello world!"); assert!(poh_recorder - .record(bank.slot(), h1, vec![tx.into()]) + .record(bank.slot(), &[(h1, vec![tx.into()])]) .is_err()); assert!(poh_recorder.working_bank.is_none()); diff --git a/poh/src/poh_service.rs b/poh/src/poh_service.rs index 2b71c6ab61..d6d809f13c 100644 --- a/poh/src/poh_service.rs +++ b/poh/src/poh_service.rs @@ -194,11 +194,12 @@ impl PohService { if let Ok(record) = record { if record .sender - .send(poh_recorder.write().unwrap().record( - record.slot, - record.mixin, - record.transactions, - )) + .send( + poh_recorder + .write() + .unwrap() + .record(record.slot, &record.mixins_txs), + ) .is_err() { panic!("Error returning mixin hash"); @@ -257,11 +258,7 @@ impl PohService { timing.total_lock_time_ns += lock_time.as_ns(); let mut record_time = Measure::start("record"); loop { - let res = poh_recorder_l.record( - record.slot, - record.mixin, - std::mem::take(&mut record.transactions), - ); + let res = poh_recorder_l.record(record.slot, &record.mixins_txs); // what do we do on failure here? Ignore for now. let (_send_res, send_record_result_time) = measure!(record.sender.send(res), "send_record_result"); @@ -383,6 +380,7 @@ impl PohService { mod tests { use { super::*, + crate::poh_recorder::WorkingBankEntry, rand::{thread_rng, Rng}, solana_ledger::{ blockstore::Blockstore, @@ -462,11 +460,10 @@ mod tests { loop { // send some data let mut time = Measure::start("record"); - let _ = poh_recorder.write().unwrap().record( - bank_slot, - h1, - vec![tx.clone()], - ); + let _ = poh_recorder + .write() + .unwrap() + .record(bank_slot, &[(h1, vec![tx.clone()])]); time.stop(); total_us += time.as_us(); total_times += 1; @@ -511,7 +508,12 @@ mod tests { let time = Instant::now(); while run_time != 0 || need_tick || need_entry || need_partial { - let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap(); + let WorkingBankEntry { + bank: _, + entries_ticks, + } = entry_receiver.recv().unwrap(); + assert_eq!(entries_ticks.len(), 0); + let entry = entries_ticks.get(0).unwrap().0.clone(); if entry.is_tick() { num_ticks += 1; diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 0ced4666c7..7fb494681b 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -24,7 +24,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] @@ -36,7 +36,7 @@ dependencies = [ "cfg-if 1.0.0", "cipher 0.3.0", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -60,16 +60,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.4", + "getrandom 0.2.7", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -82,48 +82,187 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "alloc-no-stdlib" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "anchor-attribute-access-control" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "regex", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.4.0", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustversion", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.46", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-interface" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "heck 0.3.3", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-state" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.24.2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "anchor-lang" +version = "0.24.2" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-interface", + "anchor-attribute-program", + "anchor-attribute-state", + "anchor-derive-accounts", + "arrayref", + "base64 0.13.0", + "bincode", + "borsh", + "bytemuck", + "solana-program 1.15.0", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.24.2" +dependencies = [ + "anyhow", + "bs58 0.3.1", + "heck 0.3.3", + "proc-macro2 1.0.46", + "proc-macro2-diagnostics", + "quote 1.0.21", + "serde", + "serde_json", + "sha2 0.9.9", + "syn 1.0.102", + "thiserror", +] + [[package]] name = "android_system_properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi 0.3.9", ] [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arc-swap" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" [[package]] name = "arrayref" @@ -133,9 +272,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ascii" @@ -156,7 +295,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.9", + "time 0.3.15", ] [[package]] @@ -165,9 +304,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "synstructure", ] @@ -177,9 +316,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -190,9 +329,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "brotli", "flate2", @@ -227,9 +366,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -238,9 +377,9 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -271,9 +410,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.5.3" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f523b4e98ba6897ae90994bc18423d9877c54f9047b06a00ddc8122a957b1c70" +checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" dependencies = [ "async-trait", "axum-core", @@ -287,7 +426,7 @@ dependencies = [ "matchit", "memchr", "mime", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project-lite", "serde", "sync_wrapper", @@ -321,7 +460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.4", + "getrandom 0.2.7", "instant", "pin-project-lite", "rand 0.8.5", @@ -342,9 +481,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71acf5509fc522cce1b100ac0121c635129bfd4d91cdf036bcc9b9935f97ccf5" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" [[package]] name = "bincode" @@ -367,8 +506,8 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "regex", "rustc-hash", "shlex", @@ -400,19 +539,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "constant_time_eq", - "digest 0.10.3", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder 1.4.3", - "generic-array 0.12.4", + "digest 0.10.5", ] [[package]] @@ -421,26 +548,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding 0.2.1", - "generic-array 0.14.6", + "block-padding", + "generic-array", ] [[package]] name = "block-buffer" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "block-padding" -version = "0.1.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "byte-tools", + "generic-array", ] [[package]] @@ -468,8 +586,8 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.46", + "syn 1.0.102", ] [[package]] @@ -478,9 +596,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -489,9 +607,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -515,6 +633,12 @@ dependencies = [ "alloc-stdlib", ] +[[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" @@ -532,9 +656,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.3.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bv" @@ -546,30 +670,24 @@ dependencies = [ "serde", ] -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "bytemuck" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" +checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -662,16 +780,16 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time 0.1.43", + "time 0.1.44", "wasm-bindgen", "winapi 0.3.9", ] [[package]] name = "chrono-humanize" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb" +checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e" dependencies = [ "chrono", ] @@ -682,7 +800,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] @@ -697,9 +815,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -708,9 +826,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -723,18 +841,51 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.6" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", + "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.15.1", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[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 = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", ] [[package]] @@ -752,14 +903,13 @@ dependencies = [ [[package]] name = "console" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "regex", "terminal_size", "unicode-width", "winapi 0.3.9", @@ -833,20 +983,20 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.2.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] @@ -861,9 +1011,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -872,25 +1022,24 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.5" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ + "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "lazy_static", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", - "lazy_static", ] [[package]] @@ -901,11 +1050,11 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array", "typenum", ] @@ -915,7 +1064,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array", "subtle", ] @@ -942,6 +1091,50 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cxx" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2 1.0.46", + "quote 1.0.21", + "scratch", + "syn 1.0.102", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + [[package]] name = "dashmap" version = "4.0.2" @@ -995,10 +1188,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustc_version", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] @@ -1012,31 +1205,22 @@ dependencies = [ "zeroize", ] -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "block-buffer 0.10.0", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -1077,9 +1261,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -1113,9 +1297,9 @@ checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" [[package]] name = "ed25519" -version = "1.0.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf038a7b6fd7ef78ad3348b63f3a17550877b0e28f8d68bcc94894d1412158bc" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ "signature", ] @@ -1143,26 +1327,26 @@ dependencies = [ "derivation-path", "ed25519-dalek", "hmac 0.12.1", - "sha2 0.10.5", + "sha2 0.10.6", ] [[package]] name = "educe" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" +checksum = "c07b7cc9cd8c08d10db74fca3b20949b9b6199725c04a0cce6d543496098fcac" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "elf" @@ -1181,11 +1365,11 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.23" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] @@ -1203,22 +1387,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "enum-ordinalize" -version = "3.1.10" +version = "3.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" +checksum = "2170fc0efee383079a8bdd05d6ea2a184d2a0f07a1c1dcabdb2fd5e9f24bc36c" dependencies = [ "num-bigint 0.4.3", "num-traits", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "rustc_version", + "syn 1.0.102", ] [[package]] @@ -1228,9 +1413,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" dependencies = [ "once_cell", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -1284,15 +1469,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fast-math" @@ -1305,9 +1484,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -1320,7 +1499,7 @@ checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517" dependencies = [ "cfg-if 1.0.0", "rustix", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -1331,21 +1510,27 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" [[package]] name = "filetime" -version = "0.2.10" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", - "redox_syscall 0.1.56", - "winapi 0.3.9", + "redox_syscall", + "windows-sys", ] [[package]] name = "fixedbitset" -version = "0.4.1" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" @@ -1380,12 +1565,11 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", ] [[package]] @@ -1455,9 +1639,9 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -1500,15 +1684,6 @@ dependencies = [ "byteorder 1.4.3", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.6" @@ -1532,11 +1707,12 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", "wasm-bindgen", @@ -1544,14 +1720,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "js-sys", "libc", - "wasi 0.10.1+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1563,9 +1739,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" dependencies = [ "aho-corasick", "bstr", @@ -1589,15 +1765,15 @@ dependencies = [ "serde_json", "simpl", "smpl_jwt", - "time 0.3.9", + "time 0.3.15", "tokio", ] [[package]] name = "goblin" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c955ab4e0ad8c843ea653a3d143048b87490d9be56bd7132a435c2407846ac8f" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" dependencies = [ "log", "plain", @@ -1606,9 +1782,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ "bytes", "fnv", @@ -1619,7 +1795,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.7.2", "tracing", ] @@ -1652,9 +1828,9 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.0", "bitflags", @@ -1663,7 +1839,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha-1 0.10.0", + "sha1", ] [[package]] @@ -1692,9 +1868,9 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" -version = "0.1.13" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -1721,7 +1897,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -1731,7 +1907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array", "hmac 0.8.1", ] @@ -1765,21 +1941,21 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" @@ -1833,7 +2009,7 @@ dependencies = [ "hyper", "rustls 0.20.6", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls 0.23.4", ] [[package]] @@ -1863,17 +2039,28 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.46" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi 0.3.9", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "idna" version = "0.1.5" @@ -1887,11 +2074,10 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -1909,7 +2095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core 0.6.3", + "rand_core 0.6.4", "rand_xoshiro", "rayon", "serde", @@ -1948,18 +2134,18 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1f03d4ab4d5dc9ec2d219f86c15d2a15fc08239d1cd3b2d6a19717c0a2f443" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array", ] [[package]] name = "instant" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] @@ -1972,9 +2158,9 @@ checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" [[package]] name = "ipnet" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" @@ -1987,24 +2173,35 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "jito-protos" +version = "1.15.0" +dependencies = [ + "bytes", + "prost 0.8.0", + "prost-types 0.8.0", + "tonic 0.5.2", + "tonic-build 0.5.2", +] [[package]] name = "jobserver" -version = "0.1.21" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -2071,9 +2268,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -2136,15 +2333,15 @@ dependencies = [ "log", "tokio", "tokio-stream", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "unicase", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "kernel32-sys" @@ -2170,9 +2367,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libloading" @@ -2186,9 +2383,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" [[package]] name = "librocksdb-sys" @@ -2225,9 +2422,9 @@ dependencies = [ [[package]] name = "libsecp256k1" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", "base64 0.13.0", @@ -2299,20 +2496,29 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" dependencies = [ "cc", "pkg-config", "vcpkg", ] +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" @@ -2322,10 +2528,11 @@ checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] @@ -2367,12 +2574,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "matches" version = "0.1.9" @@ -2387,24 +2588,24 @@ checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] @@ -2417,7 +2618,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder 1.4.3", "keccak", - "rand_core 0.6.3", + "rand_core 0.6.4", "zeroize", ] @@ -2441,9 +2642,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", ] @@ -2486,9 +2687,9 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -2552,9 +2753,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi 0.3.9", ] @@ -2607,20 +2808,20 @@ dependencies = [ [[package]] name = "num-derive" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "num-integer" -version = "0.1.42" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -2683,17 +2884,17 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro-crate 1.2.1", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "num_threads" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] @@ -2715,15 +2916,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "opaque-debug" -version = "0.2.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -2733,9 +2928,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.41" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2752,9 +2947,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -2774,9 +2969,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg", "cc", @@ -2799,7 +2994,7 @@ dependencies = [ "futures-util", "js-sys", "lazy_static", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project", "rand 0.8.5", "thiserror", @@ -2807,35 +3002,31 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "ouroboros" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f31a3b678685b150cba82b702dcdc5e155893f63610cf388d30cd988d4ca2bf" +checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca" dependencies = [ "aliasable", "ouroboros_macro", - "stable_deref_trait", ] [[package]] name = "ouroboros_macro" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408" +checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -2870,7 +3061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.1", + "parking_lot_core 0.9.3", ] [[package]] @@ -2882,22 +3073,22 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.2.10", + "redox_syscall", "smallvec", "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.10", + "redox_syscall", "smallvec", - "windows-sys 0.32.0", + "windows-sys", ] [[package]] @@ -2915,7 +3106,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -2941,9 +3132,9 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "percentage" @@ -2956,18 +3147,19 @@ dependencies = [ [[package]] name = "pest" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" dependencies = [ "pest", "pest_generator", @@ -2975,63 +3167,73 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1 0.8.2", + "sha1", ] [[package]] name = "petgraph" -version = "0.6.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" dependencies = [ - "fixedbitset", + "fixedbitset 0.2.0", + "indexmap", +] + +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset 0.4.2", "indexmap", ] [[package]] name = "pin-project" -version = "1.0.5" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.5" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -3052,9 +3254,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "plain" @@ -3070,15 +3272,15 @@ checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", "universal-hash", ] [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "pretty-hex" @@ -3088,12 +3290,12 @@ checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "prettyplease" -version = "0.1.9" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18" +checksum = "83fead41e178796ef8274dc612a7d8ce4c7e10ca35cd2c5b5ad24cac63aeb6c0" dependencies = [ - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.46", + "syn 1.0.102", ] [[package]] @@ -3107,10 +3309,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] @@ -3122,9 +3325,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "version_check", ] @@ -3134,8 +3337,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "version_check", ] @@ -3150,13 +3353,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.41" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", + "version_check", + "yansi", +] + +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes", + "prost-derive 0.8.0", +] + [[package]] name = "prost" version = "0.9.0" @@ -3171,10 +3397,28 @@ dependencies = [ name = "prost" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" +checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" +dependencies = [ + "bytes", + "prost-derive 0.11.0", +] + +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" dependencies = [ "bytes", - "prost-derive 0.11.0", + "heck 0.3.3", + "itertools", + "log", + "multimap", + "petgraph 0.5.1", + "prost 0.8.0", + "prost-types 0.8.0", + "tempfile", + "which", ] [[package]] @@ -3189,7 +3433,7 @@ dependencies = [ "lazy_static", "log", "multimap", - "petgraph", + "petgraph 0.6.2", "prost 0.9.0", "prost-types 0.9.0", "regex", @@ -3199,9 +3443,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49d928704208aba2cb1fb022ce1a319bdedcb03caf51ddf82734fa903407762" +checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb" dependencies = [ "bytes", "heck 0.4.0", @@ -3209,7 +3453,7 @@ dependencies = [ "lazy_static", "log", "multimap", - "petgraph", + "petgraph 0.6.2", "prost 0.11.0", "prost-types 0.11.1", "regex", @@ -3217,6 +3461,19 @@ dependencies = [ "which", ] +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + [[package]] name = "prost-derive" version = "0.9.0" @@ -3225,9 +3482,9 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -3238,9 +3495,19 @@ checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes", + "prost 0.8.0", ] [[package]] @@ -3265,9 +3532,9 @@ dependencies = [ [[package]] name = "protobuf-src" -version = "1.0.5+3.19.3" +version = "1.1.0+21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe57f68bf9767f48f8cbcbceb5da21524e2b1330a821c1c2502c447d8043f078" +checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" dependencies = [ "autotools", ] @@ -3278,14 +3545,14 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", ] [[package]] name = "quinn" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21afdc492bf2a8688cb386be6605d1163b6ace89afa5e3b529037d2b4334b860" +checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f" dependencies = [ "bytes", "futures-channel", @@ -3311,7 +3578,7 @@ dependencies = [ "rand 0.8.5", "ring", "rustls 0.20.6", - "rustls-native-certs", + "rustls-native-certs 0.6.2", "rustls-pemfile 0.2.1", "slab", "thiserror", @@ -3345,11 +3612,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.46", ] [[package]] @@ -3358,7 +3625,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14", + "getrandom 0.1.16", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", @@ -3372,8 +3639,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.3", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] @@ -3388,12 +3655,12 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3402,16 +3669,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14", + "getrandom 0.1.16", ] [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.4", + "getrandom 0.2.7", ] [[package]] @@ -3429,7 +3696,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3446,9 +3713,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -3458,39 +3725,34 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fa2d386df8533b02184941c76ae2e0d0c1d053f5d43339169d80f21275fc5e" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.9", + "time 0.3.15", "yasna", ] [[package]] name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" - -[[package]] -name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.4", - "redox_syscall 0.2.10", + "getrandom 0.2.7", + "redox_syscall", + "thiserror", ] [[package]] @@ -3505,7 +3767,7 @@ dependencies = [ "lru", "parking_lot 0.11.2", "smallvec", - "spin 0.9.3", + "spin 0.9.4", ] [[package]] @@ -3527,9 +3789,9 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi 0.3.9", ] @@ -3558,23 +3820,23 @@ dependencies = [ "mime", "native-tls", "once_cell", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project-lite", "rustls 0.20.6", - "rustls-pemfile 1.0.0", + "rustls-pemfile 1.0.1", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.2", - "tokio-util 0.7.1", + "tokio-rustls 0.23.4", + "tokio-util 0.7.2", "tower-service", - "url 2.2.2", + "url 2.3.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.22.5", "winreg", ] @@ -3615,9 +3877,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "rustc-hash" @@ -3645,16 +3907,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.35.9" +version = "0.35.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" +checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -3684,12 +3946,24 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943" +checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" dependencies = [ "openssl-probe", - "rustls-pemfile 0.2.1", + "rustls 0.19.1", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.1", "schannel", "security-framework", ] @@ -3705,9 +3979,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ "base64 0.13.0", ] @@ -3720,9 +3994,9 @@ checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" [[package]] name = "ryu" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "same-file" @@ -3735,12 +4009,12 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi 0.3.9", + "windows-sys", ] [[package]] @@ -3749,6 +4023,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "scroll" version = "0.11.0" @@ -3764,9 +4044,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -3791,9 +4071,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -3820,9 +4100,9 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] @@ -3838,20 +4118,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ "itoa", "ryu", @@ -3882,18 +4162,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha-1" version = "0.9.8" @@ -3904,7 +4172,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -3915,7 +4183,18 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", ] [[package]] @@ -3928,18 +4207,18 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] name = "sha2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -3951,24 +4230,24 @@ dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", "keccak", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] name = "sha3" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaedf34ed289ea47c2b741bb72e5357a209512d67bcd4bda44359e5bf0470f56" +checksum = "e2904bea16a1ae962b483322a1c7b81d976029203aea1f461e51cd7705db7ba9" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", "keccak", ] [[package]] name = "sharded-slab" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] @@ -4000,9 +4279,9 @@ dependencies = [ [[package]] name = "signature" -version = "1.1.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65211b7b6fc3f14ff9fc7a2011a434e3e6880585bd2e9e9396315ae24cbf7852" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] name = "simpl" @@ -4022,15 +4301,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smpl_jwt" @@ -4045,14 +4327,14 @@ dependencies = [ "serde_derive", "serde_json", "simpl", - "time 0.3.9", + "time 0.3.15", ] [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi 0.3.9", @@ -4080,7 +4362,7 @@ dependencies = [ "Inflector", "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", "bv", "lazy_static", "serde", @@ -4147,6 +4429,7 @@ dependencies = [ "futures 0.3.24", "solana-banks-interface", "solana-client", + "solana-gossip", "solana-runtime", "solana-sdk 1.15.0", "solana-send-transaction-service", @@ -4454,7 +4737,7 @@ dependencies = [ name = "solana-bpf-rust-rand" version = "1.15.0" dependencies = [ - "getrandom 0.1.14", + "getrandom 0.1.16", "rand 0.7.3", "solana-program 1.15.0", ] @@ -4502,7 +4785,7 @@ dependencies = [ name = "solana-bpf-rust-secp256k1-recover" version = "1.15.0" dependencies = [ - "libsecp256k1 0.7.0", + "libsecp256k1 0.7.1", "solana-program 1.15.0", ] @@ -4595,7 +4878,7 @@ name = "solana-clap-utils" version = "1.15.0" dependencies = [ "chrono", - "clap 2.33.3", + "clap 2.34.0", "rpassword", "solana-perf", "solana-remote-wallet", @@ -4603,7 +4886,7 @@ dependencies = [ "thiserror", "tiny-bip39", "uriparse", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -4617,7 +4900,7 @@ dependencies = [ "serde_yaml", "solana-clap-utils", "solana-sdk 1.15.0", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -4627,7 +4910,7 @@ dependencies = [ "Inflector", "base64 0.13.0", "chrono", - "clap 2.33.3", + "clap 2.34.0", "console", "humantime", "indicatif", @@ -4685,22 +4968,31 @@ name = "solana-core" version = "1.15.0" dependencies = [ "ahash", + "anchor-lang", "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", + "bytes", "chrono", + "clap 3.2.22", "crossbeam-channel", "dashmap", "eager", "etcd-client", "fs_extra", + "futures 0.3.24", + "futures-util", "histogram", + "indexmap", "itertools", + "jito-protos", "lazy_static", "log", "lru", "min-max-heap", "num_enum", + "prost 0.8.0", + "prost-types 0.8.0", "rand 0.7.3", "rand_chacha 0.2.2", "rayon", @@ -4736,8 +5028,14 @@ dependencies = [ "sysctl", "tempfile", "thiserror", + "tip-distribution", + "tip-payment", "tokio", + "tokio-stream", + "tonic 0.5.2", + "tonic-build 0.5.2", "trees", + "uuid", ] [[package]] @@ -4779,7 +5077,7 @@ version = "1.15.0" dependencies = [ "bincode", "byteorder 1.4.3", - "clap 2.33.3", + "clap 2.34.0", "crossbeam-channel", "log", "serde", @@ -4797,34 +5095,34 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e4e35bc58c465f161bde764ebce41fdfcb503583cf3a77e0211274cc12b22d" +checksum = "3f07a997db3dac7b9da06b007d4a8df6dbd8281182e6ebbbd8a56f935f540b0a" dependencies = [ "ahash", "blake3", "block-buffer 0.9.0", - "bs58", + "bs58 0.4.0", "bv", "byteorder 1.4.3", "cc", "either", - "generic-array 0.14.6", - "getrandom 0.1.14", + "generic-array", + "getrandom 0.1.16", "hashbrown 0.12.3", "im", "lazy_static", "log", "memmap2", "once_cell", - "rand_core 0.6.3", + "rand_core 0.6.4", "rustc_version", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "solana-frozen-abi-macro 1.11.5", + "sha2 0.10.6", + "solana-frozen-abi-macro 1.14.5", "subtle", "thiserror", ] @@ -4836,26 +5134,26 @@ dependencies = [ "ahash", "blake3", "block-buffer 0.9.0", - "bs58", + "bs58 0.4.0", "bv", "byteorder 1.4.3", "cc", "either", - "generic-array 0.14.6", - "getrandom 0.1.14", + "generic-array", + "getrandom 0.1.16", "hashbrown 0.12.3", "im", "lazy_static", "log", "memmap2", "once_cell", - "rand_core 0.6.3", + "rand_core 0.6.4", "rustc_version", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", + "sha2 0.10.6", "solana-frozen-abi-macro 1.15.0", "subtle", "thiserror", @@ -4863,24 +5161,24 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708f837d748e574b1e53b250ab1f4a69ba330bbc10d041d02381165f0f36291a" +checksum = "dcd7d529da0fa5b3b5ca71645122fc94c2aaf867744497969c109e1d4b8ad02d" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustc_version", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] name = "solana-frozen-abi-macro" version = "1.15.0" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustc_version", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] @@ -4906,7 +5204,7 @@ dependencies = [ name = "solana-geyser-plugin-manager" version = "1.15.0" dependencies = [ - "bs58", + "bs58 0.4.0", "crossbeam-channel", "json5", "libloading", @@ -4928,7 +5226,7 @@ version = "1.15.0" dependencies = [ "bincode", "bv", - "clap 2.33.3", + "clap 2.34.0", "crossbeam-channel", "flate2", "indexmap", @@ -4996,7 +5294,7 @@ dependencies = [ "rustc_version", "serde", "serde_bytes", - "sha2 0.10.5", + "sha2 0.10.6", "solana-account-decoder", "solana-bpf-loader-program", "solana-entry", @@ -5026,9 +5324,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ea6fc68d63d33d862d919d4c8ad7f613ec243ccf6762d595c660020b289b57" +checksum = "c690a6ae623bdd2d71229880a9f668ff714b5c6a9bc180a1abef4887da8b6f27" dependencies = [ "env_logger", "lazy_static", @@ -5078,7 +5376,7 @@ name = "solana-net-utils" version = "1.15.0" dependencies = [ "bincode", - "clap 3.1.6", + "clap 3.2.22", "crossbeam-channel", "log", "nix", @@ -5090,7 +5388,7 @@ dependencies = [ "solana-sdk 1.15.0", "solana-version", "tokio", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -5137,9 +5435,9 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd314d85b171bb20ccdcaf07346a9d52a012b10d84f4706f0628813d002fef8" +checksum = "f835be7a20e172209538241cdf46451c08b38eaaca65cf16e65658700c447b17" dependencies = [ "base64 0.13.0", "bincode", @@ -5147,14 +5445,14 @@ dependencies = [ "blake3", "borsh", "borsh-derive", - "bs58", + "bs58 0.4.0", "bv", "bytemuck", "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.4", + "getrandom 0.2.7", "itertools", "js-sys", "lazy_static", @@ -5173,11 +5471,11 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", - "solana-frozen-abi 1.11.5", - "solana-frozen-abi-macro 1.11.5", - "solana-sdk-macro 1.11.5", + "sha2 0.10.6", + "sha3 0.10.5", + "solana-frozen-abi 1.14.5", + "solana-frozen-abi-macro 1.14.5", + "solana-sdk-macro 1.14.5", "thiserror", "tiny-bip39", "wasm-bindgen", @@ -5194,14 +5492,14 @@ dependencies = [ "blake3", "borsh", "borsh-derive", - "bs58", + "bs58 0.4.0", "bv", "bytemuck", "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.4", + "getrandom 0.2.7", "itertools", "js-sys", "lazy_static", @@ -5220,8 +5518,8 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", + "sha2 0.10.6", + "sha3 0.10.5", "solana-frozen-abi 1.15.0", "solana-frozen-abi-macro 1.15.0", "solana-sdk-macro 1.15.0", @@ -5300,7 +5598,7 @@ dependencies = [ "tokio-stream", "tokio-tungstenite", "tungstenite", - "url 2.2.2", + "url 2.3.1", ] [[package]] @@ -5334,7 +5632,7 @@ version = "1.15.0" dependencies = [ "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", "crossbeam-channel", "dashmap", "itertools", @@ -5377,7 +5675,7 @@ dependencies = [ "stream-cancel", "thiserror", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.6.10", ] [[package]] @@ -5387,7 +5685,7 @@ dependencies = [ "async-trait", "base64 0.13.0", "bincode", - "bs58", + "bs58 0.4.0", "indicatif", "log", "reqwest", @@ -5409,7 +5707,7 @@ name = "solana-rpc-client-api" version = "1.15.0" dependencies = [ "base64 0.13.0", - "bs58", + "bs58 0.4.0", "jsonrpc-core", "reqwest", "semver", @@ -5428,7 +5726,7 @@ dependencies = [ name = "solana-rpc-client-nonce-utils" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "solana-clap-utils", "solana-rpc-client", "solana-sdk 1.15.0", @@ -5496,24 +5794,24 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7d954df63b267857e26670e3aacfd8e2943ca703653b0418e5afc85046c2f3" +checksum = "f74e8d699c3a441a5b0cd94c718e75b25c1a4295c2180a714b12fb1bcf66a51e" dependencies = [ "assert_matches", "base64 0.13.0", "bincode", "bitflags", "borsh", - "bs58", + "bs58 0.4.0", "bytemuck", "byteorder 1.4.3", "chrono", "derivation-path", - "digest 0.10.3", + "digest 0.10.5", "ed25519-dalek", "ed25519-dalek-bip32", - "generic-array 0.14.6", + "generic-array", "hmac 0.12.1", "itertools", "js-sys", @@ -5533,13 +5831,13 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", - "solana-frozen-abi 1.11.5", - "solana-frozen-abi-macro 1.11.5", - "solana-logger 1.11.5", - "solana-program 1.11.5", - "solana-sdk-macro 1.11.5", + "sha2 0.10.6", + "sha3 0.10.5", + "solana-frozen-abi 1.14.5", + "solana-frozen-abi-macro 1.14.5", + "solana-logger 1.14.5", + "solana-program 1.14.5", + "solana-sdk-macro 1.14.5", "thiserror", "uriparse", "wasm-bindgen", @@ -5549,20 +5847,21 @@ dependencies = [ name = "solana-sdk" version = "1.15.0" dependencies = [ + "anchor-lang", "assert_matches", "base64 0.13.0", "bincode", "bitflags", "borsh", - "bs58", + "bs58 0.4.0", "bytemuck", "byteorder 1.4.3", "chrono", "derivation-path", - "digest 0.10.3", + "digest 0.10.5", "ed25519-dalek", "ed25519-dalek-bip32", - "generic-array 0.14.6", + "generic-array", "hmac 0.12.1", "itertools", "js-sys", @@ -5582,8 +5881,8 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.5", - "sha3 0.10.4", + "sha2 0.10.6", + "sha3 0.10.5", "solana-frozen-abi 1.15.0", "solana-frozen-abi-macro 1.15.0", "solana-logger 1.15.0", @@ -5591,31 +5890,32 @@ dependencies = [ "solana-sdk-macro 1.15.0", "thiserror", "uriparse", + "uuid", "wasm-bindgen", ] [[package]] name = "solana-sdk-macro" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d9e81bc46edcc517b2df504856d57a5101c7586ec63f3143ae11fbe2eba613" +checksum = "92ffde9b5b7313629780baca10eaffec7421d53be725c76031ca409a5298705c" dependencies = [ - "bs58", - "proc-macro2 1.0.41", - "quote 1.0.18", + "bs58 0.4.0", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustversion", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] name = "solana-sdk-macro" version = "1.15.0" dependencies = [ - "bs58", - "proc-macro2 1.0.41", - "quote 1.0.18", + "bs58 0.4.0", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustversion", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] @@ -5624,6 +5924,7 @@ version = "1.15.0" dependencies = [ "crossbeam-channel", "log", + "solana-gossip", "solana-measure", "solana-metrics", "solana-runtime", @@ -5672,7 +5973,7 @@ dependencies = [ "solana-transaction-status", "thiserror", "tokio", - "tonic 0.8.0", + "tonic 0.8.2", "zstd", ] @@ -5681,14 +5982,14 @@ name = "solana-storage-proto" version = "1.15.0" dependencies = [ "bincode", - "bs58", + "bs58 0.4.0", "prost 0.11.0", "protobuf-src", "serde", "solana-account-decoder", "solana-sdk 1.15.0", "solana-transaction-status", - "tonic-build 0.8.0", + "tonic-build 0.8.2", ] [[package]] @@ -5724,7 +6025,7 @@ dependencies = [ name = "solana-sys-tuner" version = "1.15.0" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "libc", "log", "nix", @@ -5814,7 +6115,7 @@ dependencies = [ "base64 0.13.0", "bincode", "borsh", - "bs58", + "bs58 0.4.0", "lazy_static", "log", "serde", @@ -5835,7 +6136,7 @@ name = "solana-validator" version = "1.15.0" dependencies = [ "chrono", - "clap 2.33.3", + "clap 2.34.0", "console", "core_affinity", "crossbeam-channel", @@ -5884,6 +6185,7 @@ dependencies = [ "solana-vote-program", "symlink", "tikv-jemallocator", + "tonic 0.5.2", ] [[package]] @@ -5925,7 +6227,7 @@ name = "solana-zk-token-proof-program" version = "1.15.0" dependencies = [ "bytemuck", - "getrandom 0.1.14", + "getrandom 0.1.16", "num-derive", "num-traits", "solana-program-runtime", @@ -5935,9 +6237,9 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.11.5" +version = "1.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62415c05a9ebfffaf8befaa61b24492ebf88269cf84cbeba714bac4125ec4ea3" +checksum = "0a23a2c443027e8cc2981131a38928cb37e554970c497b5735e888049cc85d3f" dependencies = [ "aes-gcm-siv", "arrayref", @@ -5947,7 +6249,8 @@ dependencies = [ "byteorder 1.4.3", "cipher 0.4.3", "curve25519-dalek", - "getrandom 0.1.14", + "getrandom 0.1.16", + "itertools", "lazy_static", "merlin", "num-derive", @@ -5956,8 +6259,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.9.1", - "solana-program 1.11.5", - "solana-sdk 1.11.5", + "solana-program 1.14.5", + "solana-sdk 1.14.5", "subtle", "thiserror", "zeroize", @@ -5975,7 +6278,7 @@ dependencies = [ "byteorder 1.4.3", "cipher 0.4.3", "curve25519-dalek", - "getrandom 0.1.14", + "getrandom 0.1.16", "itertools", "lazy_static", "merlin", @@ -6018,9 +6321,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" [[package]] name = "spki" @@ -6042,7 +6345,7 @@ dependencies = [ "borsh", "num-derive", "num-traits", - "solana-program 1.11.5", + "solana-program 1.14.5", "spl-token", "spl-token-2022", "thiserror", @@ -6054,7 +6357,7 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" dependencies = [ - "solana-program 1.11.5", + "solana-program 1.14.5", ] [[package]] @@ -6068,7 +6371,7 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-program 1.11.5", + "solana-program 1.14.5", "thiserror", ] @@ -6083,19 +6386,13 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-program 1.11.5", - "solana-zk-token-sdk 1.11.5", + "solana-program 1.14.5", + "solana-zk-token-sdk 1.14.5", "spl-memo", "spl-token", "thiserror", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_assertions" version = "1.1.0" @@ -6127,24 +6424,24 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.24.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.0", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "rustversion", - "syn 1.0.98", + "syn 1.0.102", ] [[package]] @@ -6172,12 +6469,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.46", + "quote 1.0.21", "unicode-ident", ] @@ -6189,14 +6486,14 @@ checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" [[package]] name = "synstructure" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", - "unicode-xid 0.2.3", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", + "unicode-xid 0.2.4", ] [[package]] @@ -6211,9 +6508,9 @@ dependencies = [ [[package]] name = "sysctl" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1123645dfaf2b5eac6b6c88addafc359c789b8ef2a770ecaef758c1ddf363ea4" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" dependencies = [ "bitflags", "byteorder 1.4.3", @@ -6252,7 +6549,7 @@ dependencies = [ "thiserror", "tokio", "tokio-serde", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "tracing", "tracing-opentelemetry", ] @@ -6263,9 +6560,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -6277,7 +6574,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "libc", - "redox_syscall 0.2.10", + "redox_syscall", "remove_dir_all", "winapi 0.3.9", ] @@ -6293,9 +6590,9 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd2d183bd3fac5f5fe38ddbeb4dc9aec4a39a9d7d59e7491d900302da01cbe1" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" dependencies = [ "libc", "winapi 0.3.9", @@ -6312,28 +6609,28 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -6368,19 +6665,20 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi 0.3.9", ] [[package]] name = "time" -version = "0.3.9" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" dependencies = [ "itoa", "libc", @@ -6415,9 +6713,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -6428,6 +6726,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tip-distribution" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "solana-program 1.15.0", +] + +[[package]] +name = "tip-payment" +version = "0.1.0" +dependencies = [ + "anchor-lang", +] + [[package]] name = "tokio" version = "1.14.1" @@ -6460,13 +6773,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] @@ -6492,9 +6805,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.6", "tokio", @@ -6519,9 +6832,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite", @@ -6538,17 +6851,17 @@ dependencies = [ "log", "rustls 0.20.6", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls 0.23.4", "tungstenite", "webpki 0.22.0", - "webpki-roots", + "webpki-roots 0.22.5", ] [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -6562,9 +6875,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" dependencies = [ "bytes", "futures-core", @@ -6576,13 +6889,47 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796c5e1cd49905e65dd8e700d4cb1dffcbfdb4fc9d017de08c1a537afd83627c" +dependencies = [ + "async-stream", + "async-trait", + "base64 0.13.0", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding 2.2.0", + "pin-project", + "prost 0.8.0", + "prost-derive 0.8.0", + "rustls-native-certs 0.5.0", + "tokio", + "tokio-rustls 0.22.0", + "tokio-stream", + "tokio-util 0.6.10", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", + "webpki-roots 0.21.1", +] + [[package]] name = "tonic" version = "0.6.2" @@ -6600,14 +6947,14 @@ dependencies = [ "http-body", "hyper", "hyper-timeout", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project", "prost 0.9.0", "prost-derive 0.9.0", "tokio", "tokio-rustls 0.22.0", "tokio-stream", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "tower", "tower-layer", "tower-service", @@ -6617,9 +6964,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498f271adc46acce75d66f639e4d35b31b2394c295c82496727dafa16d465dd2" +checksum = "55b9af819e54b8f33d453655bef9b9acc171568fb49523078d0cc4e7484200ec" dependencies = [ "async-stream", "async-trait", @@ -6633,15 +6980,15 @@ dependencies = [ "http-body", "hyper", "hyper-timeout", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project", "prost 0.11.0", "prost-derive 0.11.0", - "rustls-pemfile 1.0.0", + "rustls-pemfile 1.0.1", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls 0.23.4", "tokio-stream", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "tower", "tower-layer", "tower-service", @@ -6649,36 +6996,48 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "tonic-build" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b52d07035516c2b74337d2ac7746075e7dcae7643816c1b12c5ff8a7484c08" +dependencies = [ + "proc-macro2 1.0.46", + "prost-build 0.8.0", + "quote 1.0.21", + "syn 1.0.102", +] + [[package]] name = "tonic-build" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.46", "prost-build 0.9.0", - "quote 1.0.18", - "syn 1.0.98", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "tonic-build" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbcd2800e34e743b9ae795867d5f77b535d3a3be69fd731e39145719752df8c" +checksum = "48c6fd7c2581e36d63388a9e04c350c21beb7a8b059580b2e93993c526899ddc" dependencies = [ "prettyplease", - "proc-macro2 1.0.41", - "prost-build 0.11.0", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "prost-build 0.11.1", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "tower" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", @@ -6688,7 +7047,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "tower-layer", "tower-service", "tracing", @@ -6696,9 +7055,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.2.5" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", "bytes", @@ -6715,21 +7074,21 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", "log", @@ -6740,22 +7099,22 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", ] [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -6771,10 +7130,11 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.17.2" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f9378e96a9361190ae297e7f3a8ff644aacd2897f244b1ff81f381669196fa6" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" dependencies = [ + "once_cell", "opentelemetry", "tracing", "tracing-core", @@ -6783,9 +7143,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.11" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ "sharded-slab", "thread_local", @@ -6800,9 +7160,9 @@ checksum = "0de5f738ceab88e2491a94ddc33c3feeadfa95fedc60363ef110845df12f3878" [[package]] name = "try-lock" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "tungstenite" @@ -6820,10 +7180,10 @@ dependencies = [ "rustls 0.20.6", "sha-1 0.10.0", "thiserror", - "url 2.2.2", + "url 2.3.1", "utf-8", "webpki 0.22.0", - "webpki-roots", + "webpki-roots 0.22.5", ] [[package]] @@ -6834,9 +7194,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicase" @@ -6849,39 +7209,36 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.16" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" @@ -6891,9 +7248,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -6901,7 +7258,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.6", + "generic-array", "subtle", ] @@ -6952,14 +7309,13 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna 0.2.0", - "matches", - "percent-encoding 2.1.0", + "idna 0.3.0", + "percent-encoding 2.2.0", ] [[package]] @@ -6974,9 +7330,19 @@ dependencies = [ [[package]] name = "utf-8" -version = "0.7.5" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +dependencies = [ + "getrandom 0.2.7", + "rand 0.8.5", +] [[package]] name = "valuable" @@ -6998,9 +7364,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" @@ -7010,9 +7376,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi 0.3.9", @@ -7037,15 +7403,21 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.1+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[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.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -7053,24 +7425,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.22" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -7080,38 +7452,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote 1.0.18", + "quote 1.0.21", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.40" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -7139,22 +7511,31 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.1" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki 0.21.4", +] + +[[package]] +name = "webpki-roots" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c475786c6f47219345717a043a37ec04cb4bc185e28853adcc4fa0a947eba630" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki 0.22.0", ] [[package]] name = "which" -version = "4.2.5" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] @@ -7200,86 +7581,43 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", -] - [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" - [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" - [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" - [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" - [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -7310,14 +7648,14 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.9", + "time 0.3.15", ] [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] @@ -7331,13 +7669,19 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "yasna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" dependencies = [ - "time 0.3.9", + "time 0.3.15", ] [[package]] @@ -7351,13 +7695,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.2.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.46", + "quote 1.0.21", + "syn 1.0.102", "synstructure", ] @@ -7372,9 +7716,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "5.0.1+zstd.1.5.2" +version = "5.0.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c12659121420dd6365c5c3de4901f97145b79651fb1d25814020ed2ed0585ae" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" dependencies = [ "libc", "zstd-sys", diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 0a50fb8122..e0695d61ce 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -415,7 +415,7 @@ fn execute_transactions( let batch = bank.prepare_batch_for_tests(txs.clone()); let mut timings = ExecuteTimings::default(); let mut mint_decimals = HashMap::new(); - let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals); + let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals, None); let ( TransactionResults { execution_results, .. @@ -435,7 +435,7 @@ fn execute_transactions( &mut timings, None, ); - let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals); + let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals, None); izip!( txs.iter(), diff --git a/rpc-client-api/src/config.rs b/rpc-client-api/src/config.rs index ee98402a57..05e97b9594 100644 --- a/rpc-client-api/src/config.rs +++ b/rpc-client-api/src/config.rs @@ -46,7 +46,52 @@ pub struct RpcSimulateTransactionConfig { pub min_context_slot: Option, } -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum SimulationSlotConfig { + Commitment(CommitmentConfig), + Slot(Slot), +} + +impl Default for SimulationSlotConfig { + fn default() -> Self { + Self::Commitment(CommitmentConfig { + commitment: CommitmentLevel::Confirmed, + }) + } +} + +#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBundleRequest { + pub encoded_transactions: Vec, +} + +#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcSimulateBundleConfig { + /// Gives the state of accounts pre/post transaction execution. + /// The length of each of these must be equal to the number transactions. + pub pre_execution_accounts_configs: Vec>, + pub post_execution_accounts_configs: Vec>, + + /// Specifies the encoding scheme of the contained transactions. + pub transaction_encoding: Option, + + /// Specifies the bank to run simulation against. + #[serde(flatten)] + pub simulation_bank: Option, + + /// Opt to skip sig-verify for faster performance. + #[serde(default)] + pub skip_sig_verify: bool, + + /// Replace recent blockhash to simulate old transactions without resigning. + #[serde(default)] + pub replace_recent_blockhash: bool, +} + +#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcRequestAirdropConfig { pub recent_blockhash: Option, // base-58 encoded blockhash diff --git a/rpc-client-api/src/request.rs b/rpc-client-api/src/request.rs index 07395d9cae..f0169eed4b 100644 --- a/rpc-client-api/src/request.rs +++ b/rpc-client-api/src/request.rs @@ -112,6 +112,7 @@ pub enum RpcRequest { RequestAirdrop, SendTransaction, SimulateTransaction, + SimulateBundle, SignVote, } @@ -187,6 +188,7 @@ impl fmt::Display for RpcRequest { RpcRequest::RequestAirdrop => "requestAirdrop", RpcRequest::SendTransaction => "sendTransaction", RpcRequest::SimulateTransaction => "simulateTransaction", + RpcRequest::SimulateBundle => "simulateBundle", RpcRequest::SignVote => "signVote", }; @@ -256,6 +258,7 @@ pub enum RpcError { RpcRequestError(String), #[error("RPC response error {code}: {message} {data}")] RpcResponseError { + request_id: u64, code: i64, message: String, data: RpcResponseErrorData, diff --git a/rpc-client-api/src/response.rs b/rpc-client-api/src/response.rs index dfe9272a5c..062f36c49b 100644 --- a/rpc-client-api/src/response.rs +++ b/rpc-client-api/src/response.rs @@ -3,6 +3,7 @@ use { serde::{Deserialize, Deserializer, Serialize, Serializer}, solana_account_decoder::{parse_token::UiTokenAmount, UiAccount}, solana_sdk::{ + bundle::error::BundleExecutionError, clock::{Epoch, Slot, UnixTimestamp}, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, @@ -17,6 +18,7 @@ use { thiserror::Error, }; +pub type BatchRpcResult = client_error::Result>>; pub type RpcResult = client_error::Result>; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -27,6 +29,15 @@ pub struct RpcResponseContext { pub api_version: Option, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BatchRpcResponseContext { + #[serde(skip_serializing_if = "Option::is_none")] + pub slot: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub api_version: Option, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct RpcApiVersion(semver::Version); @@ -73,6 +84,12 @@ impl RpcResponseContext { } } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct BatchResponse { + pub id: u64, + pub result: Response, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Response { pub context: RpcResponseContext, @@ -338,6 +355,24 @@ pub struct RpcIdentity { pub identity: String, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub enum RpcBundleSimulationSummary { + /// error and offending transaction signature + Failed { + error: BundleExecutionError, + tx_signature: String, + }, + Succeeded, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RpcSimulateBundleResult { + pub summary: RpcBundleSimulationSummary, + pub transaction_results: Vec, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RpcVote { @@ -392,6 +427,18 @@ pub struct RpcSignatureConfirmation { pub status: Result<()>, } +// TODO: consolidate with [RpcSimulateTransactionResult] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct RpcSimulateBundleTransactionResult { + pub err: Option, + pub logs: Option>, + pub pre_execution_accounts: Option>, + pub post_execution_accounts: Option>, + pub units_consumed: Option, + pub return_data: Option, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RpcSimulateTransactionResult { diff --git a/rpc-client/src/http_sender.rs b/rpc-client/src/http_sender.rs index 902f86ce63..5c2089cda0 100644 --- a/rpc-client/src/http_sender.rs +++ b/rpc-client/src/http_sender.rs @@ -11,7 +11,7 @@ use { }, solana_rpc_client_api::{ client_error::Result, - custom_error, + custom_error::{self}, error_object::RpcErrorObject, request::{RpcError, RpcRequest, RpcResponseErrorData}, response::RpcSimulateTransactionResult, @@ -72,62 +72,74 @@ impl HttpSender { stats: RwLock::new(RpcTransportStats::default()), } } -} -struct StatsUpdater<'a> { - stats: &'a RwLock, - request_start_time: Instant, - rate_limited_time: Duration, -} + fn check_response(json: &serde_json::Value) -> Result<()> { + if json["error"].is_object() { + return match serde_json::from_value::(json["error"].clone()) { + Ok(rpc_error_object) => { + let data = match rpc_error_object.code { + custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => { + match serde_json::from_value::( + json["error"]["data"].clone(), + ) { + Ok(data) => { + RpcResponseErrorData::SendTransactionPreflightFailure(data) + } + Err(err) => { + debug!( + "Failed to deserialize RpcSimulateTransactionResult: {:?}", + err + ); + RpcResponseErrorData::Empty + } + } + } + custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY => { + match serde_json::from_value::( + json["error"]["data"].clone(), + ) { + Ok(custom_error::NodeUnhealthyErrorData { num_slots_behind }) => { + RpcResponseErrorData::NodeUnhealthy { num_slots_behind } + } + Err(_err) => RpcResponseErrorData::Empty, + } + } + _ => RpcResponseErrorData::Empty, + }; -impl<'a> StatsUpdater<'a> { - fn new(stats: &'a RwLock) -> Self { - Self { - stats, - request_start_time: Instant::now(), - rate_limited_time: Duration::default(), + Err(RpcError::RpcResponseError { + request_id: json["id"].as_u64().unwrap(), + code: rpc_error_object.code, + message: rpc_error_object.message, + data, + } + .into()) + } + Err(err) => Err(RpcError::RpcRequestError(format!( + "Failed to deserialize RPC error response: {} [{}]", + serde_json::to_string(&json["error"]).unwrap(), + err + )) + .into()), + }; } + Ok(()) } - fn add_rate_limited_time(&mut self, duration: Duration) { - self.rate_limited_time += duration; - } -} - -impl<'a> Drop for StatsUpdater<'a> { - fn drop(&mut self) { - let mut stats = self.stats.write().unwrap(); - stats.request_count += 1; - stats.elapsed_time += Instant::now().duration_since(self.request_start_time); - stats.rate_limited_time += self.rate_limited_time; - } -} - -#[async_trait] -impl RpcSender for HttpSender { - fn get_transport_stats(&self) -> RpcTransportStats { - self.stats.read().unwrap().clone() - } - - async fn send( + async fn do_send_with_retry( &self, - request: RpcRequest, - params: serde_json::Value, - ) -> Result { + request: serde_json::Value, + ) -> reqwest::Result { let mut stats_updater = StatsUpdater::new(&self.stats); - - let request_id = self.request_id.fetch_add(1, Ordering::Relaxed); - let request_json = request.build_request_json(request_id, params).to_string(); - let mut too_many_requests_retries = 5; loop { let response = { let client = self.client.clone(); - let request_json = request_json.clone(); + let request = request.to_string(); client .post(&self.url) .header(CONTENT_TYPE, "application/json") - .body(request_json) + .body(request) .send() .await }?; @@ -155,54 +167,82 @@ impl RpcSender for HttpSender { sleep(duration).await; stats_updater.add_rate_limited_time(duration); + continue; } - return Err(response.error_for_status().unwrap_err().into()); - } - let mut json = response.json::().await?; - if json["error"].is_object() { - return match serde_json::from_value::(json["error"].clone()) { - Ok(rpc_error_object) => { - let data = match rpc_error_object.code { - custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => { - match serde_json::from_value::(json["error"]["data"].clone()) { - Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data), - Err(err) => { - debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err); - RpcResponseErrorData::Empty - } - } - }, - custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY => { - match serde_json::from_value::(json["error"]["data"].clone()) { - Ok(custom_error::NodeUnhealthyErrorData {num_slots_behind}) => RpcResponseErrorData::NodeUnhealthy {num_slots_behind}, - Err(_err) => { - RpcResponseErrorData::Empty - } - } - }, - _ => RpcResponseErrorData::Empty - }; - - Err(RpcError::RpcResponseError { - code: rpc_error_object.code, - message: rpc_error_object.message, - data, - } - .into()) - } - Err(err) => Err(RpcError::RpcRequestError(format!( - "Failed to deserialize RPC error response: {} [{}]", - serde_json::to_string(&json["error"]).unwrap(), - err - )) - .into()), - }; + return Err(response.error_for_status().unwrap_err()); } - return Ok(json["result"].take()); + + return response.json::().await; } } +} + +struct StatsUpdater<'a> { + stats: &'a RwLock, + request_start_time: Instant, + rate_limited_time: Duration, +} + +impl<'a> StatsUpdater<'a> { + fn new(stats: &'a RwLock) -> Self { + Self { + stats, + request_start_time: Instant::now(), + rate_limited_time: Duration::default(), + } + } + + fn add_rate_limited_time(&mut self, duration: Duration) { + self.rate_limited_time += duration; + } +} + +impl<'a> Drop for StatsUpdater<'a> { + fn drop(&mut self) { + let mut stats = self.stats.write().unwrap(); + stats.request_count += 1; + stats.elapsed_time += Instant::now().duration_since(self.request_start_time); + stats.rate_limited_time += self.rate_limited_time; + } +} + +#[async_trait] +impl RpcSender for HttpSender { + async fn send( + &self, + request: RpcRequest, + params: serde_json::Value, + ) -> Result { + let request_id = self.request_id.fetch_add(1, Ordering::Relaxed); + let request = request.build_request_json(request_id, params); + let mut resp = self.do_send_with_retry(request).await?; + Self::check_response(&resp)?; + + Ok(resp["result"].take()) + } + + async fn send_batch( + &self, + requests_and_params: Vec<(RpcRequest, serde_json::Value)>, + ) -> Result { + let mut batch_request = vec![]; + for (rpc_req, params) in requests_and_params { + let request_id = self.request_id.fetch_add(1, Ordering::Relaxed); + batch_request.push(rpc_req.build_request_json(request_id, params)); + } + + let resp = self + .do_send_with_retry(serde_json::Value::Array(batch_request)) + .await?; + + Ok(resp) + } + + fn get_transport_stats(&self) -> RpcTransportStats { + self.stats.read().unwrap().clone() + } fn url(&self) -> String { self.url.clone() diff --git a/rpc-client/src/mock_sender.rs b/rpc-client/src/mock_sender.rs index 346c75278e..b254e06b06 100644 --- a/rpc-client/src/mock_sender.rs +++ b/rpc-client/src/mock_sender.rs @@ -480,4 +480,11 @@ impl RpcSender for MockSender { fn url(&self) -> String { format!("MockSender: {}", self.url) } + + async fn send_batch( + &self, + _requests_and_params: Vec<(RpcRequest, serde_json::Value)>, + ) -> Result { + todo!() + } } diff --git a/rpc-client/src/nonblocking/rpc_client.rs b/rpc-client/src/nonblocking/rpc_client.rs index 4ab7cf1d66..7d74ebda48 100644 --- a/rpc-client/src/nonblocking/rpc_client.rs +++ b/rpc-client/src/nonblocking/rpc_client.rs @@ -42,6 +42,7 @@ use { }, solana_sdk::{ account::Account, + bundle::VersionedBundle, clock::{Epoch, Slot, UnixTimestamp, DEFAULT_MS_PER_SLOT}, commitment_config::{CommitmentConfig, CommitmentLevel}, epoch_info::EpochInfo, @@ -50,7 +51,7 @@ use { hash::Hash, pubkey::Pubkey, signature::Signature, - transaction, + transaction::{self, VersionedTransaction}, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -963,6 +964,7 @@ impl RpcClient { code, message, data, + .. }) = &err.kind { debug!("{} {}", code, message); @@ -1406,6 +1408,111 @@ impl RpcClient { .await } + pub async fn batch_simulate_bundle( + &self, + bundles: Vec, + ) -> BatchRpcResult { + let configs = bundles + .iter() + .map(|b| RpcSimulateBundleConfig { + simulation_bank: Some(SimulationSlotConfig::Commitment(self.commitment())), + pre_execution_accounts_configs: vec![None; b.transactions.len()], + post_execution_accounts_configs: vec![None; b.transactions.len()], + ..RpcSimulateBundleConfig::default() + }) + .collect::>(); + + self.batch_simulate_bundle_with_config(bundles.into_iter().zip(configs).collect()) + .await + } + + pub async fn batch_simulate_bundle_with_config( + &self, + bundles_and_configs: Vec<(VersionedBundle, RpcSimulateBundleConfig)>, + ) -> BatchRpcResult { + let mut params = vec![]; + for (bundle, config) in bundles_and_configs { + let transaction_encoding = if let Some(encoding) = config.transaction_encoding { + encoding + } else { + self.default_cluster_transaction_encoding().await? + }; + + let simulation_bank = config.simulation_bank.unwrap_or_default(); + + let config = RpcSimulateBundleConfig { + transaction_encoding: Some(transaction_encoding), + simulation_bank: Some(simulation_bank), + ..config + }; + + let encoded_transactions = bundle + .transactions + .iter() + .map(|tx| serialize_and_encode::(tx, transaction_encoding)) + .collect::, ClientError>>()?; + let rpc_bundle_request = RpcBundleRequest { + encoded_transactions, + }; + + params.push(json!([rpc_bundle_request, config])); + } + + let requests_and_params = vec![RpcRequest::SimulateBundle; params.len()] + .into_iter() + .zip(params) + .collect(); + self.send_batch(requests_and_params).await + } + + pub async fn simulate_bundle( + &self, + bundle: &VersionedBundle, + ) -> RpcResult { + self.simulate_bundle_with_config( + bundle, + RpcSimulateBundleConfig { + simulation_bank: Some(SimulationSlotConfig::Commitment(self.commitment())), + ..RpcSimulateBundleConfig::default() + }, + ) + .await + } + + pub async fn simulate_bundle_with_config( + &self, + bundle: &VersionedBundle, + config: RpcSimulateBundleConfig, + ) -> RpcResult { + let transaction_encoding = if let Some(enc) = config.transaction_encoding { + enc + } else { + self.default_cluster_transaction_encoding().await? + }; + let simulation_bank = Some(config.simulation_bank.unwrap_or_default()); + + let encoded_transactions = bundle + .transactions + .iter() + .map(|tx| serialize_and_encode::(tx, transaction_encoding)) + .collect::>>()?; + let rpc_bundle_request = RpcBundleRequest { + encoded_transactions, + }; + + let config = RpcSimulateBundleConfig { + transaction_encoding: Some(transaction_encoding), + simulation_bank, + ..config + }; + + self.send( + RpcRequest::SimulateBundle, + json!([rpc_bundle_request, config]), + ) + .await + } + /// Returns the highest slot information that the node has snapshots for. /// /// This will find the highest full snapshot slot, and the highest incremental snapshot slot @@ -5331,6 +5438,21 @@ impl RpcClient { .map_err(|err| ClientError::new_with_request(err.into(), request)) } + pub async fn send_batch( + &self, + requests_and_params: Vec<(RpcRequest, Value)>, + ) -> ClientResult + where + T: serde::de::DeserializeOwned, + { + let response = self.sender.send_batch(requests_and_params).await?; + + serde_json::from_value(response).map_err(|err| ClientError { + request: None, + kind: err.into(), + }) + } + pub fn get_transport_stats(&self) -> RpcTransportStats { self.sender.get_transport_stats() } diff --git a/rpc-client/src/rpc_client.rs b/rpc-client/src/rpc_client.rs index 372839d4a4..fae03de604 100644 --- a/rpc-client/src/rpc_client.rs +++ b/rpc-client/src/rpc_client.rs @@ -35,6 +35,7 @@ use { }, solana_sdk::{ account::Account, + bundle::VersionedBundle, clock::{Epoch, Slot, UnixTimestamp}, commitment_config::CommitmentConfig, epoch_info::EpochInfo, @@ -1150,6 +1151,34 @@ impl RpcClient { ) } + pub fn batch_simulate_bundle( + &self, + bundles: Vec, + ) -> BatchRpcResult { + self.invoke(self.rpc_client.batch_simulate_bundle(bundles)) + } + + pub fn batch_simulate_bundle_with_config( + &self, + bundles_and_configs: Vec<(VersionedBundle, RpcSimulateBundleConfig)>, + ) -> BatchRpcResult { + self.invoke( + (self.rpc_client.as_ref()).batch_simulate_bundle_with_config(bundles_and_configs), + ) + } + + pub fn simulate_bundle(&self, bundle: &VersionedBundle) -> RpcResult { + self.invoke((self.rpc_client.as_ref()).simulate_bundle(bundle)) + } + + pub fn simulate_bundle_with_config( + &self, + bundle: &VersionedBundle, + config: RpcSimulateBundleConfig, + ) -> RpcResult { + self.invoke((self.rpc_client.as_ref()).simulate_bundle_with_config(bundle, config)) + } + /// Returns the highest slot information that the node has snapshots for. /// /// This will find the highest full snapshot slot, and the highest incremental snapshot slot diff --git a/rpc-client/src/rpc_sender.rs b/rpc-client/src/rpc_sender.rs index 948ac45a46..6a357b4e6b 100644 --- a/rpc-client/src/rpc_sender.rs +++ b/rpc-client/src/rpc_sender.rs @@ -31,6 +31,10 @@ pub trait RpcSender { request: RpcRequest, params: serde_json::Value, ) -> Result; + async fn send_batch( + &self, + requests_and_params: Vec<(RpcRequest, serde_json::Value)>, + ) -> Result; fn get_transport_stats(&self) -> RpcTransportStats; fn url(&self) -> String; } diff --git a/rpc-test/tests/rpc.rs b/rpc-test/tests/rpc.rs index d5eff757eb..615d71af8c 100644 --- a/rpc-test/tests/rpc.rs +++ b/rpc-test/tests/rpc.rs @@ -240,6 +240,7 @@ fn test_rpc_slot_updates() { } #[test] +#[ignore] // TODO (LB): this test is flaky in master fn test_rpc_subscriptions() { solana_logger::setup(); diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index ad79a697b5..c7033b24bf 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -230,6 +230,13 @@ impl JsonRpcRequestProcessor { Ok(bank) } + fn bank_from_slot(&self, slot: Slot) -> Option> { + debug!("Slot: {:?}", slot); + + let r_bank_forks = self.bank_forks.read().unwrap(); + r_bank_forks.get(slot) + } + #[allow(deprecated)] fn bank(&self, commitment: Option) -> Arc { debug!("RPC commitment_config: {:?}", commitment); @@ -359,10 +366,9 @@ impl JsonRpcRequestProcessor { Arc::new(Keypair::new()), socket_addr_space, )); - let tpu_address = cluster_info.my_contact_info().tpu; let (sender, receiver) = unbounded(); SendTransactionService::new::( - tpu_address, + cluster_info.clone(), &bank_forks, None, receiver, @@ -2678,13 +2684,16 @@ pub mod rpc_minimal { }) .unwrap(); - let full_snapshot_slot = - snapshot_utils::get_highest_full_snapshot_archive_slot(&full_snapshot_archives_dir) - .ok_or(RpcCustomError::NoSnapshot)?; + let full_snapshot_slot = snapshot_utils::get_highest_full_snapshot_archive_slot( + &full_snapshot_archives_dir, + None, + ) + .ok_or(RpcCustomError::NoSnapshot)?; let incremental_snapshot_slot = snapshot_utils::get_highest_incremental_snapshot_archive_slot( &incremental_snapshot_archives_dir, full_snapshot_slot, + None, ); Ok(RpcSnapshotSlotInfo { @@ -3275,13 +3284,166 @@ pub mod rpc_accounts { } } +pub mod utils { + use { + crate::rpc::{encode_account, sanitize_transaction, verify_pubkey}, + jsonrpc_core::Error, + solana_account_decoder::{UiAccount, UiAccountEncoding}, + solana_rpc_client_api::{ + config::{RpcSimulateBundleConfig, RpcSimulateTransactionAccountsConfig}, + response::{ + RpcBundleSimulationSummary, RpcSimulateBundleResult, + RpcSimulateBundleTransactionResult, + }, + }, + solana_runtime::bank::{ + AccountData, Bank, BundleSimulationResult, BundleSimulationSummary, + }, + solana_sdk::{ + pubkey::Pubkey, + transaction::{SanitizedTransaction, VersionedTransaction}, + }, + }; + + pub type BundleSimulationParams = ( + Vec, + Vec>>, + Vec>>, + ); + + fn try_build_pubkeys_from_config( + maybe_config: &Option, + sanitized_tx: &SanitizedTransaction, + ) -> Result>, Error> { + if let Some(config) = maybe_config { + if config.addresses.len() > sanitized_tx.message().account_keys().len() { + return Err(Error::invalid_params( + "too many pre execution addresses requested", + )); + } + + Ok(Some( + config + .addresses + .iter() + .map(|address_string| verify_pubkey(address_string)) + .collect::, Error>>()?, + )) + } else { + Ok(None) + } + } + + pub fn build_simulate_bundle_params( + txs_and_configs: Vec<( + VersionedTransaction, + Option, + Option, + )>, + bank: &Bank, + ) -> Result { + let mut sanitized_txs = Vec::with_capacity(txs_and_configs.len()); + let mut pre_accounts = Vec::with_capacity(txs_and_configs.len()); + let mut post_accounts = Vec::with_capacity(txs_and_configs.len()); + + for (tx, pre_cfg, post_cfg) in txs_and_configs { + let sanitized_tx = sanitize_transaction(tx, bank)?; + pre_accounts.push(try_build_pubkeys_from_config(&pre_cfg, &sanitized_tx)?); + post_accounts.push(try_build_pubkeys_from_config(&post_cfg, &sanitized_tx)?); + sanitized_txs.push(sanitized_tx); + } + + Ok((sanitized_txs, pre_accounts, post_accounts)) + } + + fn try_encode_accounts( + accounts: Option>, + encoding: Option, + ) -> Result>, Error> { + if let Some(accounts) = accounts { + let encoding = encoding.unwrap_or(UiAccountEncoding::Base64); + Ok(Some( + accounts + .iter() + .map(|a| encode_account(&a.data, &a.pubkey, encoding, None)) + .collect::, Error>>()?, + )) + } else { + Ok(None) + } + } + + /// create a [RpcSimulateBundleResult] from a given bank [BundleSimulationResult] + pub fn rpc_bundle_result_from_bank_result( + bank_result: BundleSimulationResult, + rpc_config: RpcSimulateBundleConfig, + ) -> Result { + let BundleSimulationResult { + ref summary, + ref transaction_results, + } = bank_result; + + let summary = match summary.clone() { + BundleSimulationSummary::Failed { + error, + tx_signature, + } => RpcBundleSimulationSummary::Failed { + error, + tx_signature: tx_signature.to_string(), + }, + BundleSimulationSummary::Succeeded => RpcBundleSimulationSummary::Succeeded, + }; + + let mut transaction_results = Vec::with_capacity(transaction_results.len()); + for (i, res) in bank_result.transaction_results.into_iter().enumerate() { + let logs = if res.logs.is_empty() { + None + } else { + Some(res.logs) + }; + + transaction_results.push(RpcSimulateBundleTransactionResult { + err: res.result.err(), + logs, + pre_execution_accounts: try_encode_accounts( + res.pre_execution_accounts, + rpc_config + .pre_execution_accounts_configs + .get(i) + .cloned() + .unwrap_or_default() + .and_then(|c| c.encoding), + )?, + post_execution_accounts: try_encode_accounts( + res.post_execution_accounts, + rpc_config + .post_execution_accounts_configs + .get(i) + .cloned() + .unwrap_or_default() + .and_then(|c| c.encoding), + )?, + units_consumed: Some(res.units_consumed), + return_data: res.return_data.map(|d| d.into()), + }); + } + + Ok(RpcSimulateBundleResult { + summary, + transaction_results, + }) + } +} // Full RPC interface that an API node is expected to provide // (rpc_minimal should also be provided by an API node) pub mod rpc_full { use { super::*, + crate::rpc::utils::{build_simulate_bundle_params, rpc_bundle_result_from_bank_result}, + itertools::izip, solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage}, }; + #[rpc] pub trait Full { type Metadata; @@ -3343,6 +3505,14 @@ pub mod rpc_full { config: Option, ) -> Result>; + #[rpc(meta, name = "simulateBundle")] + fn simulate_bundle( + &self, + meta: Self::Metadata, + rpc_bundle_request: RpcBundleRequest, + config: Option, + ) -> Result>; + #[rpc(meta, name = "minimumLedgerSlot")] fn minimum_ledger_slot(&self, meta: Self::Metadata) -> Result; @@ -3642,7 +3812,6 @@ pub mod rpc_full { commitment: preflight_commitment, min_context_slot, })?; - let transaction = sanitize_transaction(unsanitized_tx, preflight_bank)?; let signature = *transaction.signature(); @@ -3831,6 +4000,107 @@ pub mod rpc_full { )) } + fn simulate_bundle( + &self, + meta: Self::Metadata, + rpc_bundle_request: RpcBundleRequest, + config: Option, + ) -> Result> { + debug!("simulate_bundle rpc request received"); + + let config = config.unwrap_or_else(|| RpcSimulateBundleConfig { + pre_execution_accounts_configs: vec![ + None; + rpc_bundle_request.encoded_transactions.len() + ], + post_execution_accounts_configs: vec![ + None; + rpc_bundle_request.encoded_transactions.len() + ], + ..RpcSimulateBundleConfig::default() + }); + + // Run some request validations + if !(config.pre_execution_accounts_configs.len() + == rpc_bundle_request.encoded_transactions.len() + && config.post_execution_accounts_configs.len() + == rpc_bundle_request.encoded_transactions.len()) + { + return Err(Error::invalid_params( + "pre/post_execution_accounts_configs must be equal in length to the number of transactions", + )); + } + + let bank = match config.simulation_bank.unwrap_or_default() { + SimulationSlotConfig::Commitment(commitment) => Ok(meta.bank(Some(commitment))), + SimulationSlotConfig::Slot(slot) => meta.bank_from_slot(slot).ok_or_else(|| { + Error::invalid_params(format!("bank not found for the provided slot: {}", slot)) + }), + }?; + + // TODO: Come back to this and allow unfrozen bank as long as the parent is frozen. + if !bank.is_frozen() { + return Err(Error::invalid_params(format!( + "bank at slot {} is not frozen", + bank.slot() + ))); + } + + let tx_encoding = config + .transaction_encoding + .unwrap_or(UiTransactionEncoding::Base64); + let binary_encoding = tx_encoding.into_binary_encoding().ok_or_else(|| { + Error::invalid_params(format!( + "Unsupported encoding: {}. Supported encodings are: base58 & base64", + tx_encoding + )) + })?; + let mut decoded_transactions = rpc_bundle_request + .encoded_transactions + .into_iter() + .map(|encoded_tx| { + decode_and_deserialize::(encoded_tx, binary_encoding) + .map(|de| de.1) + }) + .collect::>>()?; + + if config.replace_recent_blockhash { + if !config.skip_sig_verify { + return Err(Error::invalid_params( + "sigVerify may not be used with replaceRecentBlockhash", + )); + } + decoded_transactions.iter_mut().for_each(|tx| { + tx.message.set_recent_blockhash(bank.last_blockhash()); + }); + } + + let zipped = izip!( + decoded_transactions, + config.pre_execution_accounts_configs.clone(), + config.post_execution_accounts_configs.clone(), + ); + let (sanitized_txs, pre_execution_pks, post_execution_pks) = + build_simulate_bundle_params(zipped.collect(), &*bank)?; + + if !config.skip_sig_verify { + for tx in &sanitized_txs { + verify_transaction(tx, &bank.feature_set)?; + } + } + + let bank_result = bank + .simulate_bundle(sanitized_txs, pre_execution_pks, post_execution_pks) + .map_err(|e| { + error!("bank error {}", e); + Error::internal_error() + })?; + + let rpc_bundle_result = rpc_bundle_result_from_bank_result(bank_result, config)?; + + Ok(new_response(&*bank, rpc_bundle_result)) + } + fn minimum_ledger_slot(&self, meta: Self::Metadata) -> Result { debug!("minimum_ledger_slot rpc request received"); meta.minimum_ledger_slot() @@ -4134,6 +4404,7 @@ pub mod rpc_deprecated_v1_9 { .and_then(|snapshot_config| { snapshot_utils::get_highest_full_snapshot_archive_slot( &snapshot_config.full_snapshot_archives_dir, + None, ) }) .ok_or_else(|| RpcCustomError::NoSnapshot.into()) @@ -5779,6 +6050,145 @@ pub mod tests { assert_eq!(result.len(), 0); } + #[test] + fn test_rpc_simulate_bundle_happy_path() { + // 1. setup + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + + let recent_blockhash = bank.confirmed_last_blockhash(); + let RpcHandler { + ref meta, ref io, .. + } = rpc; + + let data_len = 100; + let lamports = bank.get_minimum_balance_for_rent_exemption(data_len); + let leader_pubkey = solana_sdk::pubkey::new_rand(); + let leader_account_data = AccountSharedData::new(lamports, data_len, &system_program::id()); + bank.store_account(&leader_pubkey, &leader_account_data); + bank.freeze(); + + // 2. build bundle + + // let's pretend the RPC keypair is a searcher + let searcher_keypair = rpc.mint_keypair; + + // create tip tx + let tip_amount = 10000; + let tip_tx = VersionedTransaction::from(system_transaction::transfer( + &searcher_keypair, + &leader_pubkey, + tip_amount, + recent_blockhash, + )); + + // some random mev tx + let mev_amount = 20000; + let goku_pubkey = solana_sdk::pubkey::new_rand(); + let mev_tx = VersionedTransaction::from(system_transaction::transfer( + &searcher_keypair, + &goku_pubkey, + mev_amount, + recent_blockhash, + )); + + let encoded_mev_tx = base64::encode(serialize(&mev_tx).unwrap()); + let encoded_tip_tx = base64::encode(serialize(&tip_tx).unwrap()); + + let b64_data = base64::encode(leader_account_data.data()); + + // 3. test and assert + let skip_sig_verify = true; + let replace_recent_blockhash = false; + let expected_response = json!({ + "jsonrpc": "2.0", + "result": { + "context": {"slot": bank.slot(), "apiVersion": RpcApiVersion::default()}, + "value":{ + "summary": "succeeded", + "transactionResults": [ + { + "err": null, + "logs": ["Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 success"], + "returnData": null, + "unitsConsumed": 0, + "postExecutionAccounts": [], + "preExecutionAccounts": [ + { + "data": [b64_data, "base64"], + "executable": false, + "lamports": leader_account_data.lamports(), + "owner": "11111111111111111111111111111111", + "rentEpoch": 0, + } + ], + }, + { + "err": null, + "logs": ["Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 success"], + "returnData": null, + "unitsConsumed": 0, + "preExecutionAccounts": [], + "postExecutionAccounts": [ + { + "data": [b64_data, "base64"], + "executable": false, + "lamports": leader_account_data.lamports() + tip_amount, + "owner": "11111111111111111111111111111111", + "rentEpoch": 0, + } + ] + }, + ], + } + }, + "id": 1, + }); + + let request = format!( + r#"{{"jsonrpc":"2.0", + "id":1, + "method":"simulateBundle", + "params":[ + {{ + "encodedTransactions": ["{}", "{}"] + }}, + {{ + "skipSigVerify": {}, + "replaceRecentBlockhash": {}, + "slot": {}, + "preExecutionAccountsConfigs": [ + {{ "encoding": "base64", "addresses": ["{}"] }}, + {{ "encoding": "base64", "addresses": [] }} + ], + "postExecutionAccountsConfigs": [ + {{ "encoding": "base64", "addresses": [] }}, + {{ "encoding": "base64", "addresses": ["{}"] }} + ] + }} + ] + }}"#, + encoded_mev_tx, + encoded_tip_tx, + skip_sig_verify, + replace_recent_blockhash, + bank.slot(), + leader_pubkey, + leader_pubkey, + ); + + let actual_response = io + .handle_request_sync(&request, meta.clone()) + .expect("response"); + + let expected_response = serde_json::from_value::(expected_response) + .expect("expected_response deserialization"); + let actual_response = serde_json::from_str::(&actual_response) + .expect("actual_response deserialization"); + + assert_eq!(expected_response, actual_response); + } + #[test] fn test_rpc_simulate_transaction() { let rpc = RpcHandler::start(); @@ -6363,7 +6773,6 @@ pub mod tests { Arc::new(Keypair::new()), SocketAddrSpace::Unspecified, )); - let tpu_address = cluster_info.my_contact_info().tpu; let (meta, receiver) = JsonRpcRequestProcessor::new( JsonRpcConfig::default(), None, @@ -6372,7 +6781,7 @@ pub mod tests { blockstore, validator_exit, health.clone(), - cluster_info, + cluster_info.clone(), Hash::default(), None, OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -6384,7 +6793,7 @@ pub mod tests { ); let connection_cache = Arc::new(ConnectionCache::default()); SendTransactionService::new::( - tpu_address, + cluster_info, &bank_forks, None, receiver, @@ -6634,7 +7043,6 @@ pub mod tests { Arc::new(Keypair::new()), SocketAddrSpace::Unspecified, )); - let tpu_address = cluster_info.my_contact_info().tpu; let (request_processor, receiver) = JsonRpcRequestProcessor::new( JsonRpcConfig::default(), None, @@ -6643,7 +7051,7 @@ pub mod tests { blockstore, validator_exit, RpcHealth::stub(), - cluster_info, + cluster_info.clone(), Hash::default(), None, OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -6655,7 +7063,7 @@ pub mod tests { ); let connection_cache = Arc::new(ConnectionCache::default()); SendTransactionService::new::( - tpu_address, + cluster_info, &bank_forks, None, receiver, diff --git a/rpc/src/rpc_service.rs b/rpc/src/rpc_service.rs index 823818b25b..be76e46cda 100644 --- a/rpc/src/rpc_service.rs +++ b/rpc/src/rpc_service.rs @@ -255,6 +255,7 @@ impl RequestMiddleware for RpcRequestMiddleware { let full_snapshot_archive_info = snapshot_utils::get_highest_full_snapshot_archive_info( &snapshot_config.full_snapshot_archives_dir, + None, ); let snapshot_archive_info = if let Some(full_snapshot_archive_info) = full_snapshot_archive_info { @@ -264,6 +265,7 @@ impl RequestMiddleware for RpcRequestMiddleware { snapshot_utils::get_highest_incremental_snapshot_archive_info( &snapshot_config.incremental_snapshot_archives_dir, full_snapshot_archive_info.slot(), + None, ) .map(|incremental_snapshot_archive_info| { incremental_snapshot_archive_info @@ -376,8 +378,6 @@ impl JsonRpcService { LARGEST_ACCOUNTS_CACHE_DURATION, ))); - let tpu_address = cluster_info.my_contact_info().tpu; - // sadly, some parts of our current rpc implemention block the jsonrpc's // _socket-listening_ event loop for too long, due to (blocking) long IO or intesive CPU, // causing no further processing of incoming requests and ultimatily innocent clients timing-out. @@ -472,7 +472,7 @@ impl JsonRpcService { let leader_info = poh_recorder.map(|recorder| ClusterTpuInfo::new(cluster_info.clone(), recorder)); let _send_transaction_service = Arc::new(SendTransactionService::new_with_config( - tpu_address, + cluster_info, &bank_forks, leader_info, receiver, diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 64fcdc4f5f..03b0324745 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -1053,15 +1053,21 @@ impl Accounts { account_locks: &mut AccountLocks, writable_keys: Vec<&Pubkey>, readonly_keys: Vec<&Pubkey>, + additional_read_locks: &HashSet, + additional_write_locks: &HashSet, ) -> Result<()> { for k in writable_keys.iter() { - if account_locks.is_locked_write(k) || account_locks.is_locked_readonly(k) { + if account_locks.is_locked_write(k) + || account_locks.is_locked_readonly(k) + || additional_write_locks.contains(k) + || additional_read_locks.contains(k) + { debug!("Writable account in use: {:?}", k); return Err(TransactionError::AccountInUse); } } for k in readonly_keys.iter() { - if account_locks.is_locked_write(k) { + if account_locks.is_locked_write(k) || additional_write_locks.contains(k) { debug!("Read-only account in use: {:?}", k); return Err(TransactionError::AccountInUse); } @@ -1123,7 +1129,23 @@ impl Accounts { let tx_account_locks_results: Vec> = txs .map(|tx| tx.get_account_locks(tx_account_lock_limit)) .collect(); - self.lock_accounts_inner(tx_account_locks_results) + self.lock_accounts_inner( + tx_account_locks_results, + &HashSet::default(), + &HashSet::default(), + ) + } + + pub fn lock_accounts_sequential_with_results<'a>( + &self, + txs: impl Iterator, + tx_account_lock_limit: usize, + account_locks_override: Option>, + ) -> Vec> { + let tx_account_locks_results: Vec> = txs + .map(|tx| tx.get_account_locks(tx_account_lock_limit)) + .collect(); + self.lock_accounts_sequential_inner(tx_account_locks_results, account_locks_override) } #[must_use] @@ -1133,6 +1155,8 @@ impl Accounts { txs: impl Iterator, results: impl Iterator>, tx_account_lock_limit: usize, + additional_read_locks: &HashSet, + additional_write_locks: &HashSet, ) -> Vec> { let tx_account_locks_results: Vec> = txs .zip(results) @@ -1141,13 +1165,19 @@ impl Accounts { Err(err) => Err(err.clone()), }) .collect(); - self.lock_accounts_inner(tx_account_locks_results) + self.lock_accounts_inner( + tx_account_locks_results, + additional_read_locks, + additional_write_locks, + ) } #[must_use] fn lock_accounts_inner( &self, tx_account_locks_results: Vec>, + additional_read_locks: &HashSet, + additional_write_locks: &HashSet, ) -> Vec> { let account_locks = &mut self.account_locks.lock().unwrap(); tx_account_locks_results @@ -1157,12 +1187,51 @@ impl Accounts { account_locks, tx_account_locks.writable, tx_account_locks.readonly, + additional_read_locks, + additional_write_locks, ), Err(err) => Err(err), }) .collect() } + #[must_use] + fn lock_accounts_sequential_inner( + &self, + tx_account_locks_results: Vec>, + account_locks_override: Option>, + ) -> Vec> { + let mut l_account_locks = if let Some(ref account_locks) = account_locks_override { + account_locks.lock().unwrap() + } else { + self.account_locks.lock().unwrap() + }; + + let mut account_in_use_set = false; + tx_account_locks_results + .into_iter() + .map(|tx_account_locks_result| match tx_account_locks_result { + Ok(tx_account_locks) => match account_in_use_set { + true => Err(TransactionError::BundleNotContinuous), + false => { + let locked = self.lock_account( + &mut l_account_locks, + tx_account_locks.writable, + tx_account_locks.readonly, + &HashSet::default(), + &HashSet::default(), + ); + if matches!(locked, Err(TransactionError::AccountInUse)) { + account_in_use_set = true; + } + locked + } + }, + Err(err) => Err(err), + }) + .collect() + } + /// Once accounts are unlocked, new transactions that modify that state can enter the pipeline #[allow(clippy::needless_collect)] pub fn unlock_accounts<'a>( @@ -1175,6 +1244,7 @@ impl Accounts { .filter_map(|(tx, res)| match res { Err(TransactionError::AccountLoadedTwice) | Err(TransactionError::AccountInUse) + | Err(TransactionError::BundleNotContinuous) | Err(TransactionError::SanitizeFailure) | Err(TransactionError::TooManyAccountLocks) | Err(TransactionError::WouldExceedMaxBlockCostLimit) @@ -1206,7 +1276,7 @@ impl Accounts { lamports_per_signature: u64, preserve_rent_epoch_for_rent_exempt_accounts: bool, ) { - let (accounts_to_store, txn_signatures) = self.collect_accounts_to_store( + let (accounts_to_store, txn_signatures) = Self::collect_accounts_to_store( txs, res, loaded, @@ -1232,8 +1302,7 @@ impl Accounts { } #[allow(clippy::too_many_arguments)] - fn collect_accounts_to_store<'a>( - &self, + pub fn collect_accounts_to_store<'a>( txs: &'a [SanitizedTransaction], execution_results: &'a [TransactionExecutionResult], load_results: &'a mut [TransactionLoadResult], @@ -1438,6 +1507,7 @@ mod tests { sync::atomic::{AtomicBool, AtomicU64, Ordering}, thread, time, }, + Accounts, }; fn new_sanitized_tx( @@ -2912,6 +2982,8 @@ mod tests { txs.iter(), qos_results.iter(), MAX_TX_ACCOUNT_LOCKS, + &HashSet::default(), + &HashSet::default(), ); assert!(results[0].is_ok()); // Read-only account (keypair0) can be referenced multiple times @@ -3036,7 +3108,7 @@ mod tests { } let txs = vec![tx0, tx1]; let execution_results = vec![new_execution_result(Ok(()), None); 2]; - let (collected_accounts, txn_signatures) = accounts.collect_accounts_to_store( + let (collected_accounts, txn_signatures) = Accounts::collect_accounts_to_store( &txs, &execution_results, loaded.as_mut_slice(), @@ -3505,7 +3577,7 @@ mod tests { let mut loaded = vec![loaded]; let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique()); - let accounts = Accounts::new_with_config_for_tests( + let _accounts = Accounts::new_with_config_for_tests( Vec::new(), &ClusterType::Development, AccountSecondaryIndexes::default(), @@ -3520,7 +3592,7 @@ mod tests { )), nonce.as_ref(), )]; - let (collected_accounts, _) = accounts.collect_accounts_to_store( + let (collected_accounts, _) = Accounts::collect_accounts_to_store( &txs, &execution_results, loaded.as_mut_slice(), @@ -3620,7 +3692,7 @@ mod tests { let mut loaded = vec![loaded]; let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique()); - let accounts = Accounts::new_with_config_for_tests( + let _accounts = Accounts::new_with_config_for_tests( Vec::new(), &ClusterType::Development, AccountSecondaryIndexes::default(), @@ -3635,7 +3707,7 @@ mod tests { )), nonce.as_ref(), )]; - let (collected_accounts, _) = accounts.collect_accounts_to_store( + let (collected_accounts, _) = Accounts::collect_accounts_to_store( &txs, &execution_results, loaded.as_mut_slice(), diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index e4c832c5ba..22a9a59a3c 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -40,7 +40,7 @@ use { crate::{ account_overrides::AccountOverrides, accounts::{ - AccountAddressFilter, Accounts, LoadedTransaction, PubkeyAccountSlot, + AccountAddressFilter, AccountLocks, Accounts, LoadedTransaction, PubkeyAccountSlot, TransactionLoadResult, }, accounts_db::{ @@ -102,6 +102,7 @@ use { AccountSharedData, InheritableAccountFields, ReadableAccount, WritableAccount, }, account_utils::StateMut, + bundle::{error::BundleExecutionError, utils::check_bundle_lock_results}, clock::{ BankId, Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, INITIAL_RENT_EPOCH, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY, @@ -155,18 +156,21 @@ use { std::{ borrow::Cow, cell::RefCell, + cmp::min, collections::{HashMap, HashSet}, convert::{TryFrom, TryInto}, + error::Error, fmt, mem, ops::{Deref, RangeInclusive}, path::PathBuf, rc::Rc, + result, sync::{ atomic::{ AtomicBool, AtomicI64, AtomicU64, AtomicUsize, Ordering::{AcqRel, Acquire, Relaxed}, }, - Arc, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard, + Arc, LockResult, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, }, thread::Builder, time::{Duration, Instant}, @@ -285,7 +289,7 @@ impl RentDebits { } pub type BankStatusCache = StatusCache>; -#[frozen_abi(digest = "A7T7XohiSoo8FGoCPTsaXAYYugXTkoYnBjQAdBgYHH85")] +#[frozen_abi(digest = "24fumu9gE7YDgrkDzqEFRFciDu3BtozgAaLDTUuZatXM")] pub type BankSlotDelta = SlotDelta>; // Eager rent collection repeats in cyclic manner. @@ -424,8 +428,40 @@ impl TransactionExecutionResult { Self::NotExecuted(err) => Err(err.clone()), } } + + /// Return an Error if a transaction was executed and reverted + /// NOTE: `execution_results` are zipped with `sanitized_txs` so it's expected a sanitized tx at + /// position i has a corresponding execution result at position i within the `execution_results` + /// slice + pub fn check_bundle_execution_results<'a>( + execution_results: &[TransactionExecutionResult], + sanitized_txs: &'a [SanitizedTransaction], + ) -> result::Result<(), (BundleExecutionError, &'a Signature)> { + for (exec_results, sanitized_tx) in execution_results.iter().zip(sanitized_txs) { + match exec_results { + TransactionExecutionResult::Executed { + details, + tx_executor_cache: _, + } => { + if let Err(e) = &details.status { + return Err((e.clone().into(), sanitized_tx.signature())); + } + } + TransactionExecutionResult::NotExecuted(e) => { + if !matches!( + e, + TransactionError::AccountInUse | TransactionError::BundleNotContinuous + ) { + return Err((e.clone().into(), sanitized_tx.signature())); + } + } + } + } + Ok(()) + } } +#[derive(Debug)] pub struct LoadAndExecuteTransactionsOutput { pub loaded_transactions: Vec, // Vector of results indicating whether a transaction was executed or could not @@ -465,6 +501,39 @@ impl DurableNonceFee { } } +#[derive(Debug, Clone, PartialEq)] +pub enum BundleSimulationSummary { + // error and transaction signature responsible + Failed { + error: BundleExecutionError, + tx_signature: Signature, + }, + Succeeded, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AccountData { + pub pubkey: Pubkey, + pub data: AccountSharedData, +} + +#[derive(Clone)] +pub struct BundleSimulationResult { + /// Gives high level summary of bundle. + pub summary: BundleSimulationSummary, + pub transaction_results: Vec, +} + +#[derive(Clone)] +pub struct BundleTransactionSimulationResult { + pub result: Result<()>, + pub logs: TransactionLogMessages, + pub pre_execution_accounts: Option>, + pub post_execution_accounts: Option>, + pub return_data: Option, + pub units_consumed: u64, +} + pub struct TransactionSimulationResult { pub result: Result<()>, pub logs: TransactionLogMessages, @@ -472,6 +541,7 @@ pub struct TransactionSimulationResult { pub units_consumed: u64, pub return_data: Option, } + pub struct TransactionBalancesSet { pub pre_balances: TransactionBalances, pub post_balances: TransactionBalances, @@ -1035,7 +1105,7 @@ pub struct Bank { inflation: Arc>, /// cache of vote_account and stake_account state for this fork - stakes_cache: StakesCache, + pub stakes_cache: StakesCache, /// staked nodes on epoch boundaries, saved off when a bank.slot() is at /// a leader schedule calculation boundary @@ -3714,13 +3784,34 @@ impl Bank { &'a self, transactions: &'b [SanitizedTransaction], transaction_results: impl Iterator>, + additional_read_locks: &HashSet, + additional_write_locks: &HashSet, ) -> TransactionBatch<'a, 'b> { - // this lock_results could be: Ok, AccountInUse, WouldExceedBlockMaxLimit or WouldExceedAccountMaxLimit let tx_account_lock_limit = self.get_transaction_account_lock_limit(); let lock_results = self.rc.accounts.lock_accounts_with_results( transactions.iter(), transaction_results, tx_account_lock_limit, + additional_read_locks, + additional_write_locks, + ); + TransactionBatch::new(lock_results, self, Cow::Borrowed(transactions)) + } + + /// Prepare a locked transaction batch from a list of sanitized transactions, and their cost + /// limited packing status, where transactions will be locked sequentially until the first failure + pub fn prepare_sequential_sanitized_batch_with_results<'a, 'b>( + &'a self, + transactions: &'b [SanitizedTransaction], + // For use cases where you don't want to actually lock the accounts, for example when simulating. + account_locks_override: Option>, + ) -> TransactionBatch<'a, 'b> { + // this lock_results could be: Ok, AccountInUse, BundleNotContinuous, AccountLoadedTwice, or TooManyAccountLocks + let tx_account_lock_limit = self.get_transaction_account_lock_limit(); + let lock_results = self.rc.accounts.lock_accounts_sequential_with_results( + transactions.iter(), + tx_account_lock_limit, + account_locks_override, ); TransactionBatch::new(lock_results, self, Cow::Borrowed(transactions)) } @@ -3740,6 +3831,254 @@ impl Bank { batch } + /// Run bundles against a frozen bank without committing the results and return [BundleSimulationResult]. + /// Client has the option to request pre/post execution results on a per-transaction basis. + /// + /// For example given: + /// + /// Bundle: [T0{A, B, C}, T1{D}, T2{E, A, C}, T3{D, F}] + /// Requested Pre-Execution Accounts: [None, [A, D], [B], [A, C, F]] + /// Requested Post-Execution Accounts: [None, [D], None, [A, B, F]] + /// + /// It is expected that the following is returned: + /// Returned Pre-Execution Accounts: [None, [T0(A), D], [T0(B)], [T0(T2(A)), T0(T2(C)), F]] + /// Returned Post-Execution Accounts: [None, [T1(D)], None, [T0(T2(A), T0(B), T3(F)]] + pub fn simulate_bundle( + &self, + bundle: Vec, + pre_execution_accounts_requested: Vec>>, + post_execution_accounts_requested: Vec>>, + ) -> result::Result> { + assert!(self.is_frozen(), "simulation bank must be frozen"); + assert_eq!(pre_execution_accounts_requested.len(), bundle.len()); + assert_eq!(post_execution_accounts_requested.len(), bundle.len()); + + self.simulate_bundle_unchecked( + bundle, + pre_execution_accounts_requested, + post_execution_accounts_requested, + ) + } + + /// Run transactions against a bank without committing the results; does not check if the bank is frozen. + fn simulate_bundle_unchecked( + &self, + bundle: Vec, + pre_execution_accounts_requested: Vec>>, + post_execution_accounts_requested: Vec>>, + ) -> result::Result> { + // Used to cache account data in between batch execution iterations + let mut account_overrides = AccountOverrides::default(); + + let mut pre_execution_accounts_return_data = + Vec::with_capacity(pre_execution_accounts_requested.len()); + let mut post_execution_accounts_return_data = + Vec::with_capacity(post_execution_accounts_requested.len()); + let mut transaction_results = Vec::with_capacity(bundle.len()); + + let mut timings = ExecuteTimings::default(); + let mut chunk_start = 0; + while chunk_start != bundle.len() { + let chunk_end = min(bundle.len(), chunk_start + 128); + let chunk = &bundle[chunk_start..chunk_end]; + + let account_locks_override = Mutex::new(AccountLocks::default()); + let batch = self.prepare_sequential_sanitized_batch_with_results( + chunk, + Some(account_locks_override), + ); + + // check if any error + if let Some((error, failed_tx_idx)) = check_bundle_lock_results(batch.lock_results()) { + transaction_results.extend(vec![ + BundleTransactionSimulationResult { + result: Err(TransactionError::SkippedExecution), + logs: vec![], + pre_execution_accounts: None, + post_execution_accounts: None, + return_data: None, + units_consumed: 0, + }; + bundle.len() - chunk_start + ]); + + let mut res = transaction_results + .get_mut(failed_tx_idx + chunk_start) + .unwrap(); + res.result = Err(error.clone()); + + let failed_tx = &batch.sanitized_transactions()[failed_tx_idx]; + return Ok(BundleSimulationResult { + summary: BundleSimulationSummary::Failed { + error: error.into(), + tx_signature: *failed_tx.signature(), + }, + transaction_results, + }); + } + + // Set chunk_end to its true value i.e. the first occurrence of an acceptable lock error. + let chunk_end = match batch.lock_results().iter().position(|res| res.is_err()) { + Some(err_idx) => chunk_start + err_idx, + None => chunk_end, + }; + // Load the accounts requested by caller for current chunk of transactions prior to executing. + let pre_execution_accounts = &pre_execution_accounts_requested[chunk_start..chunk_end]; + for maybe_accounts in pre_execution_accounts { + if let Some(accounts) = maybe_accounts { + let mut pre_accounts = Vec::with_capacity(accounts.len()); + + for pubkey in accounts { + let data = if let Some(data) = account_overrides.get(pubkey).cloned() { + Ok(data) + } else { + self.get_account(pubkey) + // TODO(seg): let's use a concrete error type + .ok_or(format!("pubkey {} does not exist", pubkey)) + }?; + pre_accounts.push(AccountData { + pubkey: *pubkey, + data, + }); + } + + pre_execution_accounts_return_data.push(Some(pre_accounts)) + } else { + pre_execution_accounts_return_data.push(None); + } + } + + // Execute the transaction! + let LoadAndExecuteTransactionsOutput { + mut loaded_transactions, + execution_results, + .. + } = self.load_and_execute_transactions( + &batch, + // After simulation, transactions will need to be forwarded to the leader + // for processing. During forwarding, the transaction could expire if the + // delay is not accounted for. + MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY, + false, + true, + true, + &mut timings, + Some(&account_overrides), + None, + ); + + // Load account data for successful txs in current batch and store them to the overrides/cache. + let post_loaded_accounts = self + .collect_accounts_to_store( + batch.sanitized_transactions(), + &execution_results, + &mut loaded_transactions, + ) + .into_iter() + .map(|(pubkey, data)| { + account_overrides.set_account(pubkey, Some(data.clone())); + (pubkey, data) + }) + .collect::>(); + + // We know `transactions[chunk_start..chunk_end]` succeeded, so fetch the corresponding requested pubkeys. + // e.g. given Bundle: [T0{A, B}, T1{B, C}, T2{E, F}] and Post Execution Accounts: [None, [A, B], [E]] + // where current chunk is (1..3) then we load up [[A, B], [E]] + let post_execution_accounts = + &post_execution_accounts_requested[chunk_start..chunk_end]; + for maybe_accounts in post_execution_accounts { + if let Some(accounts) = maybe_accounts { + let mut post_accounts = Vec::with_capacity(accounts.len()); + for pubkey in accounts { + let maybe_data = + if let Some(data) = post_loaded_accounts.get(pubkey).cloned() { + Some(data.clone()) + } else { + account_overrides.get(pubkey).cloned() + }; + if let Some(data) = maybe_data { + post_accounts.push(AccountData { + pubkey: *pubkey, + data: data.clone(), + }); + } + } + + post_execution_accounts_return_data.push(Some(post_accounts)) + } else { + post_execution_accounts_return_data.push(None); + } + } + + let simulation_results = loaded_transactions.iter().zip(&execution_results[..]).map( + |(loaded_tx_result, exec_result)| { + Self::build_transaction_simulation_result(loaded_tx_result, exec_result) + }, + ); + + // save the transaction results + for (offset, tx_result) in simulation_results.enumerate() { + let position = offset + chunk_start; + if position == chunk_end { + break; + } + + transaction_results.push(BundleTransactionSimulationResult { + result: tx_result.result, + logs: tx_result.logs, + pre_execution_accounts: pre_execution_accounts_return_data + .get(position) + .cloned() + .unwrap_or_default(), + post_execution_accounts: post_execution_accounts_return_data + .get(position) + .cloned() + .unwrap_or_default(), + return_data: tx_result.return_data, + units_consumed: tx_result.units_consumed, + }); + } + + if let Err((error, tx_signature)) = + TransactionExecutionResult::check_bundle_execution_results( + &execution_results[..], + batch.sanitized_transactions(), + ) + { + // fill the result of the vector with [SkippedExecution] if any txs left over + transaction_results.extend(vec![ + BundleTransactionSimulationResult { + result: Err(TransactionError::SkippedExecution), + logs: vec![], + pre_execution_accounts: None, + post_execution_accounts: None, + return_data: None, + units_consumed: 0, + }; + bundle.len() - chunk_end + ]); + + return Ok(BundleSimulationResult { + summary: BundleSimulationSummary::Failed { + error, + tx_signature: *tx_signature, + }, + transaction_results, + }); + } + + // Welcome to Rust & Solana where we optimize for performance over readability! + // Remember chunk_end was updated above based on whether or not there was the + // batch was not continuous. + chunk_start = chunk_end; + } + + Ok(BundleSimulationResult { + summary: BundleSimulationSummary::Succeeded, + transaction_results, + }) + } + /// Run transactions against a frozen bank without committing the results pub fn simulate_transaction( &self, @@ -3757,14 +4096,13 @@ impl Bank { transaction: SanitizedTransaction, ) -> TransactionSimulationResult { let account_keys = transaction.message().account_keys(); - let number_of_accounts = account_keys.len(); let account_overrides = self.get_account_overrides_for_simulation(&account_keys); let batch = self.prepare_simulation_batch(transaction); let mut timings = ExecuteTimings::default(); let LoadAndExecuteTransactionsOutput { loaded_transactions, - mut execution_results, + execution_results, .. } = self.load_and_execute_transactions( &batch, @@ -3780,43 +4118,42 @@ impl Bank { None, ); - let post_simulation_accounts = loaded_transactions - .into_iter() - .next() - .unwrap() - .0 - .ok() - .map(|loaded_transaction| { - loaded_transaction - .accounts - .into_iter() - .take(number_of_accounts) - .collect::>() - }) - .unwrap_or_default(); - - let units_consumed = timings - .details - .per_program_timings - .iter() - .fold(0, |acc: u64, (_, program_timing)| { - acc.saturating_add(program_timing.accumulated_units) - }); - - debug!("simulate_transaction: {:?}", timings); + Self::build_transaction_simulation_result(&loaded_transactions[0], &execution_results[0]) + } - let execution_result = execution_results.pop().unwrap(); - let flattened_result = execution_result.flattened_result(); - let (logs, return_data) = match execution_result { + fn build_transaction_simulation_result( + loaded_transaction_result: &TransactionLoadResult, + execution_result: &TransactionExecutionResult, + ) -> TransactionSimulationResult { + let (logs, return_data, units_consumed, result) = match execution_result { TransactionExecutionResult::Executed { details, .. } => { - (details.log_messages, details.return_data) + let log_messages = if let Some(ref log_messages) = details.log_messages { + log_messages.clone() + } else { + vec![] + }; + + ( + log_messages, + details.return_data.as_ref().cloned(), + details.executed_units, + execution_result.flattened_result(), + ) + } + TransactionExecutionResult::NotExecuted(_) => { + (vec![], None, 0, execution_result.flattened_result()) } - TransactionExecutionResult::NotExecuted(_) => (None, None), }; - let logs = logs.unwrap_or_default(); + + let post_simulation_accounts = loaded_transaction_result + .0 + .as_ref() + .ok() + .map(|tx| tx.accounts.clone()) + .unwrap_or_default(); TransactionSimulationResult { - result: flattened_result, + result, logs, post_simulation_accounts, units_consumed, @@ -3992,6 +4329,29 @@ impl Bank { balances } + pub fn collect_balances_with_cache( + &self, + batch: &TransactionBatch, + account_overrides: Option<&AccountOverrides>, + ) -> TransactionBalances { + let mut balances: TransactionBalances = vec![]; + for transaction in batch.sanitized_transactions() { + let mut transaction_balances: Vec = vec![]; + for account_key in transaction.message().account_keys().iter() { + let balance = match account_overrides { + None => self.get_balance(account_key), + Some(overrides) => match overrides.get(account_key) { + None => self.get_balance(account_key), + Some(account_data) => account_data.lamports(), + }, + }; + transaction_balances.push(balance); + } + balances.push(transaction_balances); + } + balances + } + /// Get any cached executors needed by the transaction fn get_tx_executor_cache( &self, @@ -4870,6 +5230,27 @@ impl Bank { } } + pub fn collect_accounts_to_store<'a>( + &self, + txs: &'a [SanitizedTransaction], + res: &'a [TransactionExecutionResult], + loaded: &'a mut [TransactionLoadResult], + ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> { + let (last_blockhash, lamports_per_signature) = + self.last_blockhash_and_lamports_per_signature(); + let durable_nonce = DurableNonce::from_blockhash(&last_blockhash); + Accounts::collect_accounts_to_store( + txs, + res, + loaded, + &self.rent_collector, + &durable_nonce, + lamports_per_signature, + self.preserve_rent_epoch_for_rent_exempt_accounts(), + ) + .0 + } + // Distribute collected rent fees for this slot to staked validators (excluding stakers) // according to stake. // @@ -7941,6 +8322,127 @@ pub(crate) mod tests { } } + fn tx_factory( + readonly_accounts: Vec, + mut writeable_accounts: Vec, + signer_key_pair: Keypair, + ) -> Transaction { + if !writeable_accounts.contains(&signer_key_pair.pubkey()) { + writeable_accounts.insert(0, signer_key_pair.pubkey()); + } + let num_readonly_unsigned_accounts = readonly_accounts.len() as u8; + writeable_accounts.extend(readonly_accounts); + + let message = Message { + header: MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts, + }, + account_keys: writeable_accounts, + recent_blockhash: Hash::default(), + instructions: vec![], + }; + let signature = signer_key_pair.sign_message(&message.serialize()[..]); + + Transaction { + signatures: vec![signature], + message, + } + } + + #[test] + fn test_prepare_sequential_sanitized_batch_with_results_happy_path() { + let (genesis_config, _mint_keypair) = create_genesis_config(10); + let bank = Bank::new_for_tests(&genesis_config); + + // 1. create a bundle of self-conflicting account accesses + // e.g. T0{write-a, write-b}, T1{write-a, read-b, read-c}, T2{read-c, write-d, write-e}, T3{read-e, write-f} + let a = Keypair::new(); + let b = Keypair::new(); + let c = Keypair::new(); + let d = Keypair::new(); + let e = Keypair::new(); + let f = Keypair::new(); + + let tx_0 = tx_factory( + vec![], + vec![a.pubkey(), b.pubkey()], + Keypair::from_base58_string(&*a.to_base58_string()), + ); + let tx_0 = SanitizedTransaction::from_transaction_for_tests(tx_0); + + let tx_1 = tx_factory(vec![b.pubkey(), c.pubkey()], vec![a.pubkey()], a); + let tx_1 = SanitizedTransaction::from_transaction_for_tests(tx_1); + + let tx_2 = tx_factory(vec![c.pubkey()], vec![d.pubkey(), e.pubkey()], d); + let tx_2 = SanitizedTransaction::from_transaction_for_tests(tx_2); + + let tx_3 = tx_factory(vec![e.pubkey()], vec![f.pubkey()], f); + let tx_3 = SanitizedTransaction::from_transaction_for_tests(tx_3); + + // 2. test batches are chunked correctly + let sanitized_txs = vec![tx_0, tx_1, tx_2, tx_3]; + + let expected_next_start = 1; + _test_prepare_sequential_sanitized_batch_with_results( + &bank, + &sanitized_txs, + 0, + sanitized_txs.len(), + Some(expected_next_start), + 1, + ); + + let new_start = expected_next_start; + let expected_next_start = 3; + _test_prepare_sequential_sanitized_batch_with_results( + &bank, + &sanitized_txs, + new_start, + sanitized_txs.len(), + Some(expected_next_start), + 2, + ); + + let new_start = expected_next_start; + _test_prepare_sequential_sanitized_batch_with_results( + &bank, + &sanitized_txs, + new_start, + sanitized_txs.len(), + None, + 1, + ); + } + + fn _test_prepare_sequential_sanitized_batch_with_results( + bank: &Bank, + sanitized_txs: &[SanitizedTransaction], + chunk_start: usize, + chunk_end: usize, + expected_next_start: Option, + expected_okays: usize, + ) { + let account_locks_override = Mutex::new(AccountLocks::default()); + let chunk = &sanitized_txs[chunk_start..chunk_end]; + let batch = bank + .prepare_sequential_sanitized_batch_with_results(chunk, Some(account_locks_override)); + + assert_eq!( + batch + .lock_results() + .iter() + .filter(|res| res.is_ok()) + .count(), + expected_okays + ); + + let first_err_idx = batch.lock_results().iter().position(|res| res.is_err()); + let actual_next_start = first_err_idx.map(|first_err_idx| first_err_idx + chunk_start); + assert_eq!(actual_next_start, expected_next_start); + } + impl Bank { fn clean_accounts_for_tests(&self) { self.rc.accounts.accounts_db.clean_accounts_for_tests() @@ -8522,6 +9024,593 @@ pub(crate) mod tests { assert_eq!(account, bank.get_account(&pubkey).unwrap()); } + #[test] + #[should_panic] + fn test_simulate_bundle_unfrozen_bank() { + let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000); + let bank = Bank::new_for_tests(&genesis_config); + let _ = bank.simulate_bundle(vec![], vec![], vec![]); + } + + #[test] + #[should_panic] + fn test_simulate_bundle_mismatched_lengths() { + let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000); + let bank = Bank::new_for_tests(&genesis_config); + bank.freeze(); + + let _ = bank.simulate_bundle(vec![], vec![None], vec![None]); + } + + fn setup_system_accounts( + pubkeys: Vec, + lamports: u64, + bank: &Bank, + ) -> Vec { + pubkeys + .iter() + .map(|pk| { + let data = AccountSharedData::new(lamports, 0, &system_program::id()); + bank.store_account(pk, &data); + data + }) + .collect::>() + } + + fn assert_transaction_results( + actual_results: Vec>, + expected_results: Vec>, + ) { + assert_eq!(actual_results.len(), expected_results.len()); + for (i, (actual, expected)) in actual_results.iter().zip(expected_results).enumerate() { + assert_eq!( + actual, + &expected, + "{}", + format_args!("result at index {} did not match", i) + ); + } + } + + fn assert_simulate_bundle_correct_lamports( + actual_account_data: Vec>>, + expected_lamports: Vec>>, + ) { + assert_eq!(actual_account_data.len(), expected_lamports.len()); + + for (i, maybe_actual) in actual_account_data.iter().enumerate() { + if let Some(expected) = expected_lamports[i].clone() { + assert!(maybe_actual.is_some()); + let actual = maybe_actual.clone().unwrap(); + assert_eq!(actual.len(), expected.keys().len()); + + for (pk, lamports) in expected { + let account = actual.iter().find(|acc| acc.pubkey == pk).unwrap(); + assert_eq!(account.data.lamports(), lamports) + } + } else { + assert!(maybe_actual.is_none()); + } + } + } + + /// Tests with a bundle expected to fail due to `check_bundle_lock_results`. None of the transactions + /// should execute due to bad locking behaviour! + /// + /// Bundle: [T0{Faucet, A, B}, T1{Z, C}, T2{Z, C}, T3{Faucet, A, A, B}, T4{Faucet, C}] + /// Requested Pre-Execution Accounts: [[A, B], None, None, [A, B], [C]] + /// Requested Post-Execution Accounts: [[A], [C], None, [A, B], [C]] + /// + /// Expect the following: + /// Returned Pre-Execution Accounts: [None, None, None, None, None] + /// Returned Post-Execution Accounts: [None, None, None, None, None] + #[test] + fn test_simulate_bundle_with_bad_locks() { + let (genesis_config, faucet_keypair) = create_genesis_config(1_000_000); + let bank = Bank::new_for_tests(&genesis_config); + let recent_blockhash = bank.confirmed_last_blockhash(); + + // Setup + let a = solana_sdk::pubkey::new_rand(); + let b = solana_sdk::pubkey::new_rand(); + let c = solana_sdk::pubkey::new_rand(); + let z = Keypair::new(); + let initial_lamports = 100_000; + let _ = setup_system_accounts(vec![a, b, c, z.pubkey()], initial_lamports, &bank); + + bank.freeze(); + + let mut expected_pre_lamports_returned = vec![]; + let mut expected_post_lamports_returned = vec![]; + + // Create the transactions + let to_lamports = &[(a, 100), (b, 1000)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_0 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + let to_lamports = &[(c, 1)]; + let ixs = system_instruction::transfer_many(&z.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&z.pubkey())); + let tx_1 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&z], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + let to_lamports = &[(c, 42069)]; + let ixs = system_instruction::transfer_many(&z.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&z.pubkey())); + let tx_2 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&z], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + let message = Message { + header: MessageHeader { + num_required_signatures: 1, + ..MessageHeader::default() + }, + account_keys: vec![faucet_keypair.pubkey(), a, a], + ..Message::default() + }; + let tx_3 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + let to_lamports = &[(c, 3433)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_4 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + // Create params + let pre_execution_accounts = vec![ + Some(vec![a, b]), + None, + None, + Some(vec![a, b]), + Some(vec![c]), + ]; + let post_execution_accounts = vec![ + Some(vec![a]), + Some(vec![c]), + None, + Some(vec![a, b]), + Some(vec![c]), + ]; + let bundle = vec![tx_0, tx_1, tx_2, tx_3.clone(), tx_4]; + + // Do it! + let result = bank + .simulate_bundle( + bundle.clone(), + pre_execution_accounts, + post_execution_accounts, + ) + .unwrap(); + + // Basic assertions + assert_eq!( + result.summary, + BundleSimulationSummary::Failed { + error: BundleExecutionError::TransactionFailure( + TransactionError::AccountLoadedTwice + ), + tx_signature: *tx_3.signature() + } + ); + assert_eq!(result.transaction_results.len(), bundle.len()); + + let expected_reults = vec![ + Err(TransactionError::SkippedExecution), + Err(TransactionError::SkippedExecution), + Err(TransactionError::SkippedExecution), + Err(TransactionError::AccountLoadedTwice), + Err(TransactionError::SkippedExecution), + ]; + let actual_results = result + .transaction_results + .clone() + .into_iter() + .map(|res| res.result) + .collect::>>(); + assert_transaction_results(actual_results, expected_reults); + + let actual_pre_lamports = result + .transaction_results + .clone() + .into_iter() + .map(|res| res.pre_execution_accounts) + .collect::>>>(); + assert_simulate_bundle_correct_lamports( + actual_pre_lamports, + expected_pre_lamports_returned, + ); + + let actual_post_lamports = result + .transaction_results + .into_iter() + .map(|res| res.post_execution_accounts) + .collect::>>>(); + assert_simulate_bundle_correct_lamports( + actual_post_lamports, + expected_post_lamports_returned, + ); + } + + /// Tests with a bundle expected to fail due to failing transaction execution. + /// The first two txs are parallelize and both succeed. T3 fails execution causing + /// all txs in its chunk (T2 & T4) to fail with [TransactionError::SkippedExecution]. + /// + /// Bundle: [T0{Faucet, A, B}, T1{Z, C}, T2{Faucet, C}, T3{Z, A, B}, T4{Faucet, C}] + /// Requested Pre-Execution Accounts: [[A, B], None, [C], [A, B], [C]] + /// Requested Post-Execution Accounts: [[A], [C], None, [A, B], [C]] + /// + /// Expect the following: + /// Returned Pre-Execution Accounts: [[A, B], None, [T1(C)], [T0(A), T0(B)], None] + /// Returned Post-Execution Accounts: [[T0(A)], [T1(C)], None, [T0(A), T0(B)], None] + #[test] + fn test_simulate_bundle_with_failing_tx() { + let (genesis_config, faucet_keypair) = create_genesis_config(1_000_000); + let bank = Bank::new_for_tests(&genesis_config); + let recent_blockhash = bank.confirmed_last_blockhash(); + + // Setup + let a = solana_sdk::pubkey::new_rand(); + let b = solana_sdk::pubkey::new_rand(); + let c = solana_sdk::pubkey::new_rand(); + let z = Keypair::new(); + let initial_lamports = 100_000; + let (pre_a_data, pre_b_data, pre_c_data, pre_z_data) = + match &setup_system_accounts(vec![a, b, c, z.pubkey()], initial_lamports, &bank)[..] { + [pre_a_data, pre_b_data, pre_c_data, pre_z_data] => ( + pre_a_data.clone(), + pre_b_data.clone(), + pre_c_data.clone(), + pre_z_data.clone(), + ), + _ => unreachable!(), + }; + + bank.freeze(); + + let mut expected_pre_lamports_returned = vec![]; + let mut expected_post_lamports_returned = vec![]; + + // Create the transactions + let to_lamports = &[(a, 100), (b, 1000)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_0 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports()); + m.insert(b, pre_b_data.lamports()); + expected_pre_lamports_returned.push(Some(m)); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports() + 100); + expected_post_lamports_returned.push(Some(m)); + + let to_lamports = &[(c, 1)]; + let ixs = system_instruction::transfer_many(&z.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&z.pubkey())); + let tx_1 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&z], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + let mut m = HashMap::new(); + m.insert(c, pre_c_data.lamports() + 1); + expected_post_lamports_returned.push(Some(m)); + + let to_lamports = &[(c, 42069)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_2 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + let mut m = HashMap::new(); + m.insert(c, pre_c_data.lamports() + 1); + expected_pre_lamports_returned.push(Some(m)); + expected_post_lamports_returned.push(None); + + let to_lamports = &[(c, pre_z_data.lamports() + 1000)]; + let ixs = system_instruction::transfer_many(&z.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&z.pubkey())); + let tx_3 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&z], + message, + recent_blockhash, + )) + .unwrap(); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports() + 100); + m.insert(b, pre_b_data.lamports() + 1000); + expected_pre_lamports_returned.push(Some(m)); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports() + 100); + m.insert(b, pre_b_data.lamports() + 1000); + expected_post_lamports_returned.push(Some(m)); + + let to_lamports = &[(c, 3433)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_4 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + // Create params + let pre_execution_accounts = vec![ + Some(vec![a, b]), + None, + Some(vec![c]), + Some(vec![a, b]), + Some(vec![c]), + ]; + let post_execution_accounts = vec![ + Some(vec![a]), + Some(vec![c]), + None, + Some(vec![a, b]), + Some(vec![c]), + ]; + let bundle = vec![tx_0, tx_1, tx_2, tx_3.clone(), tx_4]; + + // Do it! + let result = bank + .simulate_bundle( + bundle.clone(), + pre_execution_accounts, + post_execution_accounts, + ) + .unwrap(); + + // Basic assertions + assert_eq!( + result.summary, + BundleSimulationSummary::Failed { + error: BundleExecutionError::TransactionFailure( + TransactionError::InstructionError(0, InstructionError::Custom(1)) + ), + tx_signature: *tx_3.signature() + } + ); + assert_eq!(result.transaction_results.len(), bundle.len()); + + let expected_reults = vec![ + Ok(()), + Ok(()), + Ok(()), + Err(TransactionError::InstructionError( + 0, + InstructionError::Custom(1), + )), + Err(TransactionError::SkippedExecution), + ]; + let actual_results = result + .transaction_results + .clone() + .into_iter() + .map(|res| res.result) + .collect::>>(); + assert_transaction_results(actual_results, expected_reults); + + let actual_pre_lamports = result + .transaction_results + .clone() + .into_iter() + .map(|res| res.pre_execution_accounts) + .collect::>>>(); + assert_simulate_bundle_correct_lamports( + actual_pre_lamports, + expected_pre_lamports_returned, + ); + + let actual_post_lamports = result + .transaction_results + .into_iter() + .map(|res| res.post_execution_accounts) + .collect::>>>(); + assert_simulate_bundle_correct_lamports( + actual_post_lamports, + expected_post_lamports_returned, + ); + } + + /// Tests with a bundle expected to succeed, containing no parallelize chunks + /// + /// Bundle: [T0{Faucet, A, B, C}, T1{Faucet, D}, T2{Faucet, E, A, C}, T3{Faucet, D, F}] + /// Requested Pre-Execution Accounts: [None, [A, D], [B], [A, C, F]] + /// Requested Post-Execution Accounts: [None, [D], None, [A, B, F]] + /// + /// Expect the following: + /// Returned Pre-Execution Accounts: [None, [T0(A), D], [T0(B)], [T0(T2(A)), T0(T2(C)), F]] + /// Returned Post-Execution Accounts: [None, [T1(D)], None, [T0(T2(A), T0(B), T3(F)]] + #[test] + fn test_simulate_bundle_happy_path() { + let (genesis_config, faucet_keypair) = create_genesis_config(1_000_000); + let bank = Bank::new_for_tests(&genesis_config); + let recent_blockhash = bank.confirmed_last_blockhash(); + + // Create some accounts and save them to the bank + let a = solana_sdk::pubkey::new_rand(); + let b = solana_sdk::pubkey::new_rand(); + let c = solana_sdk::pubkey::new_rand(); + let d = solana_sdk::pubkey::new_rand(); + let e = solana_sdk::pubkey::new_rand(); + let f = solana_sdk::pubkey::new_rand(); + let initial_lamports = 100_000; + let (pre_a_data, pre_b_data, pre_c_data, pre_d_data, pre_f_data) = + match &setup_system_accounts(vec![a, b, c, d, e, f], initial_lamports, &bank)[..] { + [pre_a_data, pre_b_data, pre_c_data, pre_d_data, _, pre_f_data] => ( + pre_a_data.clone(), + pre_b_data.clone(), + pre_c_data.clone(), + pre_d_data.clone(), + pre_f_data.clone(), + ), + _ => unreachable!(), + }; + + bank.freeze(); + + let mut expected_pre_lamports_returned = vec![]; + let mut expected_post_lamports_returned = vec![]; + + // Create the transactions + let to_lamports = &[(a, 100), (b, 1000), (c, 200)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_0 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + expected_pre_lamports_returned.push(None); + expected_post_lamports_returned.push(None); + + let to_lamports = &[(d, 343)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_1 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports() + 100); + m.insert(d, pre_d_data.lamports()); + expected_pre_lamports_returned.push(Some(m)); + let mut m = HashMap::new(); + m.insert(d, pre_d_data.lamports() + 343); + expected_post_lamports_returned.push(Some(m)); + + let to_lamports = &[(e, 378), (a, 1002), (c, 200)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_2 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + let mut m = HashMap::new(); + m.insert(b, pre_b_data.lamports() + 1000); + expected_pre_lamports_returned.push(Some(m)); + expected_post_lamports_returned.push(None); + + let to_lamports = &[(d, 378), (f, 1002)]; + let ixs = system_instruction::transfer_many(&faucet_keypair.pubkey(), to_lamports); + let message = Message::new(&ixs[..], Some(&faucet_keypair.pubkey())); + let tx_3 = SanitizedTransaction::try_from_legacy_transaction(Transaction::new( + &[&faucet_keypair], + message, + recent_blockhash, + )) + .unwrap(); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports() + 100 + 1002); + m.insert(c, pre_c_data.lamports() + 200 + 200); + m.insert(f, pre_f_data.lamports()); + expected_pre_lamports_returned.push(Some(m)); + let mut m = HashMap::new(); + m.insert(a, pre_a_data.lamports() + 100 + 1002); + m.insert(b, pre_b_data.lamports() + 1000); + m.insert(f, pre_f_data.lamports() + 1002); + expected_post_lamports_returned.push(Some(m)); + + // Create params + let pre_execution_accounts = + vec![None, Some(vec![a, d]), Some(vec![b]), Some(vec![a, c, f])]; + let post_execution_accounts = vec![None, Some(vec![d]), None, Some(vec![a, b, f])]; + let bundle = vec![tx_0, tx_1, tx_2, tx_3]; + + // Do it! + let result = bank + .simulate_bundle( + bundle.clone(), + pre_execution_accounts, + post_execution_accounts, + ) + .unwrap(); + + // Basic assertions + assert_eq!(result.summary, BundleSimulationSummary::Succeeded); + assert_eq!(result.transaction_results.len(), bundle.len()); + + let expected_reults = vec![Ok(()), Ok(()), Ok(()), Ok(())]; + let actual_results = result + .transaction_results + .clone() + .into_iter() + .map(|res| res.result) + .collect::>>(); + assert_transaction_results(actual_results, expected_reults); + + let actual_pre_lamports = result + .transaction_results + .clone() + .into_iter() + .map(|res| res.pre_execution_accounts) + .collect::>>>(); + assert_simulate_bundle_correct_lamports( + actual_pre_lamports, + expected_pre_lamports_returned, + ); + + let actual_post_lamports = result + .transaction_results + .into_iter() + .map(|res| res.post_execution_accounts) + .collect::>>>(); + assert_simulate_bundle_correct_lamports( + actual_post_lamports, + expected_post_lamports_returned, + ); + } + #[test] fn test_store_account_and_update_capitalization_increased() { let old_lamports = 400; diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index d7ec37aed3..f06b6c3e8c 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -169,7 +169,7 @@ fn builtin_feature_transitions() -> Vec { ] } -pub(crate) fn get() -> Builtins { +pub fn get() -> Builtins { Builtins { genesis_builtins: genesis_builtins(), feature_transitions: builtin_feature_transitions(), diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index 0cbb70964f..94ab6e4d92 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -913,12 +913,13 @@ pub fn bank_fields_from_snapshot_archives( incremental_snapshot_archives_dir: impl AsRef, ) -> Result { let full_snapshot_archive_info = - get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir) + get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir, None) .ok_or(SnapshotError::NoSnapshotArchives)?; let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info( &incremental_snapshot_archives_dir, full_snapshot_archive_info.slot(), + None, ); let temp_dir = tempfile::Builder::new() @@ -1059,18 +1060,20 @@ pub fn bank_from_latest_snapshot_archives( accounts_db_config: Option, accounts_update_notifier: Option, exit: &Arc, + halt_at_slot: Option, ) -> Result<( Bank, FullSnapshotArchiveInfo, Option, )> { let full_snapshot_archive_info = - get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir) + get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir, halt_at_slot) .ok_or(SnapshotError::NoSnapshotArchives)?; let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info( &incremental_snapshot_archives_dir, full_snapshot_archive_info.slot(), + halt_at_slot, ); info!( @@ -1510,8 +1513,9 @@ pub fn get_incremental_snapshot_archives( /// Get the highest slot of the full snapshot archives in a directory pub fn get_highest_full_snapshot_archive_slot( full_snapshot_archives_dir: impl AsRef, + halt_at_slot: Option, ) -> Option { - get_highest_full_snapshot_archive_info(full_snapshot_archives_dir) + get_highest_full_snapshot_archive_info(full_snapshot_archives_dir, halt_at_slot) .map(|full_snapshot_archive_info| full_snapshot_archive_info.slot()) } @@ -1520,10 +1524,12 @@ pub fn get_highest_full_snapshot_archive_slot( pub fn get_highest_incremental_snapshot_archive_slot( incremental_snapshot_archives_dir: impl AsRef, full_snapshot_slot: Slot, + halt_at_slot: Option, ) -> Option { get_highest_incremental_snapshot_archive_info( incremental_snapshot_archives_dir, full_snapshot_slot, + halt_at_slot, ) .map(|incremental_snapshot_archive_info| incremental_snapshot_archive_info.slot()) } @@ -1531,8 +1537,13 @@ pub fn get_highest_incremental_snapshot_archive_slot( /// Get the path (and metadata) for the full snapshot archive with the highest slot in a directory pub fn get_highest_full_snapshot_archive_info( full_snapshot_archives_dir: impl AsRef, + halt_at_slot: Option, ) -> Option { let mut full_snapshot_archives = get_full_snapshot_archives(full_snapshot_archives_dir); + if let Some(halt_at_slot) = halt_at_slot { + full_snapshot_archives + .retain(|archive| archive.snapshot_archive_info().slot <= halt_at_slot); + } full_snapshot_archives.sort_unstable(); full_snapshot_archives.into_iter().rev().next() } @@ -1542,6 +1553,7 @@ pub fn get_highest_full_snapshot_archive_info( pub fn get_highest_incremental_snapshot_archive_info( incremental_snapshot_archives_dir: impl AsRef, full_snapshot_slot: Slot, + halt_at_slot: Option, ) -> Option { // Since we want to filter down to only the incremental snapshot archives that have the same // full snapshot slot as the value passed in, perform the filtering before sorting to avoid @@ -1553,6 +1565,9 @@ pub fn get_highest_incremental_snapshot_archive_info( incremental_snapshot_archive_info.base_slot() == full_snapshot_slot }) .collect::>(); + if let Some(halt_at_slot) = halt_at_slot { + incremental_snapshot_archives.retain(|archive| archive.slot() <= halt_at_slot); + } incremental_snapshot_archives.sort_unstable(); incremental_snapshot_archives.into_iter().rev().next() } @@ -3007,7 +3022,7 @@ mod tests { ); assert_eq!( - get_highest_full_snapshot_archive_slot(full_snapshot_archives_dir.path()), + get_highest_full_snapshot_archive_slot(full_snapshot_archives_dir.path(), None), Some(max_slot - 1) ); } @@ -3034,7 +3049,8 @@ mod tests { assert_eq!( get_highest_incremental_snapshot_archive_slot( incremental_snapshot_archives_dir.path(), - full_snapshot_slot + full_snapshot_slot, + None, ), Some(max_incremental_snapshot_slot - 1) ); @@ -3043,7 +3059,8 @@ mod tests { assert_eq!( get_highest_incremental_snapshot_archive_slot( incremental_snapshot_archives_dir.path(), - max_full_snapshot_slot + max_full_snapshot_slot, + None, ), None ); @@ -3750,6 +3767,7 @@ mod tests { Some(ACCOUNTS_DB_CONFIG_FOR_TESTING), None, &Arc::default(), + None, ) .unwrap(); diff --git a/runtime/src/stake_account.rs b/runtime/src/stake_account.rs index d55b1ab362..71bf4d4ae9 100644 --- a/runtime/src/stake_account.rs +++ b/runtime/src/stake_account.rs @@ -52,7 +52,7 @@ impl StakeAccount { impl StakeAccount { #[inline] - pub(crate) fn delegation(&self) -> Delegation { + pub fn delegation(&self) -> Delegation { // Safe to unwrap here because StakeAccount will always // only wrap a stake-state which is a delegation. self.stake_state.delegation().unwrap() diff --git a/runtime/src/stakes.rs b/runtime/src/stakes.rs index 5a3d639839..3f003b3a93 100644 --- a/runtime/src/stakes.rs +++ b/runtime/src/stakes.rs @@ -50,17 +50,17 @@ pub enum InvalidCacheEntryReason { WrongOwner, } -type StakeAccount = stake_account::StakeAccount; +pub type StakeAccount = stake_account::StakeAccount; #[derive(Default, Debug, AbiExample)] -pub(crate) struct StakesCache(RwLock>); +pub struct StakesCache(RwLock>); impl StakesCache { pub(crate) fn new(stakes: Stakes) -> Self { Self(RwLock::new(stakes)) } - pub(crate) fn stakes(&self) -> RwLockReadGuard> { + pub fn stakes(&self) -> RwLockReadGuard> { self.0.read().unwrap() } @@ -171,7 +171,7 @@ pub struct Stakes { vote_accounts: VoteAccounts, /// stake_delegations - stake_delegations: ImHashMap, + pub stake_delegations: ImHashMap, /// unused unused: u64, @@ -211,7 +211,7 @@ impl Stakes { /// full account state for respective stake pubkeys. get_account function /// should return the account at the respective slot where stakes where /// cached. - pub(crate) fn new(stakes: &Stakes, get_account: F) -> Result + pub fn new(stakes: &Stakes, get_account: F) -> Result where F: Fn(&Pubkey) -> Option, { @@ -405,7 +405,7 @@ impl Stakes { } } - pub(crate) fn stake_delegations(&self) -> &ImHashMap { + pub fn stake_delegations(&self) -> &ImHashMap { &self.stake_delegations } diff --git a/rustfmt.toml b/rustfmt.toml index e26d07f0d8..c7ccd48750 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,7 @@ imports_granularity = "One" group_imports = "One" + +ignore = [ + "jito-programs", + "anchor" +] \ No newline at end of file diff --git a/s b/s new file mode 100755 index 0000000000..bf5b782f1f --- /dev/null +++ b/s @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" + +if [ -f .env ]; then + export $(cat .env | grep -v '#' | awk '/=/ {print $1}') +else + echo "Missing .env file" + exit 0 +fi + +echo "Syncing to host: $HOST" + +# sync to build server, ignoring local builds and local/remote dev ledger +rsync -avh --delete --exclude target --exclude config "$SCRIPT_DIR" "$HOST":~/ diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 059ddc2e20..7a54a4c65b 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -69,14 +69,15 @@ if [[ -n $CI || -z $1 ]]; then fi # limit jobs to 4gb/thread -if [[ -f "/proc/meminfo" ]]; then - JOBS=$(grep MemTotal /proc/meminfo | awk '{printf "%.0f", ($2 / (4 * 1024 * 1024))}') -else - JOBS=$(sysctl hw.memsize | awk '{printf "%.0f", ($2 / (4 * 1024**3))}') -fi - -NPROC=$(nproc) -JOBS=$((JOBS>NPROC ? NPROC : JOBS)) +#if [[ -f "/proc/meminfo" ]]; then + #JOBS=$(grep MemTotal /proc/meminfo | awk '{printf "%.0f", ($2 / (4 * 1024 * 1024))}') +#else + #JOBS=$(sysctl hw.memsize | awk '{printf "%.0f", ($2 / (4 * 1024**3))}') +#fi + +#NPROC=$(nproc) +#JOBS=$((JOBS>NPROC ? NPROC : JOBS)) +JOBS=32 RUST_LOG=solana=trace _ "$cargo" nightly test --jobs "$JOBS" --target-dir target/cov --no-run "${packages[@]}" if RUST_LOG=solana=trace _ "$cargo" nightly test --jobs "$JOBS" --target-dir target/cov "${packages[@]}" -- --nocapture 2> >(tee target/cov/coverage-stderr.log >&2); then diff --git a/scripts/increment-cargo-version.sh b/scripts/increment-cargo-version.sh index 7a5f489603..a4bcfa5918 100755 --- a/scripts/increment-cargo-version.sh +++ b/scripts/increment-cargo-version.sh @@ -24,6 +24,8 @@ ignores=( target web3.js/test node_modules + jito-programs + anchor ) not_paths=() diff --git a/scripts/run.sh b/scripts/run.sh index a890aa10c1..1b446631a4 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -102,6 +102,11 @@ args=( --identity "$validator_identity" --vote-account "$validator_vote_account" --ledger "$ledgerDir" + --tip-payment-program-pubkey "DThZmRNNXh7kvTQW9hXeGoWGPKktK8pgVAyoTLjH7UrT" + --tip-distribution-program-pubkey "FjrdANjvo76aCYQ4kf9FM1R8aESUcEE6F8V7qyoVUQcM" + --tip-distribution-account-payer "$validator_identity" + --merkle-root-upload-authority "$validator_identity" + --commission-bps 0 --gossip-port 8001 --full-rpc-api --rpc-port 8899 diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 7abcfcc6b1..67c9070e75 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -34,9 +34,11 @@ full = [ "libsecp256k1", "sha3", "digest", + "uuid" ] [dependencies] +anchor-lang = { path = "../anchor/lang" } assert_matches = { version = "1.5.0", optional = true } base64 = "0.13" bincode = "1.3.3" @@ -78,6 +80,7 @@ solana-program = { path = "program", version = "=1.15.0" } solana-sdk-macro = { path = "macro", version = "=1.15.0" } thiserror = "1.0" uriparse = "0.6.4" +uuid = { version = "1.0.0", features = ["v4", "fast-rng"], optional = true } wasm-bindgen = "0.2" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/sdk/src/bundle/error.rs b/sdk/src/bundle/error.rs new file mode 100644 index 0000000000..503446f97e --- /dev/null +++ b/sdk/src/bundle/error.rs @@ -0,0 +1,51 @@ +#![cfg(feature = "full")] + +use { + anchor_lang::error::Error, serde::Deserialize, solana_program::pubkey::Pubkey, + solana_sdk::transaction::TransactionError, std::time::Duration, thiserror::Error, +}; + +#[derive(Error, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum BundleExecutionError { + #[error("PoH max height reached in the middle of a bundle.")] + PohMaxHeightError, + + #[error("A transaction in the bundle failed")] + TransactionFailure(#[from] TransactionError), + + #[error("The bundle exceeds the cost model")] + ExceedsCostModel, + + #[error("Tip error {0}")] + TipError(#[from] TipPaymentError), + + #[error("Shutdown triggered")] + Shutdown, + + #[error("The time spent retrying bundles exceeded the allowed time {0:?}")] + MaxRetriesExceeded(Duration), + + #[error("Error locking bundle because the transaction is malformed")] + LockError, +} + +#[derive(Error, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum TipPaymentError { + #[error("account is missing from bank: {0}")] + AccountMissing(Pubkey), + + #[error("MEV program is non-existent")] + ProgramNonExistent(Pubkey), + + #[error("Anchor error: {0}")] + AnchorError(String), +} + +impl From for TipPaymentError { + fn from(anchor_err: Error) -> Self { + match anchor_err { + Error::AnchorError(e) => Self::AnchorError(e.error_msg), + Error::ProgramError(e) => Self::AnchorError(e.to_string()), + } + } +} diff --git a/sdk/src/bundle/mod.rs b/sdk/src/bundle/mod.rs new file mode 100644 index 0000000000..0ecbaf9e33 --- /dev/null +++ b/sdk/src/bundle/mod.rs @@ -0,0 +1,12 @@ +#![cfg(feature = "full")] + +use crate::transaction::VersionedTransaction; + +pub mod error; +pub mod sanitized; +pub mod utils; + +#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize)] +pub struct VersionedBundle { + pub transactions: Vec, +} diff --git a/sdk/src/bundle/sanitized.rs b/sdk/src/bundle/sanitized.rs new file mode 100644 index 0000000000..e544e03c19 --- /dev/null +++ b/sdk/src/bundle/sanitized.rs @@ -0,0 +1,8 @@ +#![cfg(feature = "full")] + +use solana_sdk::transaction::SanitizedTransaction; + +#[derive(Clone, Debug)] +pub struct SanitizedBundle { + pub transactions: Vec, +} diff --git a/sdk/src/bundle/utils.rs b/sdk/src/bundle/utils.rs new file mode 100644 index 0000000000..04c7446eda --- /dev/null +++ b/sdk/src/bundle/utils.rs @@ -0,0 +1,20 @@ +use {crate::bundle::error::BundleExecutionError, solana_sdk::transaction::TransactionError}; + +type LockResult = Result<(), TransactionError>; + +/// Checks that preparing a bundle gives an acceptable batch back +pub fn check_bundle_lock_results(lock_results: &[LockResult]) -> Option<(TransactionError, usize)> { + for (i, res) in lock_results.iter().enumerate() { + match res { + Ok(()) + | Err(TransactionError::AccountInUse) + | Err(TransactionError::BundleNotContinuous) => {} + Err(e) => { + return Some((e.clone(), i)); + } + } + } + None +} + +pub type BundleExecutionResult = Result; diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index c1a2967ab5..65ff4cda1b 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -26,6 +26,7 @@ pub use solana_program::{ pub mod account; pub mod account_utils; pub mod builtins; +pub mod bundle; pub mod client; pub mod commitment_config; pub mod compute_budget; diff --git a/sdk/src/transaction/error.rs b/sdk/src/transaction/error.rs index 47d5856ce0..01dc4d7765 100644 --- a/sdk/src/transaction/error.rs +++ b/sdk/src/transaction/error.rs @@ -149,6 +149,14 @@ pub enum TransactionError { "Transaction results in an account ({account_index}) with insufficient funds for rent" )] InsufficientFundsForRent { account_index: u8 }, + + /// Bundle is not continuous + #[error("Bundle is not continuous")] + BundleNotContinuous, + + /// This error type should be used when a transaction in a bundle is not executed due to an earlier tx error. + #[error("Transaction did not execute.")] + SkippedExecution, } impl From for TransactionError { diff --git a/send-transaction-service/Cargo.toml b/send-transaction-service/Cargo.toml index 49928b7674..91c520a50d 100644 --- a/send-transaction-service/Cargo.toml +++ b/send-transaction-service/Cargo.toml @@ -12,6 +12,7 @@ edition = "2021" [dependencies] crossbeam-channel = "0.5" log = "0.4.17" +solana-gossip = { path = "../gossip", version = "=1.15.0" } solana-measure = { path = "../measure", version = "=1.15.0" } solana-metrics = { path = "../metrics", version = "=1.15.0" } solana-runtime = { path = "../runtime", version = "=1.15.0" } @@ -20,6 +21,7 @@ solana-tpu-client = { path = "../tpu-client", version = "=1.15.0", default-featu [dev-dependencies] solana-logger = { path = "../logger", version = "=1.15.0" } +solana-streamer = { path = "../streamer", version = "=1.15.0" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/send-transaction-service/src/send_transaction_service.rs b/send-transaction-service/src/send_transaction_service.rs index 16d2001a25..0129926c19 100644 --- a/send-transaction-service/src/send_transaction_service.rs +++ b/send-transaction-service/src/send_transaction_service.rs @@ -2,6 +2,7 @@ use { crate::tpu_info::TpuInfo, crossbeam_channel::{Receiver, RecvTimeoutError}, log::*, + solana_gossip::cluster_info::ClusterInfo, solana_measure::measure::Measure, solana_metrics::datapoint_warn, solana_runtime::{bank::Bank, bank_forks::BankForks}, @@ -325,7 +326,7 @@ const SEND_TRANSACTION_METRICS_REPORT_RATE_MS: u64 = 5000; impl SendTransactionService { pub fn new( - tpu_address: SocketAddr, + cluster_info: Arc, bank_forks: &Arc>, leader_info: Option, receiver: Receiver, @@ -339,7 +340,7 @@ impl SendTransactionService { ..Config::default() }; Self::new_with_config( - tpu_address, + cluster_info, bank_forks, leader_info, receiver, @@ -349,7 +350,7 @@ impl SendTransactionService { } pub fn new_with_config( - tpu_address: SocketAddr, + cluster_info: Arc, bank_forks: &Arc>, leader_info: Option, receiver: Receiver, @@ -364,7 +365,7 @@ impl SendTransactionService { let exit = Arc::new(AtomicBool::new(false)); let receive_txn_thread = Self::receive_txn_thread( - tpu_address, + cluster_info.clone(), receiver, leader_info_provider.clone(), connection_cache.clone(), @@ -375,7 +376,7 @@ impl SendTransactionService { ); let retry_thread = Self::retry_thread( - tpu_address, + cluster_info, bank_forks.clone(), leader_info_provider, connection_cache.clone(), @@ -393,7 +394,7 @@ impl SendTransactionService { /// Thread responsible for receiving transactions from RPC clients. fn receive_txn_thread( - tpu_address: SocketAddr, + cluster_info: Arc, receiver: Receiver, leader_info_provider: Arc>>, connection_cache: Arc, @@ -454,6 +455,7 @@ impl SendTransactionService { stats .sent_transactions .fetch_add(transactions.len() as u64, Ordering::Relaxed); + let tpu_address = cluster_info.my_contact_info().tpu; Self::send_transactions_in_batch( &tpu_address, &mut transactions, @@ -500,7 +502,7 @@ impl SendTransactionService { /// Thread responsible for retrying transactions fn retry_thread( - tpu_address: SocketAddr, + cluster_info: Arc, bank_forks: Arc>, leader_info_provider: Arc>>, connection_cache: Arc, @@ -536,7 +538,7 @@ impl SendTransactionService { bank_forks.working_bank().clone(), ) }; - + let tpu_address = cluster_info.my_contact_info().tpu; let _result = Self::process_transactions( &working_bank, &root_bank, @@ -777,27 +779,40 @@ mod test { super::*, crate::tpu_info::NullTpuInfo, crossbeam_channel::unbounded, + solana_gossip::contact_info::ContactInfo, solana_sdk::{ account::AccountSharedData, genesis_config::create_genesis_config, nonce::{self, state::DurableNonce}, pubkey::Pubkey, - signature::Signer, + signature::{Keypair, Signer}, system_program, system_transaction, }, - std::ops::Sub, + solana_streamer::socket::SocketAddrSpace, + std::{ + net::{IpAddr, Ipv4Addr}, + ops::Sub, + }, }; #[test] fn service_exit() { - let tpu_address = "127.0.0.1:0".parse().unwrap(); let bank = Bank::default_for_tests(); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let (sender, receiver) = unbounded(); let connection_cache = Arc::new(ConnectionCache::default()); + let contact_info = ContactInfo { + tpu: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080), + ..ContactInfo::default() + }; + let cluster_info: Arc = Arc::new(ClusterInfo::new( + contact_info, + Arc::new(Keypair::new()), + SocketAddrSpace::new(false), + )); let send_tranaction_service = SendTransactionService::new::( - tpu_address, + cluster_info, &bank_forks, None, receiver, diff --git a/start b/start new file mode 100755 index 0000000000..be2e1a9a7e --- /dev/null +++ b/start @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +solana_keygen=./target/release/solana-keygen +SOLANA_CONFIG_DIR=./config + +mkdir $SOLANA_CONFIG_DIR + +NDEBUG=1 ./multinode-demo/setup.sh +./target/release/solana-ledger-tool -l config/bootstrap-validator/ create-snapshot 0 +NDEBUG=1 ./multinode-demo/faucet.sh diff --git a/start_multi b/start_multi new file mode 100755 index 0000000000..0083cf363b --- /dev/null +++ b/start_multi @@ -0,0 +1,29 @@ +#!/usr/bin/env sh +solana_keygen=./target/release/solana-keygen +SOLANA_CONFIG_DIR=./config + +mkdir $SOLANA_CONFIG_DIR +if [ $? -eq 0 ] ; then + echo "New Config! Generating Identities" + $solana_keygen new --no-passphrase -so "$SOLANA_CONFIG_DIR"/a/identity.json + $solana_keygen new --no-passphrase -so "$SOLANA_CONFIG_DIR"/a/stake-account.json + $solana_keygen new --no-passphrase -so "$SOLANA_CONFIG_DIR"/a/vote-account.json + + $solana_keygen new --no-passphrase -so "$SOLANA_CONFIG_DIR"/b/identity.json + $solana_keygen new --no-passphrase -so "$SOLANA_CONFIG_DIR"/b/stake-account.json + $solana_keygen new --no-passphrase -so "$SOLANA_CONFIG_DIR"/b/vote-account.json +fi + + +NDEBUG=1 ./multinode-demo/setup.sh \ + --bootstrap-validator \ + "$SOLANA_CONFIG_DIR"/a/identity.json \ + "$SOLANA_CONFIG_DIR"/a/vote-account.json \ + "$SOLANA_CONFIG_DIR"/a/stake-account.json \ + --bootstrap-validator \ + "$SOLANA_CONFIG_DIR"/b/identity.json \ + "$SOLANA_CONFIG_DIR"/b/vote-account.json \ + "$SOLANA_CONFIG_DIR"/b/stake-account.json + +./target/release/solana-ledger-tool -l config/bootstrap-validator/ create-snapshot 0 +NDEBUG=1 ./multinode-demo/faucet.sh diff --git a/storage-proto/proto/transaction_by_addr.proto b/storage-proto/proto/transaction_by_addr.proto index 6a1869fc2c..001a5a73dc 100644 --- a/storage-proto/proto/transaction_by_addr.proto +++ b/storage-proto/proto/transaction_by_addr.proto @@ -57,6 +57,8 @@ enum TransactionErrorType { WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT = 29; DUPLICATE_INSTRUCTION = 30; INSUFFICIENT_FUNDS_FOR_RENT = 31; + BUNDLE_NOT_CONTINUOUS = 32; + SKIPPED_EXECUTION = 33; } message InstructionError { diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 4e2d6ef3df..e38c21dbd5 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -769,6 +769,8 @@ impl TryFrom for TransactionError { 27 => TransactionError::InvalidRentPayingAccount, 28 => TransactionError::WouldExceedMaxVoteCostLimit, 29 => TransactionError::WouldExceedAccountDataTotalLimit, + 32 => TransactionError::BundleNotContinuous, + 33 => TransactionError::SkippedExecution, _ => return Err("Invalid TransactionError"), }) } @@ -872,6 +874,12 @@ impl From for tx_by_addr::TransactionError { TransactionError::InsufficientFundsForRent { .. } => { tx_by_addr::TransactionErrorType::InsufficientFundsForRent } + TransactionError::BundleNotContinuous => { + tx_by_addr::TransactionErrorType::BundleNotContinuous + } + TransactionError::SkippedExecution => { + tx_by_addr::TransactionErrorType::SkippedExecution + } } as i32, instruction_error: match transaction_error { TransactionError::InstructionError(index, ref instruction_error) => { diff --git a/tip-distributor/Cargo.toml b/tip-distributor/Cargo.toml new file mode 100644 index 0000000000..8bd15e9524 --- /dev/null +++ b/tip-distributor/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "solana-tip-distributor" +version = "1.15.0" +edition = "2021" +license = "Apache-2.0" +description = "Collection of binaries used to distribute MEV rewards to delegators and validators." + +[dependencies] +anchor-lang = { path = "../anchor/lang" } +bigdecimal = "0.3.0" +clap = { version = "3.2.5", features = ["derive", "env"] } +env_logger = "0.9.0" +futures = "0.3.21" +im = "15.1.0" +itertools = "0.10.3" +log = "0.4.17" +num-traits = "0.2.15" +serde = "1.0.137" +serde_json = "1.0.81" +solana-client = { path = "../client", version = "=1.15.0" } +solana-genesis-utils = { path = "../genesis-utils", version = "=1.15.0" } +solana-ledger = { path = "../ledger", version = "=1.15.0" } +solana-merkle-tree = { path = "../merkle-tree", version = "=1.15.0" } +solana-program = { path = "../sdk/program", version = "=1.15.0" } +solana-runtime = { path = "../runtime", version = "=1.15.0" } +solana-sdk = { path = "../sdk", version = "=1.15.0" } +solana-stake-program = { path = "../programs/stake", version = "=1.15.0" } +thiserror = "1.0.31" +tip-distribution = { path = "../jito-programs/tip-payment/programs/tip-distribution", features = ["no-entrypoint"] } +tokio = { version = "1.12.0", features = ["rt-multi-thread", "macros", "sync", "time", "full"] } + +[[bin]] +name = "solana-stake-meta-generator" +path = "src/bin/stake-meta-generator.rs" + +[[bin]] +name = "solana-merkle-root-generator" +path = "src/bin/merkle-root-generator.rs" diff --git a/tip-distributor/README.md b/tip-distributor/README.md new file mode 100644 index 0000000000..018cef98ac --- /dev/null +++ b/tip-distributor/README.md @@ -0,0 +1,43 @@ +# Tip Distributor +This library and collection of binaries are responsible for generating and uploading merkle roots to the on-chain +tip-distribution program found [here](https://github.com/jito-labs/jito-programs/blob/a450ef006e60e10894c02269ec8a301b81a083a0/tip-payment/programs/tip-distribution/src/lib.rs). + +## Background +Each individual validator is assigned a new PDA per epoch where their share of tips, in lamports, will be stored. +At the end of the epoch it's expected that validators take a commission and then distribute the rest of the funds +to their delegators such that delegators receive rewards proportional to their respective delegations. The distribution +mechanism is via merkle proofs similar to how airdrops work. + +The merkle roots are calculated off-chain and uploaded to the validator's **TipDistributionAccount** PDA. Validators may +elect an account to upload the merkle roots on their behalf. Once uploaded, users can invoke the **claim** instruction +and receive the rewards they're entitled to. Once all funds are claimed by users the validator can close the account and +refunded the rent. + +## Scripts + +### stake-meta-generator + +This script generates a JSON file identifying individual stake delegations to a validator, along with amount of lamports +in each validator's **TipDistributionAccount**. All validators will be contained in the JSON list, regardless of whether +the validator is a participant in the system; participant being indicative of running the jito-solana client to accept tips +having initialized a **TipDistributionAccount** PDA account for the epoch. + +One edge case that we've taken into account is the last validator in an epoch N receives tips but those tips don't get transferred +out into the PDA until some slot in epoch N + 1. Due to this we cannot rely on the bank's state at epoch N for lamports amount +in the PDAs. We use the bank solely to take a snapshot of delegations, but an RPC node to fetch the PDA lamports for more up-to-date data. + +### merkle-root-generator +This script accepts a path to the above JSON file as one of its arguments, and generates a merkle-root. It'll optionally upload the root +on-chain if specified. Additionally, it'll spit the generated merkle trees out into a JSON file. + +## How it works? +In order to use this library as the merkle root creator one must follow the following steps: +1. Download a ledger snapshot containing the slot of interest, i.e. the last slot in an epoch. The Solana foundation has snapshots that can be found [here](https://console.cloud.google.com/storage/browser/mainnet-beta-ledger-us-ny5). +2. Download the snapshot onto your worker machine (where this script will run). +3. Run `solana-ledger-tool -l ${PATH_TO_LEDGER} create-snapshot ${YOUR_SLOT} ${WHERE_TO_CREATE_SNAPSHOT}` + 1. The snapshot created at `${WHERE_TO_CREATE_SNAPSHOT}` will have the highest slot of `${YOUR_SLOT}`, assuming you downloaded the correct snapshot. +4. Run `stake-meta-generator --ledger-path ${WHERE_TO_CREATE_SNAPSHOT} --tip-distribution-program-id ${PUBKEY} --out-path ${JSON_OUT_PATH} --snapshot-slot ${SLOT} --rpc-url ${URL}` + 1. Note: `${WHERE_TO_CREATE_SNAPSHOT}` must be the same in steps 3 & 4. +5. Run `merkle-root-generator --path-to-my-keypair ${KEYPAIR_PATH} --stake-meta-coll-path ${STAKE_META_COLLECTION_JSON} --rpc-url ${URL} --upload-roots ${BOOL} --force-upload-root ${BOOL}` + +Voila! diff --git a/tip-distributor/src/bin/merkle-root-generator.rs b/tip-distributor/src/bin/merkle-root-generator.rs new file mode 100644 index 0000000000..059ceb644c --- /dev/null +++ b/tip-distributor/src/bin/merkle-root-generator.rs @@ -0,0 +1,60 @@ +//! This binary generates a merkle tree for each [TipDistributionAccount]; they are derived +//! using a user provided [StakeMetaCollection] JSON file. The roots are then uploaded to their +//! their [TipDistributionAccount] as long as the provided keypair is the +//! `merkle_root_upload_authority`. All funds minus the validator's commission are distributed +//! to delegated accounts proportional to their amounts delegated. + +use { + clap::Parser, log::*, solana_sdk::signature::read_keypair_file, + solana_tip_distributor::merkle_root_generator_workflow::run_workflow, std::path::PathBuf, +}; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// The path to the keypair used to sign and pay for the `upload_merkle_root` transactions. + /// This will skip uploading to accounts where `merkle_root_upload_authority` does not match this keypair. + #[clap(long, env)] + path_to_my_keypair: PathBuf, + + /// Path to JSON file containing the [StakeMetaCollection] object. + #[clap(long, env)] + stake_meta_coll_path: PathBuf, + + /// The RPC to send transactions to. + #[clap(long, env)] + rpc_url: String, + + /// Path to JSON file to get populated with tree node data. + #[clap(long, env)] + out_path: String, + + /// Indicates whether or not to actually upload roots to their respective + /// [TipDistributionAccount]'s + #[clap(long, env)] + upload_roots: bool, + + /// If true then this will upload the merkle-root disregarding whether one's been uploaded already. + #[clap(long, env)] + force_upload_root: Option, +} + +fn main() { + env_logger::init(); + info!("Starting merkle-root-generator workflow..."); + + let args: Args = Args::parse(); + + let my_keypair = + read_keypair_file(&args.path_to_my_keypair).expect("Failed to read keypair file."); + + run_workflow( + args.stake_meta_coll_path, + args.out_path, + args.rpc_url, + my_keypair, + args.upload_roots, + args.force_upload_root.unwrap_or_default(), + ) + .unwrap(); +} diff --git a/tip-distributor/src/bin/stake-meta-generator.rs b/tip-distributor/src/bin/stake-meta-generator.rs new file mode 100644 index 0000000000..780afd7ede --- /dev/null +++ b/tip-distributor/src/bin/stake-meta-generator.rs @@ -0,0 +1,67 @@ +//! This binary is responsible for generating a JSON file that contains meta-data about stake +//! & delegations given a ledger snapshot directory. The JSON file is structured as an array +//! of [StakeMeta] objects. + +use { + clap::Parser, + log::*, + solana_client::rpc_client::RpcClient, + solana_sdk::{clock::Slot, pubkey::Pubkey}, + solana_tip_distributor::{self, stake_meta_generator_workflow::run_workflow}, + std::{ + fs::{self}, + path::PathBuf, + process::exit, + }, +}; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Ledger path, where you created the snapshot. + #[clap(long, env, parse(try_from_str = Args::ledger_path_parser))] + ledger_path: PathBuf, + + /// The tip-distribution program id. + #[clap(long, env)] + tip_distribution_program_id: Pubkey, + + /// Path to JSON file populated with the [StakeMetaCollection] object. + #[clap(long, env)] + out_path: String, + + /// The expected snapshot slot. + #[clap(long, env)] + snapshot_slot: Slot, + + /// The RPC to fetch lamports from for the tip distribution accounts. + #[clap(long, env)] + rpc_url: String, +} + +impl Args { + fn ledger_path_parser(ledger_path: &str) -> Result { + Ok(fs::canonicalize(ledger_path).unwrap_or_else(|err| { + error!("Unable to access ledger path '{}': {}", ledger_path, err); + exit(1); + })) + } +} + +fn main() { + env_logger::init(); + info!("Starting stake-meta-generator..."); + + let args: Args = Args::parse(); + + let rpc_client = RpcClient::new(args.rpc_url); + + run_workflow( + &args.ledger_path, + args.snapshot_slot, + args.tip_distribution_program_id, + args.out_path, + rpc_client, + ) + .expect("Workflow failed."); +} diff --git a/tip-distributor/src/lib.rs b/tip-distributor/src/lib.rs new file mode 100644 index 0000000000..6e29ca132c --- /dev/null +++ b/tip-distributor/src/lib.rs @@ -0,0 +1,706 @@ +pub mod merkle_root_generator_workflow; +pub mod stake_meta_generator_workflow; + +use { + crate::{ + merkle_root_generator_workflow::Error, + stake_meta_generator_workflow::Error::CheckedMathError, + }, + anchor_lang::AccountDeserialize, + bigdecimal::{num_bigint::BigUint, BigDecimal}, + log::*, + num_traits::{CheckedDiv, CheckedMul, ToPrimitive}, + serde::{Deserialize, Serialize}, + solana_client::rpc_client::RpcClient, + solana_merkle_tree::MerkleTree, + solana_runtime::bank::Bank, + solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, + bs58, + clock::Slot, + hash::{Hash, Hasher}, + pubkey::Pubkey, + stake_history::Epoch, + }, + std::{ + ops::{Div, Mul}, + sync::Arc, + }, + tip_distribution::state::TipDistributionAccount, +}; + +#[derive(Deserialize, Serialize, Debug)] +pub struct GeneratedMerkleTreeCollection { + pub generated_merkle_trees: Vec, + pub bank_hash: String, + pub epoch: Epoch, + pub slot: Slot, +} + +#[derive(Eq, Debug, Hash, PartialEq, Deserialize, Serialize)] +pub struct GeneratedMerkleTree { + pub tip_distribution_account: Pubkey, + #[serde(skip_serializing, skip_deserializing)] + pub merkle_tree: MerkleTree, + pub tree_nodes: Vec, + pub max_total_claim: u64, + pub max_num_nodes: u64, +} + +impl GeneratedMerkleTreeCollection { + pub fn new_from_stake_meta_collection( + stake_meta_coll: StakeMetaCollection, + merkle_root_upload_authority: Pubkey, + ) -> Result { + let b58_merkle_root_upload_authority = + bs58::encode(merkle_root_upload_authority.as_ref()).into_string(); + let generated_merkle_trees = stake_meta_coll + .stake_metas + .into_iter() + .filter(|stake_meta| { + if let Some(tip_distribution_meta) = stake_meta.maybe_tip_distribution_meta.as_ref() + { + tip_distribution_meta.merkle_root_upload_authority + == b58_merkle_root_upload_authority + } else { + false + } + }) + .filter_map(|stake_meta| { + // Build the tree + let mut tree_nodes = match TreeNode::vec_from_stake_meta(&stake_meta) { + Err(e) => return Some(Err(e)), + Ok(maybe_tree_nodes) => maybe_tree_nodes, + }?; + + // Hash the nodes + let hashed_nodes: Vec<[u8; 32]> = + tree_nodes.iter().map(|n| n.hash().to_bytes()).collect(); + + let tip_distribution_meta = stake_meta.maybe_tip_distribution_meta.unwrap(); + + let tip_distribution_account = + match tip_distribution_meta.tip_distribution_account.parse() { + Err(_) => return Some(Err(Error::Base58DecodeError)), + Ok(tip_distribution_account) => tip_distribution_account, + }; + + let merkle_tree = MerkleTree::new(&hashed_nodes[..], true); + let max_num_nodes = tree_nodes.len() as u64; + + for (i, tree_node) in tree_nodes.iter_mut().enumerate() { + tree_node.proof = Some(get_proof(&merkle_tree, i)); + } + + Some(Ok(GeneratedMerkleTree { + max_num_nodes, + tip_distribution_account, + merkle_tree, + tree_nodes, + max_total_claim: tip_distribution_meta.total_tips, + })) + }) + .collect::, Error>>()?; + + Ok(GeneratedMerkleTreeCollection { + generated_merkle_trees, + bank_hash: stake_meta_coll.bank_hash, + epoch: stake_meta_coll.epoch, + slot: stake_meta_coll.slot, + }) + } +} + +pub fn get_proof(merkle_tree: &MerkleTree, i: usize) -> Vec<[u8; 32]> { + let mut proof = Vec::new(); + let path = merkle_tree.find_path(i).expect("path to index"); + for branch in path.get_proof_entries() { + if let Some(hash) = branch.get_left_sibling() { + proof.push(hash.to_bytes()); + } else if let Some(hash) = branch.get_right_sibling() { + proof.push(hash.to_bytes()); + } else { + panic!("expected some hash at each level of the tree"); + } + } + proof +} + +#[derive(Clone, Eq, Debug, Hash, PartialEq, Deserialize, Serialize)] +pub struct TreeNode { + /// The account entitled to redeem. + pub claimant: Pubkey, + + /// The amount this account is entitled to. + pub amount: u64, + + /// The proof associated with this TreeNode + pub proof: Option>, +} + +impl TreeNode { + fn vec_from_stake_meta(stake_meta: &StakeMeta) -> Result>, Error> { + let validator_vote_account = stake_meta + .validator_vote_account + .parse() + .map_err(|_| Error::Base58DecodeError)?; + if let Some(tip_distribution_meta) = stake_meta.maybe_tip_distribution_meta.as_ref() { + let validator_fee = calc_validator_fee( + tip_distribution_meta.total_tips, + tip_distribution_meta.validator_fee_bps, + ); + let mut tree_nodes = vec![TreeNode { + claimant: validator_vote_account, + amount: validator_fee, + proof: None, + }]; + + let remaining_tips = tip_distribution_meta + .total_tips + .checked_sub(validator_fee) + .unwrap(); + + // The theoretically smallest weight an account can have is (1 / SOL_TOTAL_SUPPLY_IN_LAMPORTS) + // where we round SOL_TOTAL_SUPPLY is rounded to 500_000_000. We use u64::MAX. This gives a reasonable + // guarantee that everyone gets paid out regardless of weight, as long as some non-zero amount of + // lamports were delegated. + let uint_precision_multiplier = BigUint::from(u64::MAX); + let f64_precision_multiplier = BigDecimal::try_from(u64::MAX as f64).unwrap(); + + let total_delegated = BigDecimal::try_from(stake_meta.total_delegated as f64) + .expect("failed to convert total_delegated to BigDecimal"); + tree_nodes.extend(stake_meta + .delegations + .iter() + .map(|delegation| { + // TODO(seg): Check this math! + let amount_delegated = BigDecimal::try_from(delegation.amount_delegated as f64) + .expect(&*format!( + "failed to convert amount_delegated to BigDecimal [stake_account={}, amount_delegated={}]", + delegation.stake_account, + delegation.amount_delegated, + )); + let mut weight = amount_delegated.div(&total_delegated); + + let use_multiplier = weight < f64_precision_multiplier; + + if use_multiplier { + weight = weight.mul(&f64_precision_multiplier); + } + + let truncated_weight = weight.to_u128() + .expect(&*format!("failed to convert weight to u128 [stake_account={}, weight={}]", delegation.stake_account, weight)); + let truncated_weight = BigUint::from(truncated_weight); + + let mut amount = truncated_weight + .checked_mul(&BigUint::from(remaining_tips)) + .unwrap(); + + if use_multiplier { + amount = amount.checked_div(&uint_precision_multiplier).unwrap(); + } + + Ok(TreeNode { + claimant: delegation.stake_account.parse().map_err(|_| Error::Base58DecodeError)?, + amount: amount.to_u64().unwrap(), + proof: None + }) + }) + .collect::, Error>>()?); + + let total_claim_amount = tree_nodes.iter().fold(0u64, |sum, tree_node| { + sum.checked_add(tree_node.amount).unwrap() + }); + assert!(total_claim_amount < stake_meta.total_delegated); + + Ok(Some(tree_nodes)) + } else { + Ok(None) + } + } + + fn hash(&self) -> Hash { + let mut hasher = Hasher::default(); + hasher.hash(self.claimant.as_ref()); + hasher.hash(self.amount.to_le_bytes().as_ref()); + hasher.result() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct StakeMetaCollection { + /// List of [StakeMeta]. + pub stake_metas: Vec, + + /// base58 encoded tip-distribution program id. + pub tip_distribution_program_id: String, + + /// Base58 encoded bank hash this object was generated at. + pub bank_hash: String, + + /// Epoch for which this object was generated for. + pub epoch: Epoch, + + /// Slot at which this object was generated. + pub slot: Slot, +} + +#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct StakeMeta { + /// The validator's base58 encoded vote account. + pub validator_vote_account: String, + + /// The validator's tip-distribution meta if it exists. + pub maybe_tip_distribution_meta: Option, + + /// Delegations to this validator. + pub delegations: Vec, + + /// The total amount of delegations to the validator. + pub total_delegated: u64, + + /// The validator's delegation commission rate as a percentage between 0-100. + pub commission: u8, +} + +#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct TipDistributionMeta { + /// The account authorized to generate and upload a merkle_root for the validator. + pub merkle_root_upload_authority: String, + + /// The validator's base58 encoded [TipDistributionAccount]. + pub tip_distribution_account: String, + + /// The validator's total tips in the [TipDistributionAccount]. + pub total_tips: u64, + + /// The validator's cut of tips from [TipDistributionAccount], calculated from the on-chain + /// commission fee bps. + pub validator_fee_bps: u16, +} + +impl TipDistributionMeta { + fn from_tda_wrapper( + tda_wrapper: TipDistributionAccountWrapper, + // The amount that will be left remaining in the tda to maintain rent exemption status. + rent_exempt_amount: u64, + ) -> Result { + Ok(TipDistributionMeta { + tip_distribution_account: bs58::encode(tda_wrapper.tip_distribution_account_pubkey) + .into_string(), + total_tips: tda_wrapper + .account_data + .lamports() + .checked_sub(rent_exempt_amount) + .ok_or(CheckedMathError)?, + validator_fee_bps: tda_wrapper + .tip_distribution_account + .validator_commission_bps, + merkle_root_upload_authority: bs58::encode( + tda_wrapper + .tip_distribution_account + .merkle_root_upload_authority, + ) + .into_string(), + }) + } +} + +#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct Delegation { + /// The stake account of interest base58 encoded. + pub stake_account: String, + + /// Amount delegated by this account. + pub amount_delegated: u64, +} + +/// Convenience wrapper around [TipDistributionAccount] +pub struct TipDistributionAccountWrapper { + pub tip_distribution_account: TipDistributionAccount, + pub account_data: AccountSharedData, + pub tip_distribution_account_pubkey: Pubkey, +} + +// TODO: move to program's sdk +pub fn derive_tip_distribution_account_address( + tip_distribution_program_id: &Pubkey, + vote_pubkey: &Pubkey, + epoch: Epoch, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + TipDistributionAccount::SEED, + vote_pubkey.to_bytes().as_ref(), + epoch.to_le_bytes().as_ref(), + ], + tip_distribution_program_id, + ) +} + +pub trait AccountFetcher { + fn fetch_account( + &self, + pubkey: &Pubkey, + ) -> Result, stake_meta_generator_workflow::Error>; +} + +/// Fetches and deserializes the vote_pubkey's corresponding [TipDistributionAccount]. +pub fn fetch_and_deserialize_tip_distribution_account( + account_fetcher: Arc>, + vote_pubkey: &Pubkey, + tip_distribution_program_id: &Pubkey, + epoch: Epoch, +) -> Result, stake_meta_generator_workflow::Error> { + let tip_distribution_account_pubkey = + derive_tip_distribution_account_address(tip_distribution_program_id, vote_pubkey, epoch).0; + + match account_fetcher.fetch_account(&tip_distribution_account_pubkey)? { + None => { + warn!( + "TipDistributionAccount not found for vote_pubkey {}, epoch {}, tip_distribution_account_pubkey {}", + vote_pubkey, + epoch, + tip_distribution_account_pubkey, + ); + Ok(None) + } + Some(account_data) => Ok(Some(TipDistributionAccountWrapper { + tip_distribution_account: TipDistributionAccount::try_deserialize( + &mut account_data.data(), + )?, + account_data, + tip_distribution_account_pubkey, + })), + } +} + +struct BankAccountFetcher { + bank: Arc, +} + +impl AccountFetcher for BankAccountFetcher { + /// Fetches the vote_pubkey's corresponding [TipDistributionAccount] from the accounts DB. + fn fetch_account( + &self, + pubkey: &Pubkey, + ) -> Result, stake_meta_generator_workflow::Error> { + Ok(self.bank.get_account(pubkey)) + } +} + +struct RpcAccountFetcher { + rpc_client: RpcClient, +} + +impl AccountFetcher for RpcAccountFetcher { + /// Fetches the vote_pubkey's corresponding [TipDistributionAccount] from an RPC node. + fn fetch_account( + &self, + pubkey: &Pubkey, + ) -> Result, stake_meta_generator_workflow::Error> { + match self + .rpc_client + .get_account_with_commitment(pubkey, self.rpc_client.commitment()) + { + Ok(resp) => Ok(resp.value.map(|a| a.into())), + Err(e) => { + error!("error fetching account {}", e); + Err(e.into()) + } + } + } +} + +/// Calculate validator fee denominated in lamports +pub fn calc_validator_fee(total_tips: u64, validator_commission_bps: u16) -> u64 { + let validator_commission_rate = + math::fee_tenth_of_bps(((validator_commission_bps as u64).checked_mul(10).unwrap()) as u64); + let validator_fee: math::U64F64 = validator_commission_rate.mul_u64(total_tips); + + validator_fee + .floor() + .checked_add((validator_fee.frac_part() != 0) as u64) + .unwrap() +} + +mod math { + /// copy-pasta from [here](https://github.com/project-serum/serum-dex/blob/e00bb9e6dac0a1fff295acb034722be9afc1eba3/dex/src/fees.rs#L43) + #[repr(transparent)] + #[derive(Copy, Clone)] + pub(crate) struct U64F64(u128); + + #[allow(dead_code)] + impl U64F64 { + const ONE: Self = U64F64(1 << 64); + + pub(crate) fn add(self, other: U64F64) -> U64F64 { + U64F64(self.0.checked_add(other.0).unwrap()) + } + + pub(crate) fn div(self, other: U64F64) -> u128 { + self.0.checked_div(other.0).unwrap() + } + + pub(crate) fn mul_u64(self, other: u64) -> U64F64 { + U64F64(self.0.checked_mul(other as u128).unwrap()) + } + + /// right shift 64 + pub(crate) fn floor(self) -> u64 { + (self.0.checked_div(2u128.checked_pow(64).unwrap()).unwrap()) as u64 + } + + pub(crate) fn frac_part(self) -> u64 { + self.0 as u64 + } + + /// left shift 64 + pub(crate) fn from_int(n: u64) -> Self { + U64F64( + (n as u128) + .checked_mul(2u128.checked_pow(64).unwrap()) + .unwrap(), + ) + } + } + + pub(crate) fn fee_tenth_of_bps(tenth_of_bps: u64) -> U64F64 { + U64F64( + ((tenth_of_bps as u128) + .checked_mul(2u128.checked_pow(64).unwrap()) + .unwrap()) + .checked_div(100_000) + .unwrap(), + ) + } +} + +#[cfg(test)] +mod tests { + use {super::*, solana_sdk::bs58, tip_distribution::merkle_proof}; + + #[test] + fn test_merkle_tree_verify() { + // Create the merkle tree and proofs + let acct_0 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + let acct_1 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + + let tree_nodes = vec![ + TreeNode { + claimant: acct_0.parse().unwrap(), + amount: 151_507, + proof: None, + }, + TreeNode { + claimant: acct_1.parse().unwrap(), + amount: 176_624, + proof: None, + }, + ]; + + // First the nodes are hashed and merkle tree constructed + let hashed_nodes: Vec<[u8; 32]> = tree_nodes.iter().map(|n| n.hash().to_bytes()).collect(); + let mk = MerkleTree::new(&hashed_nodes[..], true); + let root = mk.get_root().expect("to have valid root").to_bytes(); + + // verify first node + let node = solana_program::hash::hashv(&[&[0u8], &hashed_nodes[0]]); + let proof = get_proof(&mk, 0); + assert!(merkle_proof::verify(proof, root, node.to_bytes())); + + // verify second node + let node = solana_program::hash::hashv(&[&[0u8], &hashed_nodes[1]]); + let proof = get_proof(&mk, 1); + assert!(merkle_proof::verify(proof, root, node.to_bytes())); + } + + #[test] + fn test_new_from_stake_meta_collection_happy_path() { + let b58_merkle_root_upload_authority = + bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + + let (tda_0, tda_1) = ( + bs58::encode(Pubkey::new_unique().as_ref()).into_string(), + bs58::encode(Pubkey::new_unique().as_ref()).into_string(), + ); + + let stake_account_0 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + let stake_account_1 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + let stake_account_2 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + let stake_account_3 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + + let validator_vote_account_0 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + let validator_vote_account_1 = bs58::encode(Pubkey::new_unique().as_ref()).into_string(); + + println!("test stake_account {}", stake_account_0); + println!("test stake_account {}", stake_account_1); + println!("test stake_account {}", stake_account_2); + println!("test stake_account {}", stake_account_3); + + let stake_meta_collection = StakeMetaCollection { + stake_metas: vec![ + StakeMeta { + validator_vote_account: validator_vote_account_0.clone(), + maybe_tip_distribution_meta: Some(TipDistributionMeta { + merkle_root_upload_authority: b58_merkle_root_upload_authority.clone(), + tip_distribution_account: tda_0.clone(), + total_tips: 1_900_122_111_000, + validator_fee_bps: 100, + }), + delegations: vec![ + Delegation { + stake_account: stake_account_0.clone(), + amount_delegated: 123_999_123_555, + }, + Delegation { + stake_account: stake_account_1.clone(), + amount_delegated: 144_555_444_556, + }, + ], + total_delegated: 1_555_123_000_333_454_000, + commission: 100, + }, + StakeMeta { + validator_vote_account: validator_vote_account_1.clone(), + maybe_tip_distribution_meta: Some(TipDistributionMeta { + merkle_root_upload_authority: b58_merkle_root_upload_authority.clone(), + tip_distribution_account: tda_1.clone(), + total_tips: 1_900_122_111_333, + validator_fee_bps: 200, + }), + delegations: vec![ + Delegation { + stake_account: stake_account_2.clone(), + amount_delegated: 224_555_444, + }, + Delegation { + stake_account: stake_account_3.clone(), + amount_delegated: 700_888_944_555, + }, + ], + total_delegated: 2_565_318_909_444_123, + commission: 10, + }, + ], + tip_distribution_program_id: bs58::encode(Pubkey::new_unique().as_ref()).into_string(), + bank_hash: solana_sdk::hash::Hash::new_unique().to_string(), + epoch: 100, + slot: 2_000_000, + }; + + let merkle_tree_collection = GeneratedMerkleTreeCollection::new_from_stake_meta_collection( + stake_meta_collection.clone(), + b58_merkle_root_upload_authority.parse().unwrap(), + ) + .unwrap(); + + assert_eq!(stake_meta_collection.epoch, merkle_tree_collection.epoch); + assert_eq!( + stake_meta_collection.bank_hash, + merkle_tree_collection.bank_hash + ); + assert_eq!(stake_meta_collection.slot, merkle_tree_collection.slot); + assert_eq!( + stake_meta_collection.stake_metas.len(), + merkle_tree_collection.generated_merkle_trees.len() + ); + + let tree_nodes = vec![ + TreeNode { + claimant: validator_vote_account_0.parse().unwrap(), + amount: 19_001_221_110, + proof: None, + }, + TreeNode { + claimant: stake_account_0.parse().unwrap(), + amount: 149_992, + proof: None, + }, + TreeNode { + claimant: stake_account_1.parse().unwrap(), + amount: 174_858, + proof: None, + }, + ]; + let hashed_nodes: Vec<[u8; 32]> = tree_nodes.iter().map(|n| n.hash().to_bytes()).collect(); + let gmt_0 = GeneratedMerkleTree { + tip_distribution_account: tda_0.parse().unwrap(), + merkle_tree: MerkleTree::new(&hashed_nodes[..], true), + tree_nodes, + max_total_claim: stake_meta_collection.stake_metas[0] + .clone() + .maybe_tip_distribution_meta + .unwrap() + .total_tips, + max_num_nodes: 3, + }; + + let tree_nodes = vec![ + TreeNode { + claimant: validator_vote_account_1.parse().unwrap(), + amount: 38_002_442_227, + proof: None, + }, + TreeNode { + claimant: stake_account_2.parse().unwrap(), + amount: 163_000, + proof: None, + }, + TreeNode { + claimant: stake_account_3.parse().unwrap(), + amount: 508_762_900, + proof: None, + }, + ]; + let hashed_nodes: Vec<[u8; 32]> = tree_nodes.iter().map(|n| n.hash().to_bytes()).collect(); + let gmt_1 = GeneratedMerkleTree { + tip_distribution_account: tda_1.parse().unwrap(), + merkle_tree: MerkleTree::new(&hashed_nodes[..], true), + tree_nodes, + max_total_claim: stake_meta_collection.stake_metas[1] + .clone() + .maybe_tip_distribution_meta + .unwrap() + .total_tips, + max_num_nodes: 3, + }; + + let expected_generated_merkle_trees = vec![gmt_0, gmt_1]; + let actual_generated_merkle_trees = merkle_tree_collection.generated_merkle_trees; + + expected_generated_merkle_trees + .iter() + .for_each(|expected_gmt| { + let actual_gmt = actual_generated_merkle_trees + .iter() + .find(|gmt| { + gmt.tip_distribution_account == expected_gmt.tip_distribution_account + }) + .unwrap(); + + assert_eq!(expected_gmt.max_num_nodes, actual_gmt.max_num_nodes); + assert_eq!(expected_gmt.max_total_claim, actual_gmt.max_total_claim); + assert_eq!( + expected_gmt.tip_distribution_account, + actual_gmt.tip_distribution_account + ); + assert_eq!(expected_gmt.tree_nodes.len(), actual_gmt.tree_nodes.len()); + expected_gmt + .tree_nodes + .iter() + .for_each(|expected_tree_node| { + let actual_tree_node = actual_gmt + .tree_nodes + .iter() + .find(|tree_node| tree_node.claimant == expected_tree_node.claimant) + .unwrap(); + assert_eq!(expected_tree_node.amount, actual_tree_node.amount); + }); + assert_eq!( + expected_gmt.merkle_tree.get_root().unwrap(), + actual_gmt.merkle_tree.get_root().unwrap() + ); + }); + } +} diff --git a/tip-distributor/src/merkle_root_generator_workflow.rs b/tip-distributor/src/merkle_root_generator_workflow.rs new file mode 100644 index 0000000000..d5d2fb811c --- /dev/null +++ b/tip-distributor/src/merkle_root_generator_workflow.rs @@ -0,0 +1,262 @@ +use { + crate::{GeneratedMerkleTreeCollection, StakeMetaCollection}, + anchor_lang::AccountDeserialize, + log::*, + solana_client::nonblocking::rpc_client::RpcClient, + solana_sdk::{ + commitment_config::{CommitmentConfig, CommitmentLevel}, + pubkey::Pubkey, + signature::{Keypair, Signature}, + signer::Signer, + transaction::Transaction, + }, + std::{ + fmt::{Debug, Display, Formatter}, + fs::File, + io::{BufReader, BufWriter, Write}, + path::PathBuf, + sync::Arc, + thread::sleep, + time::Duration, + }, + thiserror::Error as ThisError, + tip_distribution::{ + sdk::instruction::{upload_merkle_root_ix, UploadMerkleRootAccounts, UploadMerkleRootArgs}, + state::{Config, TipDistributionAccount}, + }, + tokio::{ + runtime::{Builder, Runtime}, + task::JoinHandle, + }, +}; + +#[derive(ThisError, Debug)] +pub enum Error { + Base58DecodeError, + + #[error(transparent)] + IoError(#[from] std::io::Error), + + #[error(transparent)] + RpcError(#[from] Box), + + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + + SnapshotSlotNotFound, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self, f) + } +} + +pub fn run_workflow( + stake_meta_coll_path: PathBuf, + out_path: String, + rpc_url: String, + my_keypair: Keypair, + should_upload_roots: bool, + // If true then uploads the merkle-roots disregarding if one already has been uploaded. + force_upload_roots: bool, +) -> Result<(), Error> { + let stake_meta_coll = read_stake_meta_collection(stake_meta_coll_path)?; + + let merkle_tree_coll = GeneratedMerkleTreeCollection::new_from_stake_meta_collection( + stake_meta_coll.clone(), + my_keypair.pubkey(), + )?; + + write_to_json_file(&merkle_tree_coll, out_path)?; + + if should_upload_roots { + let tip_distribution_program_id = stake_meta_coll + .tip_distribution_program_id + .parse() + .map_err(|_| Error::Base58DecodeError)?; + + let rpc_client = Arc::new(RpcClient::new_with_commitment( + rpc_url, + CommitmentConfig { + commitment: CommitmentLevel::Finalized, + }, + )); + let (config, _) = + Pubkey::find_program_address(&[Config::SEED], &tip_distribution_program_id); + + let recent_blockhash = { + let rpc_client = rpc_client.clone(); + let rt = tokio::runtime::Runtime::new().unwrap(); + // TODO(seg): is there a possibility this goes stale while synchronously executing transactions? + rt.block_on(async move { rpc_client.get_latest_blockhash().await }) + } + .map_err(Box::new)?; + + upload_roots( + rpc_client, + merkle_tree_coll, + tip_distribution_program_id, + config, + my_keypair, + recent_blockhash, + force_upload_roots, + ); + } + + Ok(()) +} + +fn read_stake_meta_collection(path: PathBuf) -> Result { + let file = File::open(path).unwrap(); + let reader = BufReader::new(file); + serde_json::from_reader(reader).map_err(|e| e.into()) +} + +fn write_to_json_file( + merkle_tree_coll: &GeneratedMerkleTreeCollection, + file_path: String, +) -> Result<(), Error> { + let file = File::create(file_path)?; + let mut writer = BufWriter::new(file); + serde_json::to_writer(&mut writer, merkle_tree_coll)?; + writer.flush()?; + + Ok(()) +} + +// TODO: Check root not already uploaded or force with a flag. +fn upload_roots( + rpc_client: Arc, + merkle_tree_coll: GeneratedMerkleTreeCollection, + tip_distribution_program_id: Pubkey, + config: Pubkey, + my_keypair: Keypair, + recent_blockhash: solana_sdk::hash::Hash, + force_upload_roots: bool, +) { + let rt = Runtime::new().unwrap(); + + let txs = merkle_tree_coll + .generated_merkle_trees + .into_iter() + .filter(|gmt| { + if !force_upload_roots { + let account = rt + .block_on(rpc_client.get_account(&gmt.tip_distribution_account)) + .expect(&*format!( + "failed to get tip_distribution_account {}", + gmt.tip_distribution_account + )); + let mut data = account.data.as_slice(); + let fetched_tip_distribution_account = + TipDistributionAccount::try_deserialize(&mut data) + .expect("failed to deserialize tip_distribution_account state"); + + fetched_tip_distribution_account.merkle_root.is_none() + } else { + true + } + }) + .map(|gmt| { + // Get root. + let root = gmt + .merkle_tree + .get_root() + .expect(&*format!( + "Failed to get merkle root for TipDistributionAccount {}", + gmt.tip_distribution_account + )) + .to_bytes(); + + // Create instruction. + let ix = upload_merkle_root_ix( + tip_distribution_program_id, + UploadMerkleRootArgs { + root, + max_total_claim: gmt.max_total_claim, + max_num_nodes: gmt.max_num_nodes, + }, + UploadMerkleRootAccounts { + config, + merkle_root_upload_authority: my_keypair.pubkey(), + tip_distribution_account: gmt.tip_distribution_account, + }, + ); + + // Sign and return transaction. + Transaction::new_signed_with_payer( + &[ix], + Some(&my_keypair.pubkey()), + &[&my_keypair], + recent_blockhash, + ) + }) + .collect::>(); + + if !txs.is_empty() { + execute_transactions(rpc_client, txs); + } else { + error!("No transactions to execute, use --force-upload-root to reupload roots"); + } +} + +fn execute_transactions(rpc_client: Arc, txs: Vec) { + const DELAY: Duration = Duration::from_millis(500); + const MAX_RETRIES: usize = 5; + + let rt = Builder::new_multi_thread() + .worker_threads(txs.len().min(20)) + .thread_name("execute_transactions") + .enable_time() + .enable_io() + .build() + .unwrap(); + + let hdls = txs + .into_iter() + .map(|tx| { + let rpc_client = rpc_client.clone(); + rt.spawn(async move { + if let Err(e) = + send_transaction_with_retry(rpc_client, &tx, DELAY, MAX_RETRIES).await + { + error!( + "error sending transaction [signature={}, error={}]", + tx.signatures[0], e + ); + } else { + info!( + "successfully sent transaction: [signature={}]", + tx.signatures[0] + ); + } + }) + }) + .collect::>>(); + + rt.block_on(async { futures::future::join_all(hdls).await }); +} + +async fn send_transaction_with_retry( + rpc_client: Arc, + tx: &Transaction, + delay: Duration, + max_retries: usize, +) -> solana_client::client_error::Result { + let mut retry_count: usize = 0; + loop { + match rpc_client.send_and_confirm_transaction(tx).await { + Ok(sig) => { + return Ok(sig); + } + Err(e) => { + retry_count = retry_count.checked_add(1).unwrap(); + if retry_count == max_retries { + return Err(e); + } + sleep(delay); + } + } + } +} diff --git a/tip-distributor/src/stake_meta_generator_workflow.rs b/tip-distributor/src/stake_meta_generator_workflow.rs new file mode 100644 index 0000000000..0127e24c55 --- /dev/null +++ b/tip-distributor/src/stake_meta_generator_workflow.rs @@ -0,0 +1,788 @@ +use { + crate::{ + fetch_and_deserialize_tip_distribution_account, AccountFetcher, BankAccountFetcher, + RpcAccountFetcher, StakeMeta, StakeMetaCollection, TipDistributionAccountWrapper, + TipDistributionMeta, + }, + itertools::Itertools, + log::*, + solana_client::{client_error::ClientError, rpc_client::RpcClient}, + solana_ledger::{ + bank_forks_utils, + blockstore::BlockstoreError, + blockstore_processor::{BlockstoreProcessorError, ProcessOptions}, + }, + solana_runtime::{ + bank::Bank, + hardened_unpack::{open_genesis_config, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE}, + snapshot_config::SnapshotConfig, + stakes::StakeAccount, + vote_account::VoteAccount, + }, + solana_sdk::{ + account::ReadableAccount, + bs58, + clock::{Epoch, Slot}, + pubkey::Pubkey, + }, + std::{ + collections::HashMap, + fmt::{Debug, Display, Formatter}, + fs::File, + io::{BufWriter, Write}, + path::{Path, PathBuf}, + sync::{atomic::AtomicBool, Arc}, + }, + thiserror::Error as ThisError, +}; + +#[derive(ThisError, Debug)] +pub enum Error { + #[error(transparent)] + AnchorError(#[from] anchor_lang::error::Error), + + #[error(transparent)] + BlockstoreError(#[from] BlockstoreError), + + #[error(transparent)] + BlockstoreProcessorError(#[from] BlockstoreProcessorError), + + #[error(transparent)] + IoError(#[from] std::io::Error), + + CheckedMathError, + + #[error(transparent)] + RpcError(#[from] ClientError), + + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + + SnapshotSlotNotFound, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self, f) + } +} + +/// Runs the entire workflow of creating a bank from a snapshot to writing stake meta-data +/// to a JSON file. +pub fn run_workflow( + ledger_path: &Path, + snapshot_slot: Slot, + tip_distribution_program_id: Pubkey, + out_path: String, + rpc_client: RpcClient, +) -> Result<(), Error> { + info!("Creating bank from ledger path..."); + let bank = create_bank_from_snapshot(ledger_path, snapshot_slot)?; + + info!("Generating stake_meta_collection object..."); + let stake_meta_coll = + generate_stake_meta_collection(&bank, tip_distribution_program_id, Some(rpc_client))?; + + info!("Writing stake_meta_collection to JSON {}...", out_path); + write_to_json_file(&stake_meta_coll, out_path)?; + + Ok(()) +} + +fn create_bank_from_snapshot(ledger_path: &Path, snapshot_slot: Slot) -> Result, Error> { + let genesis_config = open_genesis_config(ledger_path, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE); + let snapshot_config = SnapshotConfig { + full_snapshot_archive_interval_slots: Slot::MAX, + incremental_snapshot_archive_interval_slots: Slot::MAX, + full_snapshot_archives_dir: PathBuf::from(ledger_path), + incremental_snapshot_archives_dir: PathBuf::from(ledger_path), + bank_snapshots_dir: PathBuf::from(ledger_path), + ..SnapshotConfig::default() + }; + let (bank_forks, _snapshot_hashes) = bank_forks_utils::bank_forks_from_snapshot( + &genesis_config, + vec![PathBuf::from(ledger_path).join(Path::new("stake-meta.accounts"))], + None, + &snapshot_config, + &ProcessOptions::default(), + None, + &Arc::new(AtomicBool::new(false)), + ); + + let working_bank = bank_forks.read().unwrap().working_bank(); + assert_eq!( + working_bank.slot(), + snapshot_slot, + "expected working bank slot {}, found {}", + snapshot_slot, + working_bank.slot() + ); + + Ok(working_bank) +} + +fn write_to_json_file( + stake_meta_coll: &StakeMetaCollection, + out_path: String, +) -> Result<(), Error> { + let file = File::create(out_path)?; + let mut writer = BufWriter::new(file); + serde_json::to_writer(&mut writer, stake_meta_coll)?; + writer.flush()?; + + Ok(()) +} + +/// Creates a collection of [StakeMeta]'s from the given bank. +pub fn generate_stake_meta_collection( + bank: &Arc, + // Used to derive the PDA and fetch the account data from the Bank. + tip_distribution_program_id: Pubkey, + // Optionally used to fetch the tip distribution accounts from an RPC node. + maybe_rpc_client: Option, +) -> Result { + assert!(bank.is_frozen()); + + let epoch_vote_accounts = bank.epoch_vote_accounts(bank.epoch()).expect(&*format!( + "No epoch_vote_accounts found for slot {} at epoch {}", + bank.slot(), + bank.epoch() + )); + + let l_stakes = bank.stakes_cache.stakes(); + let delegations = l_stakes.stake_delegations(); + + let account_fetcher = if let Some(rpc_client) = maybe_rpc_client { + Box::new(RpcAccountFetcher { rpc_client }) as Box + } else { + Box::new(BankAccountFetcher { bank: bank.clone() }) as Box + }; + let account_fetcher = Arc::new(account_fetcher); + + let vote_pk_and_maybe_tdas: Vec<( + (Pubkey, &VoteAccount), + Option, + )> = epoch_vote_accounts + .iter() + .map(|(&vote_pubkey, (_total_stake, vote_account))| { + let tda = fetch_and_deserialize_tip_distribution_account( + account_fetcher.clone(), + &vote_pubkey, + &tip_distribution_program_id, + bank.epoch(), + ) + .map_err(Error::from)?; + + Ok(((vote_pubkey, vote_account), tda)) + }) + .collect::>()?; + + let voter_pubkey_to_delegations = group_delegations_by_voter_pubkey(delegations, bank.epoch()); + + let mut stake_metas = vec![]; + for ((vote_pubkey, vote_account), maybe_tda) in vote_pk_and_maybe_tdas { + if let Some(delegations) = voter_pubkey_to_delegations.get(&vote_pubkey).cloned() { + let total_delegated = delegations.iter().fold(0u64, |sum, delegation| { + sum.checked_add(delegation.amount_delegated).unwrap() + }); + + let maybe_tip_distribution_meta = if let Some(tda) = maybe_tda { + let rent_exempt_amount = + bank.get_minimum_balance_for_rent_exemption(tda.account_data.data().len()); + + Some(TipDistributionMeta::from_tda_wrapper( + tda, + rent_exempt_amount, + )?) + } else { + None + }; + + stake_metas.push(StakeMeta { + maybe_tip_distribution_meta, + validator_vote_account: bs58::encode(vote_pubkey).into_string(), + delegations: delegations.clone(), + total_delegated, + commission: vote_account.vote_state().as_ref().unwrap().commission, + }); + } else { + warn!( + "voter_pubkey not found in voter_pubkey_to_delegations map [validator_vote_pubkey={}]", + vote_pubkey + ); + } + } + + Ok(StakeMetaCollection { + stake_metas, + tip_distribution_program_id: bs58::encode(tip_distribution_program_id.as_ref()) + .into_string(), + bank_hash: bank.hash().to_string(), + epoch: bank.epoch(), + slot: bank.slot(), + }) +} + +/// Given an [EpochStakes] object, return delegations grouped by voter_pubkey (validator delegated to). +fn group_delegations_by_voter_pubkey( + delegations: &im::HashMap, + epoch: Epoch, +) -> HashMap> { + delegations + .into_iter() + .filter(|(_stake_pubkey, stake_account)| stake_account.delegation().stake(epoch, None) > 0) + .into_group_map_by(|(_stake_pubkey, stake_account)| stake_account.delegation().voter_pubkey) + .into_iter() + .map(|(voter_pubkey, group)| { + ( + voter_pubkey, + group + .into_iter() + .map(|(stake_pubkey, stake_account)| crate::Delegation { + stake_account: bs58::encode(stake_pubkey).into_string(), + amount_delegated: stake_account.delegation().stake, + }) + .collect::>(), + ) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::derive_tip_distribution_account_address, + anchor_lang::AccountSerialize, + solana_runtime::genesis_utils::{ + create_genesis_config_with_vote_accounts, GenesisConfigInfo, ValidatorVoteKeypairs, + }, + solana_sdk::{ + self, + account::{from_account, AccountSharedData}, + message::Message, + signature::{Keypair, Signer}, + stake::{ + self, + state::{Authorized, Lockup}, + }, + stake_history::StakeHistory, + sysvar, + transaction::Transaction, + }, + solana_stake_program::stake_state, + std::str::FromStr, + tip_distribution::state::TipDistributionAccount, + }; + + #[test] + fn test_generate_stake_meta_collection_happy_path() { + /* 1. Create a Bank seeded with some validator stake accounts */ + let validator_keypairs_0 = ValidatorVoteKeypairs::new_rand(); + let validator_keypairs_1 = ValidatorVoteKeypairs::new_rand(); + let validator_keypairs_2 = ValidatorVoteKeypairs::new_rand(); + let validator_keypairs = vec![ + &validator_keypairs_0, + &validator_keypairs_1, + &validator_keypairs_2, + ]; + const INITIAL_VALIDATOR_STAKES: u64 = 10_000; + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config_with_vote_accounts( + 1_000_000_000, + &validator_keypairs, + vec![INITIAL_VALIDATOR_STAKES; 3], + ); + + let bank = Bank::new_for_tests(&genesis_config); + + /* 2. Seed the Bank with [TipDistributionAccount]'s */ + let merkle_root_upload_authority = Pubkey::new_unique(); + let tip_distribution_program_id = Pubkey::new_unique(); + + let delegator_0 = Keypair::new(); + let delegator_1 = Keypair::new(); + let delegator_2 = Keypair::new(); + let delegator_3 = Keypair::new(); + let delegator_4 = Keypair::new(); + + let delegator_0_pk = delegator_0.pubkey(); + let delegator_1_pk = delegator_1.pubkey(); + let delegator_2_pk = delegator_2.pubkey(); + let delegator_3_pk = delegator_3.pubkey(); + let delegator_4_pk = delegator_4.pubkey(); + + let d_0_data = AccountSharedData::new( + 300_000_000_000_000 * 10, + 0, + &solana_sdk::system_program::id(), + ); + let d_1_data = AccountSharedData::new( + 100_000_203_000_000 * 10, + 0, + &solana_sdk::system_program::id(), + ); + let d_2_data = AccountSharedData::new( + 100_000_235_899_000 * 10, + 0, + &solana_sdk::system_program::id(), + ); + let d_3_data = AccountSharedData::new( + 200_000_000_000_000 * 10, + 0, + &solana_sdk::system_program::id(), + ); + let d_4_data = AccountSharedData::new( + 100_000_000_777_000 * 10, + 0, + &solana_sdk::system_program::id(), + ); + + let accounts = vec![ + (&delegator_0_pk, &d_0_data), + (&delegator_1_pk, &d_1_data), + (&delegator_2_pk, &d_2_data), + (&delegator_3_pk, &d_3_data), + (&delegator_4_pk, &d_4_data), + ]; + + bank.store_accounts((bank.slot(), &accounts[..])); + + /* 3. Delegate some stake to the initial set of validators */ + let mut validator_0_delegations = vec![crate::Delegation { + stake_account: bs58::encode(validator_keypairs_0.stake_keypair.pubkey().as_ref()) + .into_string(), + amount_delegated: INITIAL_VALIDATOR_STAKES, + }]; + let stake_account = delegate_stake_helper( + &bank, + &delegator_0, + &validator_keypairs_0.vote_keypair.pubkey(), + 30_000_000_000, + ); + validator_0_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 30_000_000_000, + }); + let stake_account = delegate_stake_helper( + &bank, + &delegator_1, + &validator_keypairs_0.vote_keypair.pubkey(), + 3_000_000_000, + ); + validator_0_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 3_000_000_000, + }); + let stake_account = delegate_stake_helper( + &bank, + &delegator_2, + &validator_keypairs_0.vote_keypair.pubkey(), + 33_000_000_000, + ); + validator_0_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 33_000_000_000, + }); + + let mut validator_1_delegations = vec![crate::Delegation { + stake_account: bs58::encode(validator_keypairs_1.stake_keypair.pubkey().as_ref()) + .into_string(), + amount_delegated: INITIAL_VALIDATOR_STAKES, + }]; + let stake_account = delegate_stake_helper( + &bank, + &delegator_3, + &validator_keypairs_1.vote_keypair.pubkey(), + 4_222_364_000, + ); + validator_1_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 4_222_364_000, + }); + let stake_account = delegate_stake_helper( + &bank, + &delegator_4, + &validator_keypairs_1.vote_keypair.pubkey(), + 6_000_000_527, + ); + validator_1_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 6_000_000_527, + }); + + let mut validator_2_delegations = vec![crate::Delegation { + stake_account: bs58::encode(validator_keypairs_2.stake_keypair.pubkey().as_ref()) + .into_string(), + amount_delegated: INITIAL_VALIDATOR_STAKES, + }]; + let stake_account = delegate_stake_helper( + &bank, + &delegator_0, + &validator_keypairs_2.vote_keypair.pubkey(), + 1_300_123_156, + ); + validator_2_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 1_300_123_156, + }); + let stake_account = delegate_stake_helper( + &bank, + &delegator_4, + &validator_keypairs_2.vote_keypair.pubkey(), + 1_610_565_420, + ); + validator_2_delegations.push(crate::Delegation { + stake_account: bs58::encode(stake_account.as_ref()).into_string(), + amount_delegated: 1_610_565_420, + }); + + /* 4. Run assertions */ + fn warmed_up(bank: &Bank, stake_pubkeys: &[Pubkey]) -> bool { + for stake_pubkey in stake_pubkeys { + let stake = + stake_state::stake_from(&bank.get_account(stake_pubkey).unwrap()).unwrap(); + + if stake.delegation.stake + != stake.stake( + bank.epoch(), + Some( + &from_account::( + &bank.get_account(&sysvar::stake_history::id()).unwrap(), + ) + .unwrap(), + ), + ) + { + return false; + } + } + + true + } + fn next_epoch(bank: &Arc) -> Arc { + bank.squash(); + + Arc::new(Bank::new_from_parent( + bank, + &Pubkey::default(), + bank.get_slots_in_epoch(bank.epoch()) + bank.slot(), + )) + } + + let mut bank = Arc::new(bank); + let mut stake_pubkeys = validator_0_delegations + .iter() + .map(|v| Pubkey::from_str(&*v.stake_account).unwrap()) + .collect::>(); + stake_pubkeys.extend( + validator_1_delegations + .iter() + .map(|v| Pubkey::from_str(&*v.stake_account).unwrap()), + ); + stake_pubkeys.extend( + validator_2_delegations + .iter() + .map(|v| Pubkey::from_str(&*v.stake_account).unwrap()), + ); + loop { + if warmed_up(&bank, &stake_pubkeys[..]) { + break; + } + + // Cycle thru banks until we're fully warmed up + bank = next_epoch(&bank); + } + + let tip_distribution_account_0 = derive_tip_distribution_account_address( + &tip_distribution_program_id, + &validator_keypairs_0.vote_keypair.pubkey(), + bank.epoch(), + ); + let tip_distribution_account_1 = derive_tip_distribution_account_address( + &tip_distribution_program_id, + &validator_keypairs_1.vote_keypair.pubkey(), + bank.epoch(), + ); + let tip_distribution_account_2 = derive_tip_distribution_account_address( + &tip_distribution_program_id, + &validator_keypairs_2.vote_keypair.pubkey(), + bank.epoch(), + ); + + let tda_0 = TipDistributionAccount { + validator_vote_account: validator_keypairs_0.vote_keypair.pubkey(), + merkle_root_upload_authority, + merkle_root: None, + epoch_created_at: bank.epoch(), + validator_commission_bps: 50, + bump: tip_distribution_account_0.1, + }; + let tda_1 = TipDistributionAccount { + validator_vote_account: validator_keypairs_1.vote_keypair.pubkey(), + merkle_root_upload_authority, + merkle_root: None, + epoch_created_at: bank.epoch(), + validator_commission_bps: 500, + bump: tip_distribution_account_1.1, + }; + let tda_2 = TipDistributionAccount { + validator_vote_account: validator_keypairs_2.vote_keypair.pubkey(), + merkle_root_upload_authority, + merkle_root: None, + epoch_created_at: bank.epoch(), + validator_commission_bps: 75, + bump: tip_distribution_account_2.1, + }; + + let tip_distro_0_tips = 1_000_000 * 10; + let tip_distro_1_tips = 69_000_420 * 10; + let tip_distro_2_tips = 789_000_111 * 10; + + let tda_0_fields = (tip_distribution_account_0.0, tda_0.validator_commission_bps); + let data_0 = + tda_to_account_shared_data(&tip_distribution_program_id, tip_distro_0_tips, tda_0); + let tda_1_fields = (tip_distribution_account_1.0, tda_1.validator_commission_bps); + let data_1 = + tda_to_account_shared_data(&tip_distribution_program_id, tip_distro_1_tips, tda_1); + let tda_2_fields = (tip_distribution_account_2.0, tda_2.validator_commission_bps); + let data_2 = + tda_to_account_shared_data(&tip_distribution_program_id, tip_distro_2_tips, tda_2); + + let accounts = vec![ + (&tip_distribution_account_0.0, &data_0), + (&tip_distribution_account_1.0, &data_1), + (&tip_distribution_account_2.0, &data_2), + ]; + bank.store_accounts((bank.slot(), &accounts[..])); + + bank.freeze(); + let stake_meta_collection = + generate_stake_meta_collection(&bank, tip_distribution_program_id, None).unwrap(); + assert_eq!( + stake_meta_collection.tip_distribution_program_id, + bs58::encode(tip_distribution_program_id.as_ref()).into_string() + ); + assert_eq!(stake_meta_collection.slot, bank.slot()); + assert_eq!(stake_meta_collection.epoch, bank.epoch()); + + let mut expected_stake_metas = HashMap::new(); + expected_stake_metas.insert( + bs58::encode(validator_keypairs_0.vote_keypair.pubkey()).into_string(), + StakeMeta { + validator_vote_account: bs58::encode( + validator_keypairs_0.vote_keypair.pubkey().as_ref(), + ) + .into_string(), + delegations: validator_0_delegations.clone(), + total_delegated: validator_0_delegations + .iter() + .fold(0u64, |sum, delegation| { + sum.checked_add(delegation.amount_delegated).unwrap() + }), + maybe_tip_distribution_meta: Some(TipDistributionMeta { + merkle_root_upload_authority: bs58::encode( + merkle_root_upload_authority.as_ref(), + ) + .into_string(), + tip_distribution_account: bs58::encode(tda_0_fields.0.as_ref()).into_string(), + total_tips: tip_distro_0_tips + .checked_sub( + bank.get_minimum_balance_for_rent_exemption( + TipDistributionAccount::SIZE, + ), + ) + .unwrap(), + validator_fee_bps: tda_0_fields.1, + }), + commission: 0, + }, + ); + expected_stake_metas.insert( + bs58::encode(validator_keypairs_1.vote_keypair.pubkey().as_ref()).into_string(), + StakeMeta { + validator_vote_account: bs58::encode( + validator_keypairs_1.vote_keypair.pubkey().as_ref(), + ) + .into_string(), + delegations: validator_1_delegations.clone(), + total_delegated: validator_1_delegations + .iter() + .fold(0u64, |sum, delegation| { + sum.checked_add(delegation.amount_delegated).unwrap() + }), + maybe_tip_distribution_meta: Some(TipDistributionMeta { + merkle_root_upload_authority: bs58::encode( + merkle_root_upload_authority.as_ref(), + ) + .into_string(), + tip_distribution_account: bs58::encode(tda_1_fields.0.as_ref()).into_string(), + total_tips: tip_distro_1_tips + .checked_sub( + bank.get_minimum_balance_for_rent_exemption( + TipDistributionAccount::SIZE, + ), + ) + .unwrap(), + validator_fee_bps: tda_1_fields.1, + }), + commission: 0, + }, + ); + expected_stake_metas.insert( + bs58::encode(validator_keypairs_2.vote_keypair.pubkey().as_ref()).into_string(), + StakeMeta { + validator_vote_account: bs58::encode( + validator_keypairs_2.vote_keypair.pubkey().as_ref(), + ) + .into_string(), + delegations: validator_2_delegations.clone(), + total_delegated: validator_2_delegations + .iter() + .fold(0u64, |sum, delegation| { + sum.checked_add(delegation.amount_delegated).unwrap() + }), + maybe_tip_distribution_meta: Some(TipDistributionMeta { + merkle_root_upload_authority: bs58::encode( + merkle_root_upload_authority.as_ref(), + ) + .into_string(), + tip_distribution_account: bs58::encode(tda_2_fields.0.as_ref()).into_string(), + total_tips: tip_distro_2_tips + .checked_sub( + bank.get_minimum_balance_for_rent_exemption( + TipDistributionAccount::SIZE, + ), + ) + .unwrap(), + validator_fee_bps: tda_2_fields.1, + }), + commission: 0, + }, + ); + + println!( + "validator_0 [vote_account={}, stake_account={}]", + validator_keypairs_0.vote_keypair.pubkey(), + validator_keypairs_0.stake_keypair.pubkey() + ); + println!( + "validator_1 [vote_account={}, stake_account={}]", + validator_keypairs_1.vote_keypair.pubkey(), + validator_keypairs_1.stake_keypair.pubkey() + ); + println!( + "validator_2 [vote_account={}, stake_account={}]", + validator_keypairs_2.vote_keypair.pubkey(), + validator_keypairs_2.stake_keypair.pubkey(), + ); + + assert_eq!( + expected_stake_metas.len(), + stake_meta_collection.stake_metas.len() + ); + + for actual_stake_meta in stake_meta_collection.stake_metas { + let expected_stake_meta = expected_stake_metas + .get(&actual_stake_meta.validator_vote_account) + .unwrap(); + assert_eq!( + expected_stake_meta.maybe_tip_distribution_meta, + actual_stake_meta.maybe_tip_distribution_meta + ); + assert_eq!( + expected_stake_meta.total_delegated, + actual_stake_meta.total_delegated + ); + assert_eq!(expected_stake_meta.commission, actual_stake_meta.commission); + assert_eq!( + expected_stake_meta.validator_vote_account, + actual_stake_meta.validator_vote_account + ); + + assert_eq!( + expected_stake_meta.delegations.len(), + actual_stake_meta.delegations.len() + ); + + for expected_delegation in &expected_stake_meta.delegations { + let actual_delegation = actual_stake_meta + .delegations + .iter() + .find(|d| d.stake_account == expected_delegation.stake_account) + .unwrap(); + + assert_eq!(expected_delegation, actual_delegation); + } + } + } + + /// Helper function that sends a delegate stake instruction to the bank. + /// Returns the created stake account pubkey. + fn delegate_stake_helper( + bank: &Bank, + from_keypair: &Keypair, + vote_account: &Pubkey, + delegation_amount: u64, + ) -> Pubkey { + let minimum_delegation = solana_stake_program::get_minimum_delegation(&*bank.feature_set); + assert!( + delegation_amount >= minimum_delegation, + "{}", + format!( + "received delegation_amount {}, must be at least {}", + delegation_amount, minimum_delegation + ) + ); + if let Some(from_account) = bank.get_account(&from_keypair.pubkey()) { + assert_eq!(from_account.owner(), &solana_sdk::system_program::id()); + } else { + panic!("from_account DNE"); + } + assert!(bank.get_account(vote_account).is_some()); + + let stake_keypair = Keypair::new(); + let instructions = stake::instruction::create_account_and_delegate_stake( + &from_keypair.pubkey(), + &stake_keypair.pubkey(), + vote_account, + &Authorized::auto(&stake_keypair.pubkey()), + &Lockup::default(), + delegation_amount, + ); + + let message = Message::new(&instructions[..], Some(&from_keypair.pubkey())); + let transaction = Transaction::new( + &[from_keypair, &stake_keypair], + message, + bank.last_blockhash(), + ); + + bank.process_transaction(&transaction) + .map_err(|e| { + eprintln!("Error delegating stake [error={}]", e); + e + }) + .unwrap(); + + stake_keypair.pubkey() + } + + fn tda_to_account_shared_data( + tip_distribution_program_id: &Pubkey, + lamports: u64, + tda: TipDistributionAccount, + ) -> AccountSharedData { + let mut account_data = AccountSharedData::new( + lamports, + TipDistributionAccount::SIZE, + tip_distribution_program_id, + ); + + let mut data: [u8; TipDistributionAccount::SIZE] = [0u8; TipDistributionAccount::SIZE]; + let mut cursor = std::io::Cursor::new(&mut data[..]); + tda.try_serialize(&mut cursor).unwrap(); + + account_data.set_data(data.to_vec()); + account_data + } +} diff --git a/validator/Cargo.toml b/validator/Cargo.toml index ad5bdaf383..9e0f8a74cb 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -58,6 +58,7 @@ solana-tpu-client = { path = "../tpu-client", version = "=1.15.0", default-featu solana-version = { path = "../version", version = "=1.15.0" } solana-vote-program = { path = "../programs/vote", version = "=1.15.0" } symlink = "0.1.0" +tonic = { version = "0.5.2", features = ["tls", "tls-roots", "tls-webpki-roots"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { package = "tikv-jemallocator", version = "0.4.1", features = ["unprefixed_malloc_on_supported_platforms"] } diff --git a/validator/src/bootstrap.rs b/validator/src/bootstrap.rs index 77965b8a44..ffc8ace67d 100644 --- a/validator/src/bootstrap.rs +++ b/validator/src/bootstrap.rs @@ -703,12 +703,13 @@ fn get_highest_local_snapshot_hash( incremental_snapshot_archives_dir: impl AsRef, incremental_snapshot_fetch: bool, ) -> Option<(Slot, Hash)> { - snapshot_utils::get_highest_full_snapshot_archive_info(full_snapshot_archives_dir).and_then( - |full_snapshot_info| { + snapshot_utils::get_highest_full_snapshot_archive_info(full_snapshot_archives_dir, None) + .and_then(|full_snapshot_info| { if incremental_snapshot_fetch { snapshot_utils::get_highest_incremental_snapshot_archive_info( incremental_snapshot_archives_dir, full_snapshot_info.slot(), + None, ) .map(|incremental_snapshot_info| { ( @@ -720,8 +721,7 @@ fn get_highest_local_snapshot_hash( None } .or_else(|| Some((full_snapshot_info.slot(), *full_snapshot_info.hash()))) - }, - ) + }) } /// Get peer snapshot hashes diff --git a/validator/src/dashboard.rs b/validator/src/dashboard.rs index 11b3e35fa2..b4287238df 100644 --- a/validator/src/dashboard.rs +++ b/validator/src/dashboard.rs @@ -273,6 +273,7 @@ fn get_validator_stats( Ok(()) => "ok".to_string(), Err(err) => { if let client_error::ErrorKind::RpcError(request::RpcError::RpcResponseError { + request_id: _, code: _, message: _, data: diff --git a/validator/src/main.rs b/validator/src/main.rs index 6ebaf43a89..2805081f70 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -21,7 +21,9 @@ use { }, solana_core::{ ledger_cleanup_service::{DEFAULT_MAX_LEDGER_SHREDS, DEFAULT_MIN_MAX_LEDGER_SHREDS}, + proxy::{block_engine_stage::BlockEngineConfig, relayer_stage::RelayerConfig}, system_monitor_service::SystemMonitorService, + tip_manager::{TipDistributionAccountConfig, TipManagerConfig}, tower_storage, tpu::DEFAULT_TPU_COALESCE_MS, validator::{is_snapshot_config_valid, Validator, ValidatorConfig, ValidatorStartProgress}, @@ -92,6 +94,7 @@ use { sync::{Arc, RwLock}, time::{Duration, SystemTime}, }, + tonic::transport::Endpoint, }; #[cfg(not(target_env = "msvc"))] @@ -1778,6 +1781,100 @@ pub fn main() { .help("Allow contacting private ip addresses") .hidden(true), ) + .arg( + Arg::with_name("relayer_address") + .long("relayer-address") + .value_name("relayer_address") + .takes_value(true) + .help("Address of the relayer") + ) + .arg( + Arg::with_name("block_engine_address") + .long("block-engine-address") + .value_name("block_engine_address") + .takes_value(true) + .help("Address of the block engine") + ) + .arg( + Arg::with_name("block_engine_auth_service_address") + .long("block-engine-auth-service-address") + .value_name("block_engine_auth_service_address") + .takes_value(true) + .help("Address of the block engine's authentication service.") + ) + .arg( + Arg::with_name("relayer_auth_service_address") + .long("relayer-auth-service-address") + .value_name("relayer_auth_service_address") + .takes_value(true) + .help("Address of the block engine's authentication service.") + ) + .arg( + Arg::with_name("trust_relayer_packets") + .long("trust-relayer-packets") + .takes_value(false) + .help("Skip signature verification on relayer packets. Not recommended unless the relayer is trusted.") + ) + .arg( + Arg::with_name("relayer_expected_heartbeat_interval_ms") + .long("relayer-expected-heartbeat-interval-ms") + .takes_value(true) + .help("Interval at which the Relayer is expected to send heartbeat messages.") + ) + .arg( + Arg::with_name("relayer_max_failed_heartbeats") + .long("relayer-max-failed-heartbeats") + .takes_value(true) + .help("Maximum number of heartbeats the Relayer can miss before falling back to the normal TPU pipeline.") + ) + .arg( + Arg::with_name("trust_block_engine_packets") + .long("trust-block-engine-packets") + .takes_value(false) + .help("Skip signature verification on block engine packets. Not recommended unless the block engine is trusted.") + ) + .arg( + Arg::with_name("tip_payment_program_pubkey") + .long("tip-payment-program-pubkey") + .value_name("TIP_PAYMENT_PROGRAM_PUBKEY") + .takes_value(true) + .help("The public key of the tip-payment program") + ) + .arg( + Arg::with_name("tip_distribution_program_pubkey") + .long("tip-distribution-program-pubkey") + .value_name("TIP_DISTRIBUTION_PROGRAM_PUBKEY") + .takes_value(true) + .help("The public key of the tip-distribution program.") + ) + .arg( + Arg::with_name("tip_distribution_account_payer") + .long("tip-distribution-account-payer") + .value_name("TIP_DISTRIBUTION_ACCOUNT_PAYER") + .takes_value(true) + .help("The payer of my tip distribution accounts.") + ) + .arg( + Arg::with_name("merkle_root_upload_authority") + .long("merkle-root-upload-authority") + .value_name("MERKLE_ROOT_UPLOAD_AUTHORITY") + .takes_value(true) + .help("The public key of the authorized merkle-root uploader.") + ) + .arg( + Arg::with_name("commission_bps") + .long("commission-bps") + .value_name("COMMISSION_BPS") + .takes_value(true) + .help("The commission validator takes from tips expressed in basis points.") + ) + .arg( + Arg::with_name("shred_receiver_address") + .long("shred-receiver-address") + .value_name("SHRED_RECEIVER_ADDRESS") + .takes_value(true) + .help("Shred receiver listening address") + ) .arg( Arg::with_name("log_messages_bytes_limit") .long("log-messages-bytes-limit") @@ -2579,6 +2676,88 @@ pub fn main() { } let full_api = matches.is_present("full_rpc_api"); + let voting_disabled = matches.is_present("no_voting") || restricted_repair_only_mode; + let tip_manager_config = tip_manager_config_from_matches(&matches, voting_disabled); + + let is_block_engine_enabled = matches.is_present("block_engine_address") + || matches.is_present("block_engine_auth_service_address") + || matches.is_present("trust_block_engine_packets"); + let maybe_block_engine_config = is_block_engine_enabled.then(|| { + let addr: String = value_of(&matches, "block_engine_auth_service_address") + .expect("missing block-engine-auth-service-address"); + let mut auth_service_endpoint = Endpoint::from_shared(addr.clone()) + .expect("invalid block-engine-auth-service-address value"); + if addr.contains("https") { + auth_service_endpoint = auth_service_endpoint + .tls_config(tonic::transport::ClientTlsConfig::new()) + .expect("failed to set tls_config"); + } + + let addr: String = + value_of(&matches, "block_engine_address").expect("missing block-engine-address"); + let mut backend_endpoint = Endpoint::from_shared(addr.clone()) + .expect("invalid block-engine-address value") + .tcp_keepalive(Some(Duration::from_secs(60))); + if addr.contains("https") { + backend_endpoint = backend_endpoint + .tls_config(tonic::transport::ClientTlsConfig::new()) + .expect("failed to set tls_config"); + } + + BlockEngineConfig { + auth_service_endpoint, + backend_endpoint, + trust_packets: matches.is_present("trust_block_engine_packets"), + } + }); + + let is_relayer_enabled = matches.is_present("relayer_auth_service_address") + || matches.is_present("relayer_address") + || matches.is_present("trust_relayer_packets") + || matches.is_present("relayer_expected_heartbeat_interval_ms") + || matches.is_present("relayer_max_failed_heartbeats"); + let maybe_relayer_config = is_relayer_enabled.then(|| { + let addr: String = value_of(&matches, "relayer_auth_service_address") + .expect("missing relayer-auth-service-address"); + let mut auth_service_endpoint = Endpoint::from_shared(addr.clone()) + .expect("invalid relayer-auth-service-address value"); + if addr.contains("https") { + auth_service_endpoint = auth_service_endpoint + .tls_config(tonic::transport::ClientTlsConfig::new()) + .expect("failed to set tls_config"); + } + + let addr: String = value_of(&matches, "relayer_address").expect("missing relayer-address"); + let mut backend_endpoint = + Endpoint::from_shared(addr.clone()).expect("invalid relayer-address value"); + if addr.contains("https") { + backend_endpoint = backend_endpoint + .tls_config(tonic::transport::ClientTlsConfig::new()) + .expect("failed to set tls_config"); + } + + let expected_heartbeat_interval_ms = + value_of(&matches, "relayer_expected_heartbeat_interval_ms").unwrap_or(500); + let expected_heartbeat_interval = Duration::from_millis(expected_heartbeat_interval_ms); + + let max_failed_heartbeats = + value_of(&matches, "relayer_max_failed_heartbeats").unwrap_or(3); + assert!( + max_failed_heartbeats > 0, + "relayer-max-failed-heartbeats must be greater than zero" + ); + let oldest_allowed_heartbeat = + Duration::from_millis(max_failed_heartbeats * expected_heartbeat_interval_ms); + + RelayerConfig { + auth_service_endpoint, + backend_endpoint, + expected_heartbeat_interval, + oldest_allowed_heartbeat, + trust_packets: matches.is_present("trust_relayer_packets"), + } + }); + let mut validator_config = ValidatorConfig { require_tower: matches.is_present("require_tower"), tower_storage, @@ -2712,6 +2891,12 @@ pub fn main() { log_messages_bytes_limit: value_of(&matches, "log_messages_bytes_limit"), ..RuntimeConfig::default() }, + maybe_relayer_config, + maybe_block_engine_config, + tip_manager_config, + shred_receiver_address: matches + .value_of("shred_receiver_address") + .map(|address| SocketAddr::from_str(address).expect("shred_receiver_address invalid")), staked_nodes_overrides: staked_nodes_overrides.clone(), replay_slots_concurrently: matches.is_present("replay_slots_concurrently"), ..ValidatorConfig::default() @@ -3341,3 +3526,58 @@ fn warn_for_deprecated_arguments(matches: &ArgMatches) { } } } + +fn tip_manager_config_from_matches( + matches: &ArgMatches, + voting_disabled: bool, +) -> TipManagerConfig { + TipManagerConfig { + tip_payment_program_id: pubkey_of(matches, "tip_payment_program_pubkey").unwrap_or_else( + || { + if !voting_disabled { + panic!("--tip-payment-program-pubkey argument required when validator is voting"); + } + Pubkey::new_unique() + }, + ), + tip_distribution_program_id: pubkey_of(matches, "tip_distribution_program_pubkey") + .unwrap_or_else(|| { + if !voting_disabled { + panic!("--tip-distribution-program-pubkey argument required when validator is voting"); + } + Pubkey::new_unique() + }), + tip_distribution_account_config: TipDistributionAccountConfig { + payer: { + let keypair = + keypair_of(matches, "tip_distribution_account_payer").unwrap_or_else(|| { + if !voting_disabled { + panic!("--tip-distribution-account-payer argument required when validator is voting"); + } + Keypair::new() + }); + + Arc::new(keypair) + }, + merkle_root_upload_authority: pubkey_of(matches, "merkle_root_upload_authority") + .unwrap_or_else(|| { + if !voting_disabled { + panic!("--merkle-root-upload-authority argument required when validator is voting"); + } + Pubkey::new_unique() + }), + vote_account: pubkey_of(matches, "vote_account").unwrap_or_else(|| { + if !voting_disabled { + panic!("--vote-account argument required when validator is voting"); + } + Pubkey::new_unique() + }), + commission_bps: value_t!(matches, "commission_bps", u16).unwrap_or_else(|_| { + if !voting_disabled { + panic!("--commission-bps argument required when validator is voting"); + } + 0 + }), + }, + } +}