From 93804fff79514569ccc16870ec027f2c4db6acbe Mon Sep 17 00:00:00 2001
From: Lucas B <lucas@jito.wtf>
Date: Thu, 25 Aug 2022 17:18:46 -0500
Subject: [PATCH] jito patch only reroute if relayer connected (#123) feat: add
 client tls config (#121) remove extra val (#129) fix clippy (#130) copy all
 binaries to docker-output (#131) Ledger tool halts at slot passed to
 create-snapshot (#118) update program submodule (#133) quick fix for tips and
 clearing old bundles (#135) update submodule to new program (#136) Improve
 stake-meta-generator usability (#134) pinning submodule head (#140) Use
 BundleAccountLocker when handling tip txs (#147) Add metrics for relayer +
 block engine proxy (#149) Build claim-mev in docker (#141) Rework bundle
 receiving and add metrics (#152) (#154) update submodule + dev files (#158)
 Deterministically find tip amounts, add meta to stake info, and cleanup
 pubkey/strings in MEV tips (#159) update jito-programs submodule (#160)
 Separate MEV tip related workflow (#161) Add block builder fee protos (#162)
 fix jito programs (#163) update submodule so autosnapshot exits out of ledger
 tool early (#164) Pipe through block builder fee (#167) use current config
 block builder backport 177 and update programs (#178) add accountsdb conn
 submod (#170) (#179) new submodules (#180) (#182)

---
 .dockerignore                                 |    1 +
 .github/dependabot.yml                        |   41 -
 .github/workflows/client-targets.yml          |    6 +-
 .gitignore                                    |    5 +-
 .gitmodules                                   |   12 +
 Cargo.lock                                    | 2956 ++++++++++-------
 Cargo.toml                                    |    7 +
 README.md                                     |   12 +-
 anchor                                        |    1 +
 banking-bench/Cargo.toml                      |    2 +-
 banking-bench/src/main.rs                     |   15 +-
 banks-server/Cargo.toml                       |    4 +
 banks-server/src/banks_server.rs              |    5 +-
 banks-server/src/rpc_banks_service.rs         |   33 +-
 bench-batch-simulate-bundle/Cargo.toml        |   16 +
 bench-batch-simulate-bundle/src/main.rs       |  396 +++
 bench-batch-simulate-bundle/src/simulator.rs  |  149 +
 .../Cargo.toml                                |   12 +
 .../src/main.rs                               |  120 +
 bootstrap                                     |   21 +
 ci/buildkite-pipeline-in-disk.sh              |   34 +-
 ci/buildkite-pipeline.sh                      |   34 +-
 ci/buildkite-solana-private.sh                |   32 +-
 ci/docker-rust/Dockerfile                     |    1 +
 cli-output/Cargo.toml                         |    2 +-
 client/src/http_sender.rs                     |  203 +-
 client/src/mock_sender.rs                     |    7 +
 client/src/nonblocking/rpc_client.rs          |  124 +-
 client/src/rpc_client.rs                      |   30 +
 client/src/rpc_config.rs                      |   45 +
 client/src/rpc_request.rs                     |    3 +
 client/src/rpc_response.rs                    |   50 +-
 client/src/rpc_sender.rs                      |    4 +
 core/Cargo.toml                               |   19 +-
 core/benches/banking_stage.rs                 |   17 +-
 core/benches/cluster_info.rs                  |    1 +
 core/benches/proto_to_packet.rs               |   56 +
 core/benches/retransmit_stage.rs              |    1 +
 core/src/backoff.rs                           |   45 +
 core/src/banking_stage.rs                     |  170 +-
 core/src/broadcast_stage.rs                   |   62 +-
 .../broadcast_duplicates_run.rs               |    3 +-
 .../broadcast_fake_shreds_run.rs              |    2 +
 core/src/broadcast_stage/broadcast_utils.rs   |   37 +-
 .../fail_entry_verification_broadcast_run.rs  |    4 +-
 .../broadcast_stage/standard_broadcast_run.rs |   18 +-
 core/src/bundle_account_locker.rs             |  334 ++
 core/src/bundle_sanitizer.rs                  |  612 ++++
 core/src/bundle_stage.rs                      | 1943 +++++++++++
 core/src/bundle_stage_leader_stats.rs         |  326 ++
 core/src/consensus_cache_updater.rs           |   52 +
 core/src/lib.rs                               |   48 +
 core/src/packet_bundle.rs                     |    7 +
 core/src/proxy/auth.rs                        |  247 ++
 core/src/proxy/block_engine_stage.rs          |  383 +++
 core/src/proxy/fetch_stage_manager.rs         |  161 +
 core/src/proxy/mod.rs                         |   55 +
 core/src/proxy/relayer_stage.rs               |  362 ++
 core/src/qos_service.rs                       |    2 +-
 core/src/retransmit_stage.rs                  |   12 +-
 core/src/tip_manager.rs                       |  473 +++
 core/src/tpu.rs                               |  121 +-
 core/src/tvu.rs                               |    7 +-
 core/src/validator.rs                         |   23 +-
 core/tests/snapshots.rs                       |    2 +
 deploy_programs                               |   17 +
 dev/Dockerfile                                |   44 +
 dos/Cargo.toml                                |    2 +-
 entry/src/entry.rs                            |    2 +-
 entry/src/poh.rs                              |   29 +-
 f                                             |   25 +
 frozen-abi/Cargo.toml                         |    2 +-
 gossip/src/cluster_info.rs                    |    4 +
 jito-programs                                 |    1 +
 jito-protos/Cargo.toml                        |   16 +
 jito-protos/build.rs                          |   17 +
 jito-protos/protos                            |    1 +
 jito-protos/src/lib.rs                        |   25 +
 ledger-tool/src/main.rs                       |    6 +
 ledger/src/bank_forks_utils.rs                |   11 +-
 ledger/src/blockstore_processor.rs            |    5 +-
 ledger/src/token_balances.rs                  |   58 +-
 .../src/local_cluster_snapshot_utils.rs       |    3 +-
 local-cluster/src/validator_configs.rs        |    4 +
 local-cluster/tests/local_cluster.rs          |    8 +-
 logger/Cargo.toml                             |    2 +-
 measure/src/lib.rs                            |    1 +
 measure/src/macros.rs                         |  143 +
 merkle-tree/src/merkle_tree.rs                |   46 +-
 multinode-demo/bootstrap-validator.sh         |   36 +
 multinode-demo/validator.sh                   |   32 +
 perf/src/sigverify.rs                         |    2 +-
 poh/src/poh_recorder.rs                       |  117 +-
 poh/src/poh_service.rs                        |   34 +-
 programs/bpf/Cargo.lock                       | 2595 +++++++++------
 programs/bpf/tests/programs.rs                |    4 +-
 replica-node/src/replica_node.rs              |    1 +
 replica-node/src/replica_util.rs              |    7 +-
 replica-node/tests/local_replica.rs           |    2 +-
 rpc/src/rpc.rs                                |  435 ++-
 rpc/src/rpc_service.rs                        |    6 +-
 runtime/Cargo.toml                            |    6 +-
 runtime/src/account_overrides.rs              |   22 +-
 runtime/src/accounts.rs                       |   96 +-
 runtime/src/bank.rs                           | 1150 ++++++-
 runtime/src/builtins.rs                       |    2 +-
 runtime/src/snapshot_utils.rs                 |   50 +-
 runtime/src/stakes.rs                         |    4 +-
 rustfmt.toml                                  |    5 +
 s                                             |   15 +
 scripts/increment-cargo-version.sh            |    3 +
 scripts/run.sh                                |    5 +
 sdk/Cargo.toml                                |    3 +
 sdk/src/bundle/error.rs                       |   51 +
 sdk/src/bundle/mod.rs                         |   12 +
 sdk/src/bundle/sanitized.rs                   |    8 +
 sdk/src/bundle/utils.rs                       |   20 +
 sdk/src/lib.rs                                |    1 +
 sdk/src/transaction/error.rs                  |    8 +
 send-transaction-service/Cargo.toml           |    3 +-
 .../src/send_transaction_service.rs           |   39 +-
 solana-accountsdb-connector                   |    1 +
 start                                         |    9 +
 start_multi                                   |   29 +
 storage-bigtable/src/bigtable.rs              |    2 +-
 storage-proto/proto/transaction_by_addr.proto |    2 +
 storage-proto/src/convert.rs                  |    8 +
 tip-distributor/Cargo.toml                    |   48 +
 tip-distributor/README.md                     |   43 +
 tip-distributor/src/bin/claim-mev-tips.rs     |   52 +
 .../src/bin/merkle-root-generator.rs          |   29 +
 .../src/bin/merkle-root-uploader.rs           |   50 +
 .../src/bin/stake-meta-generator.rs           |   67 +
 tip-distributor/src/claim_mev_workflow.rs     |  143 +
 tip-distributor/src/lib.rs                    |  779 +++++
 .../src/merkle_root_generator_workflow.rs     |   49 +
 .../src/merkle_root_upload_workflow.rs        |  126 +
 .../src/stake_meta_generator_workflow.rs      |  836 +++++
 validator/Cargo.toml                          |    1 +
 validator/src/bootstrap.rs                    |    3 +-
 validator/src/dashboard.rs                    |    1 +
 validator/src/main.rs                         |  240 ++
 142 files changed, 15018 insertions(+), 2734 deletions(-)
 create mode 100644 .dockerignore
 delete mode 100644 .github/dependabot.yml
 create mode 100644 .gitmodules
 create mode 160000 anchor
 create mode 100644 bench-batch-simulate-bundle/Cargo.toml
 create mode 100644 bench-batch-simulate-bundle/src/main.rs
 create mode 100644 bench-batch-simulate-bundle/src/simulator.rs
 create mode 100644 bench-get-confirmed-blocks-with-data/Cargo.toml
 create mode 100644 bench-get-confirmed-blocks-with-data/src/main.rs
 create mode 100755 bootstrap
 create mode 100644 core/benches/proto_to_packet.rs
 create mode 100644 core/src/backoff.rs
 create mode 100644 core/src/bundle_account_locker.rs
 create mode 100644 core/src/bundle_sanitizer.rs
 create mode 100644 core/src/bundle_stage.rs
 create mode 100644 core/src/bundle_stage_leader_stats.rs
 create mode 100644 core/src/consensus_cache_updater.rs
 create mode 100644 core/src/packet_bundle.rs
 create mode 100644 core/src/proxy/auth.rs
 create mode 100644 core/src/proxy/block_engine_stage.rs
 create mode 100644 core/src/proxy/fetch_stage_manager.rs
 create mode 100644 core/src/proxy/mod.rs
 create mode 100644 core/src/proxy/relayer_stage.rs
 create mode 100644 core/src/tip_manager.rs
 create mode 100755 deploy_programs
 create mode 100644 dev/Dockerfile
 create mode 100755 f
 create mode 160000 jito-programs
 create mode 100644 jito-protos/Cargo.toml
 create mode 100644 jito-protos/build.rs
 create mode 160000 jito-protos/protos
 create mode 100644 jito-protos/src/lib.rs
 create mode 100644 measure/src/macros.rs
 create mode 100755 s
 create mode 100644 sdk/src/bundle/error.rs
 create mode 100644 sdk/src/bundle/mod.rs
 create mode 100644 sdk/src/bundle/sanitized.rs
 create mode 100644 sdk/src/bundle/utils.rs
 create mode 160000 solana-accountsdb-connector
 create mode 100755 start
 create mode 100755 start_multi
 create mode 100644 tip-distributor/Cargo.toml
 create mode 100644 tip-distributor/README.md
 create mode 100644 tip-distributor/src/bin/claim-mev-tips.rs
 create mode 100644 tip-distributor/src/bin/merkle-root-generator.rs
 create mode 100644 tip-distributor/src/bin/merkle-root-uploader.rs
 create mode 100644 tip-distributor/src/bin/stake-meta-generator.rs
 create mode 100644 tip-distributor/src/claim_mev_workflow.rs
 create mode 100644 tip-distributor/src/lib.rs
 create mode 100644 tip-distributor/src/merkle_root_generator_workflow.rs
 create mode 100644 tip-distributor/src/merkle_root_upload_workflow.rs
 create mode 100644 tip-distributor/src/stake_meta_generator_workflow.rs

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 88b160b448..dd0b67d17a 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 124358b46f..92281a9a08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,7 @@
 /solana-release.tar.bz2
 /solana-metrics/
 /solana-metrics.tar.bz2
-/target/
+**/target/
 /test-ledger/
 
 **/*.rs.bk
@@ -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..c6bb20e21c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,12 @@
+[submodule "anchor"]
+	path = anchor
+	url = org-87542516@github.com:jito-foundation/anchor.git
+[submodule "jito-programs"]
+	path = jito-programs
+	url = org-87542516@github.com:jito-foundation/jito-programs.git
+[submodule "jito-protos/protos"]
+	path = jito-protos/protos
+	url = org-87542516@github.com:jito-labs/mev-protos-priv.git
+[submodule "solana-accountsdb-connector"]
+	path = solana-accountsdb-connector
+	url = org-87542516@github.com:jito-foundation/solana-accountsdb-connector.git
diff --git a/Cargo.lock b/Cargo.lock
index 95d7e0df24..380e2635f6 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.5",
+ "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.8",
  "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,39 +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.47",
+ "quote 1.0.21",
+ "regex",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-account"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "bs58 0.4.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "rustversion",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-constant"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "proc-macro2 1.0.47",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-error"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-event"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-interface"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "heck 0.3.3",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-program"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-state"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-derive-accounts"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[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.1",
+ "bincode",
+ "borsh",
+ "bytemuck",
+ "solana-program 1.13.5",
+ "thiserror",
+]
+
+[[package]]
+name = "anchor-syn"
+version = "0.24.2"
+dependencies = [
+ "anyhow",
+ "bs58 0.3.1",
+ "heck 0.3.3",
+ "proc-macro2 1.0.47",
+ "proc-macro2-diagnostics",
+ "quote 1.0.21",
+ "serde",
+ "serde_json",
+ "sha2 0.9.9",
+ "syn 1.0.103",
+ "thiserror",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "ansi_term"
-version = "0.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.56"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
 
 [[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"
@@ -143,11 +291,11 @@ dependencies = [
  "asn1-rs-derive",
  "asn1-rs-impl",
  "displaydoc",
- "nom 7.0.0",
+ "nom",
  "num-traits",
  "rusticata-macros",
  "thiserror",
- "time 0.3.7",
+ "time 0.3.16",
 ]
 
 [[package]]
@@ -156,9 +304,9 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "synstructure",
 ]
 
@@ -168,16 +316,16 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "assert_cmd"
-version = "2.0.4"
+version = "2.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e"
+checksum = "d5c2ca00549910ec251e3bd15f87aeeb206c9456b9a77b43ff6c97c54042a472"
 dependencies = [
  "bstr",
  "doc-comment",
@@ -195,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",
@@ -218,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",
@@ -228,24 +376,24 @@ 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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "async-trait"
-version = "0.1.52"
+version = "0.1.58"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
+checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -261,15 +409,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "axum"
-version = "0.5.1"
+version = "0.5.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47594e438a243791dba58124b6669561f5baa14cb12046641d8008bf035e5a25"
+checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43"
 dependencies = [
  "async-trait",
  "axum-core",
@@ -279,11 +427,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",
@@ -296,9 +444,9 @@ dependencies = [
 
 [[package]]
 name = "axum-core"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b"
+checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc"
 dependencies = [
  "async-trait",
  "bytes",
@@ -317,18 +465,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
 dependencies = [
  "futures-core",
- "getrandom 0.2.3",
+ "getrandom 0.2.8",
  "instant",
  "pin-project-lite",
- "rand 0.8.4",
+ "rand 0.8.5",
  "tokio",
 ]
 
 [[package]]
 name = "base-x"
-version = "0.2.8"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
+checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
 
 [[package]]
 name = "base64"
@@ -338,15 +486,38 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
 
 [[package]]
 name = "base64"
-version = "0.13.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "base64ct"
-version = "1.3.3"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
+
+[[package]]
+name = "bench-get-confirmed-blocks-with-data"
+version = "1.13.5"
+dependencies = [
+ "env_logger",
+ "log",
+ "solana-sdk 1.13.5",
+ "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"
@@ -359,9 +530,9 @@ dependencies = [
 
 [[package]]
 name = "bindgen"
-version = "0.59.1"
+version = "0.59.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453c49e5950bb0eb63bb3df640e31618846c89d5b7faa54040d76e98e0134375"
+checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
 dependencies = [
  "bitflags",
  "cexpr",
@@ -369,8 +540,8 @@ dependencies = [
  "lazy_static",
  "lazycell",
  "peeking_take_while",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "regex",
  "rustc-hash",
  "shlex",
@@ -378,9 +549,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",
 ]
@@ -406,18 +577,6 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "bitvec"
-version = "0.19.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
 [[package]]
 name = "blake3"
 version = "1.3.1"
@@ -429,19 +588,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 +597,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.5",
+ "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.5",
-]
-
-[[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]]
@@ -485,7 +623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa"
 dependencies = [
  "borsh-derive",
- "hashbrown",
+ "hashbrown 0.11.2",
 ]
 
 [[package]]
@@ -497,8 +635,8 @@ dependencies = [
  "borsh-derive-internal",
  "borsh-schema-derive-internal",
  "proc-macro-crate 0.1.5",
- "proc-macro2 1.0.32",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -507,9 +645,9 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -518,9 +656,9 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -544,6 +682,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 +708,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.8.0"
+version = "3.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
 
 [[package]]
 name = "bv"
@@ -578,12 +722,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"
@@ -595,28 +733,28 @@ dependencies = [
 
 [[package]]
 name = "bytecount"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
+checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
 
 [[package]]
 name = "bytemuck"
-version = "1.8.0"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead"
+checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
 dependencies = [
  "bytemuck_derive",
 ]
 
 [[package]]
 name = "bytemuck_derive"
-version = "1.0.1"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54"
+checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -627,9 +765,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.1.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
 
 [[package]]
 name = "bytesize"
@@ -660,20 +798,19 @@ dependencies = [
 
 [[package]]
 name = "camino"
-version = "1.0.5"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
+checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "caps"
-version = "0.5.3"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61bf7211aad104ce2769ec05efcdfabf85ee84ac92461d142f22cf8badd0e54c"
+checksum = "938c50180feacea622ef3b8f4a496057c868dcf8ac7a64d781dd8f3f51a9c143"
 dependencies = [
- "errno",
  "libc",
  "thiserror",
 ]
@@ -695,7 +832,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
 dependencies = [
  "camino",
  "cargo-platform",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_json",
 ]
@@ -711,20 +848,20 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.71"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 dependencies = [
  "jobserver",
 ]
 
 [[package]]
 name = "cexpr"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db507a7679252d2276ed0dd8113c6875ec56d3089f9225b2b42c30cc1f8e5c89"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
 dependencies = [
- "nom 6.1.2",
+ "nom",
 ]
 
 [[package]]
@@ -741,23 +878,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
 dependencies = [
- "libc",
+ "iana-time-zone",
+ "js-sys",
  "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",
 ]
@@ -768,7 +907,7 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
 dependencies = [
- "generic-array 0.14.5",
+ "generic-array",
 ]
 
 [[package]]
@@ -783,9 +922,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",
@@ -794,9 +933,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",
@@ -809,43 +948,62 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "3.1.6"
+version = "3.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
+checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
 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.16.0",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "3.1.4"
+version = "3.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
 dependencies = [
  "heck 0.4.0",
  "proc-macro-error",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[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 = "cmake"
-version = "0.1.46"
+version = "0.1.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7b858541263efe664aead4a5209a4ae5c5d2811167d4ed4ee0944503f8d2089"
+checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a"
 dependencies = [
  "cc",
 ]
 
+[[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]]
 name = "combine"
 version = "3.8.1"
@@ -861,14 +1019,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",
@@ -902,28 +1059,28 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
 
 [[package]]
 name = "const_fn"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
+checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
 
 [[package]]
 name = "const_format"
-version = "0.2.22"
+version = "0.2.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22bc6cd49b0ec407b680c3e380182b6ac63b73991cb7602de350352fc309b614"
+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.32",
- "quote 1.0.10",
- "unicode-xid 0.2.2",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "unicode-xid 0.2.4",
 ]
 
 [[package]]
@@ -940,9 +1097,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",
@@ -968,18 +1125,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",
 ]
@@ -999,9 +1156,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.3"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdbfe11fe19ff083c48923cf179540e8cd0535903dc35e178a1fdeeb59aef51f"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-utils",
@@ -1009,9 +1166,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",
@@ -1033,12 +1190,11 @@ dependencies = [
 
 [[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]]
@@ -1049,11 +1205,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.5",
+ "generic-array",
  "typenum",
 ]
 
@@ -1063,7 +1219,7 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
 dependencies = [
- "generic-array 0.14.5",
+ "generic-array",
  "subtle",
 ]
 
@@ -1100,11 +1256,11 @@ dependencies = [
 
 [[package]]
 name = "ctrlc"
-version = "3.2.1"
+version = "3.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
+checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
 dependencies = [
- "nix",
+ "nix 0.25.0",
  "winapi 0.3.9",
 ]
 
@@ -1122,6 +1278,50 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "cxx"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "scratch",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
+dependencies = [
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
 [[package]]
 name = "dashmap"
 version = "4.0.2"
@@ -1133,6 +1333,17 @@ dependencies = [
  "rayon",
 ]
 
+[[package]]
+name = "dashmap"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c"
+dependencies = [
+ "cfg-if 1.0.0",
+ "num_cpus",
+ "parking_lot 0.12.1",
+]
+
 [[package]]
 name = "data-encoding"
 version = "2.3.2"
@@ -1156,8 +1367,8 @@ checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1"
 dependencies = [
  "asn1-rs",
  "displaydoc",
- "nom 7.0.0",
- "num-bigint 0.4.2",
+ "nom",
+ "num-bigint 0.4.3",
  "num-traits",
  "rusticata-macros",
 ]
@@ -1168,35 +1379,24 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0"
 
-[[package]]
-name = "derivative"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
-dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
-]
-
 [[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.32",
- "quote 1.0.10",
- "rustc_version 0.3.3",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "rustc_version 0.4.0",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "dialoguer"
-version = "0.10.0"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349d6b4fabcd9e97e1df1ae15395ac7e49fb144946a0d453959dc2696273b9da"
+checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
 dependencies = [
  "console",
  "tempfile",
@@ -1209,31 +1409,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.5",
+ "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",
 ]
@@ -1280,9 +1471,9 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1316,9 +1507,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
 
 [[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",
 ]
@@ -1346,26 +1537,26 @@ dependencies = [
  "derivation-path",
  "ed25519-dalek",
  "hmac 0.12.1",
- "sha2 0.10.2",
+ "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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "either"
-version = "1.6.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
 
 [[package]]
 name = "encode_unicode"
@@ -1375,9 +1566,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",
 ]
@@ -1397,22 +1588,23 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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.2",
+ "num-bigint 0.4.3",
  "num-traits",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "rustc_version 0.4.0",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1422,9 +1614,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb"
 dependencies = [
  "once_cell",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1478,15 +1670,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"
@@ -1499,22 +1685,22 @@ 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",
 ]
 
 [[package]]
 name = "fd-lock"
-version = "3.0.4"
+version = "3.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02ecad9808e0596f8956d14f7fa868f996290bd01c8d7329d6e5bc2bb76adf8f"
+checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517"
 dependencies = [
  "cfg-if 1.0.0",
  "rustix",
- "windows-sys 0.30.0",
+ "windows-sys 0.36.1",
 ]
 
 [[package]]
@@ -1525,9 +1711,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",
@@ -1536,31 +1722,35 @@ dependencies = [
 
 [[package]]
 name = "filetime"
-version = "0.2.15"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
+checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
  "redox_syscall",
- "winapi 0.3.9",
+ "windows-sys 0.42.0",
 ]
 
 [[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"
-version = "1.0.22"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
 dependencies = [
- "cfg-if 1.0.0",
  "crc32fast",
- "libc",
  "miniz_oxide",
 ]
 
@@ -1587,12 +1777,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]]
@@ -1607,12 +1796,6 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
 
-[[package]]
-name = "funty"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
-
 [[package]]
 name = "futures"
 version = "0.1.31"
@@ -1621,9 +1804,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
 
 [[package]]
 name = "futures"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
+checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1636,9 +1819,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1646,15 +1829,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
+checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1664,38 +1847,38 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
+checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
 
 [[package]]
 name = "futures-task"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
 
 [[package]]
 name = "futures-util"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
 dependencies = [
  "futures 0.1.31",
  "futures-channel",
@@ -1738,18 +1921,9 @@ dependencies = [
 
 [[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.5"
+version = "0.14.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
 dependencies = [
  "serde",
  "typenum",
@@ -1781,13 +1955,15 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.3"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
 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",
 ]
 
 [[package]]
@@ -1798,9 +1974,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",
@@ -1816,7 +1992,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38f3d68c8343245dc047982651b5afb8bd659c9959ed72efe5a73bf22684e5fd"
 dependencies = [
  "arc-swap",
- "futures 0.3.21",
+ "futures 0.3.25",
  "log",
  "reqwest",
  "serde",
@@ -1824,7 +2000,7 @@ dependencies = [
  "serde_json",
  "simpl",
  "smpl_jwt",
- "time 0.3.7",
+ "time 0.3.16",
  "tokio",
 ]
 
@@ -1841,9 +2017,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.3.11"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e"
+checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
 dependencies = [
  "bytes",
  "fnv",
@@ -1854,7 +2030,7 @@ dependencies = [
  "indexmap",
  "slab",
  "tokio",
- "tokio-util 0.6.9",
+ "tokio-util 0.7.2",
  "tracing",
 ]
 
@@ -1882,20 +2058,29 @@ dependencies = [
  "ahash",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
 [[package]]
 name = "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",
+ "base64 0.13.1",
  "bitflags",
  "bytes",
  "headers-core",
  "http",
  "httpdate",
  "mime",
- "sha-1 0.10.0",
+ "sha1 0.10.5",
 ]
 
 [[package]]
@@ -1939,9 +2124,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
 [[package]]
 name = "hidapi"
-version = "1.3.4"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2ec6bf425a5c3af047bb2a029de540a7d74cefa4761f14be67d7884dcd497b0"
+checksum = "9d26e1151deaab68f34fbfd16d491a2a0170cf98d69d3efa23873b567a4199e1"
 dependencies = [
  "cc",
  "libc",
@@ -1970,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]]
@@ -1980,7 +2165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1"
 dependencies = [
  "digest 0.9.0",
- "generic-array 0.14.5",
+ "generic-array",
  "hmac 0.8.1",
 ]
 
@@ -1992,7 +2177,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
 dependencies = [
  "bytes",
  "fnv",
- "itoa 1.0.1",
+ "itoa 1.0.4",
 ]
 
 [[package]]
@@ -2014,15 +2199,15 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
 
 [[package]]
 name = "httparse"
-version = "1.5.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
+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"
@@ -2032,9 +2217,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
-version = "0.14.14"
+version = "0.14.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b"
+checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -2045,7 +2230,7 @@ dependencies = [
  "http-body",
  "httparse",
  "httpdate",
- "itoa 0.4.8",
+ "itoa 1.0.4",
  "pin-project-lite",
  "socket2",
  "tokio",
@@ -2061,7 +2246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc"
 dependencies = [
  "bytes",
- "futures 0.3.21",
+ "futures 0.3.25",
  "headers",
  "http",
  "hyper",
@@ -2080,9 +2265,9 @@ checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
 dependencies = [
  "http",
  "hyper",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "tokio",
- "tokio-rustls 0.23.2",
+ "tokio-rustls 0.23.4",
 ]
 
 [[package]]
@@ -2110,6 +2295,30 @@ dependencies = [
  "tokio-native-tls",
 ]
 
+[[package]]
+name = "iana-time-zone"
+version = "0.1.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
 [[package]]
 name = "idna"
 version = "0.1.5"
@@ -2123,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",
 ]
@@ -2145,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",
@@ -2162,12 +2370,12 @@ checksum = "5a9d968042a4902e08810946fc7cd5851eb75e80301342305af755ca06cb82ce"
 
 [[package]]
 name = "indexmap"
-version = "1.8.1"
+version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
 dependencies = [
  "autocfg",
- "hashbrown",
+ "hashbrown 0.12.3",
  "rayon",
 ]
 
@@ -2185,11 +2393,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.5",
+ "generic-array",
 ]
 
 [[package]]
@@ -2203,21 +2411,21 @@ dependencies = [
 
 [[package]]
 name = "io-lifetimes"
-version = "0.5.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6"
+checksum = "e6e481ccbe3dea62107216d0d1138bb8ad8e5e5c43009a098bd1990272c497b0"
 
 [[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"
-version = "0.10.3"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
 dependencies = [
  "either",
 ]
@@ -2230,24 +2438,37 @@ 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.13.5"
+dependencies = [
+ "bytes",
+ "crossbeam-epoch",
+ "lock_api",
+ "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.55"
+version = "0.3.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -2270,7 +2491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a"
 dependencies = [
  "derive_more",
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-core",
  "jsonrpc-pubsub",
  "jsonrpc-server-utils",
@@ -2288,7 +2509,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "futures-executor",
  "futures-util",
  "log",
@@ -2303,7 +2524,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-client-transports",
 ]
 
@@ -2314,9 +2535,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2"
 dependencies = [
  "proc-macro-crate 0.1.5",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2325,7 +2546,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "hyper",
  "jsonrpc-core",
  "jsonrpc-server-utils",
@@ -2341,7 +2562,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "382bb0206323ca7cda3dcd7e245cea86d37d02457a02a975e3378fb149a48845"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-core",
  "jsonrpc-server-utils",
  "log",
@@ -2356,7 +2577,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-core",
  "lazy_static",
  "log",
@@ -2372,22 +2593,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4"
 dependencies = [
  "bytes",
- "futures 0.3.21",
+ "futures 0.3.25",
  "globset",
  "jsonrpc-core",
  "lazy_static",
  "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"
@@ -2413,9 +2634,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.120"
+version = "0.2.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
 
 [[package]]
 name = "libloading"
@@ -2429,9 +2650,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"
@@ -2497,26 +2718,35 @@ 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"
-version = "0.0.42"
+version = "0.0.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7"
+checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
 
 [[package]]
 name = "lock_api"
@@ -2529,20 +2759,20 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 dependencies = [
  "cfg-if 1.0.0",
 ]
 
 [[package]]
 name = "lru"
-version = "0.7.5"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
+checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.12.3",
 ]
 
 [[package]]
@@ -2565,12 +2795,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"
@@ -2585,24 +2809,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",
 ]
@@ -2615,7 +2839,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
 dependencies = [
  "byteorder",
  "keccak",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
  "zeroize",
 ]
 
@@ -2633,18 +2857,17 @@ 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.4.4"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
 dependencies = [
  "adler",
- "autocfg",
 ]
 
 [[package]]
@@ -2685,9 +2908,9 @@ version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2698,9 +2921,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",
@@ -2716,9 +2939,9 @@ dependencies = [
 
 [[package]]
 name = "net2"
-version = "0.2.37"
+version = "0.2.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -2739,33 +2962,32 @@ dependencies = [
 ]
 
 [[package]]
-name = "nom"
-version = "6.1.2"
+name = "nix"
+version = "0.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
+checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
 dependencies = [
- "bitvec",
- "funty",
- "memchr",
- "version_check",
+ "autocfg",
+ "bitflags",
+ "cfg-if 1.0.0",
+ "libc",
 ]
 
 [[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]]
 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",
 ]
@@ -2797,9 +3019,9 @@ dependencies = [
 
 [[package]]
 name = "num-bigint"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
 dependencies = [
  "autocfg",
  "num-integer",
@@ -2822,16 +3044,16 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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",
@@ -2862,9 +3084,9 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
  "autocfg",
 ]
@@ -2881,31 +3103,30 @@ dependencies = [
 
 [[package]]
 name = "num_enum"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f"
+checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
 dependencies = [
- "derivative",
  "num_enum_derive",
 ]
 
 [[package]]
 name = "num_enum_derive"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9"
+checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
 dependencies = [
- "proc-macro-crate 1.1.0",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro-crate 1.2.1",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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",
 ]
@@ -2927,15 +3148,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
-
-[[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"
@@ -2945,9 +3160,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
 name = "openssl"
-version = "0.10.40"
+version = "0.10.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e"
+checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
 dependencies = [
  "bitflags",
  "cfg-if 1.0.0",
@@ -2964,16 +3179,16 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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"
@@ -2986,9 +3201,9 @@ dependencies = [
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.73"
+version = "0.9.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
+checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
 dependencies = [
  "autocfg",
  "cc",
@@ -3006,23 +3221,20 @@ checksum = "e1cf9b1c4e9a6c4de793c632496fa490bdc0e1eea73f0c91394f7b6990935d22"
 dependencies = [
  "async-trait",
  "crossbeam-channel",
- "futures 0.3.21",
+ "futures 0.3.25",
  "js-sys",
  "lazy_static",
- "percent-encoding 2.1.0",
+ "percent-encoding 2.2.0",
  "pin-project",
- "rand 0.8.4",
+ "rand 0.8.5",
  "thiserror",
 ]
 
 [[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"
@@ -3043,9 +3255,9 @@ checksum = "ed9a247206016d424fe8497bc611e510887af5c261fbbf977877c4bb55ca4d82"
 dependencies = [
  "Inflector",
  "proc-macro-error",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -3054,7 +3266,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "libc",
  "log",
  "rand 0.7.3",
@@ -3075,12 +3287,12 @@ dependencies = [
 
 [[package]]
 name = "parking_lot"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
  "lock_api",
- "parking_lot_core 0.9.1",
+ "parking_lot_core 0.9.4",
 ]
 
 [[package]]
@@ -3099,15 +3311,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.1"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
+checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-sys 0.32.0",
+ "windows-sys 0.42.0",
 ]
 
 [[package]]
@@ -3125,7 +3337,16 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7"
 dependencies = [
- "digest 0.10.3",
+ "digest 0.10.5",
+]
+
+[[package]]
+name = "pbkdf2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
+dependencies = [
+ "digest 0.10.5",
 ]
 
 [[package]]
@@ -3136,11 +3357,11 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 
 [[package]]
 name = "pem"
-version = "1.0.2"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947"
+checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
 ]
 
 [[package]]
@@ -3151,9 +3372,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"
@@ -3166,18 +3387,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",
@@ -3185,35 +3407,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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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 0.10.5",
 ]
 
 [[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",
 ]
 
@@ -3232,29 +3464,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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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"
@@ -3275,9 +3507,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"
@@ -3293,21 +3525,21 @@ 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"
-version = "2.0.3"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474"
+checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
 dependencies = [
  "difflib",
  "itertools",
@@ -3316,15 +3548,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",
@@ -3338,12 +3570,12 @@ checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131"
 
 [[package]]
 name = "prettyplease"
-version = "0.1.9"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18"
+checksum = "c142c0e46b57171fe0c528bee8c5b7569e80f0c17e377cd0e30ea57dbc11bb51"
 dependencies = [
- "proc-macro2 1.0.32",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -3357,10 +3589,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",
 ]
@@ -3372,9 +3605,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 dependencies = [
  "proc-macro-error-attr",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "version_check",
 ]
 
@@ -3384,8 +3617,8 @@ version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "version_check",
 ]
 
@@ -3406,11 +3639,24 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.32"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
+checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
 dependencies = [
- "unicode-xid 0.2.2",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+ "version_check",
+ "yansi",
 ]
 
 [[package]]
@@ -3425,7 +3671,7 @@ dependencies = [
  "lazy_static",
  "num-traits",
  "quick-error 2.0.1",
- "rand 0.8.4",
+ "rand 0.8.5",
  "rand_chacha 0.3.1",
  "rand_xorshift",
  "regex-syntax",
@@ -3433,6 +3679,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"
@@ -3445,12 +3701,30 @@ dependencies = [
 
 [[package]]
 name = "prost"
-version = "0.10.0"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e"
+dependencies = [
+ "bytes",
+ "prost-derive 0.10.1",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bd5316aa8f5c82add416dfbc25116b84b748a21153f512917e8143640a71bbd"
+checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603"
 dependencies = [
  "bytes",
- "prost-derive 0.10.0",
+ "heck 0.3.3",
+ "itertools",
+ "log",
+ "multimap",
+ "petgraph 0.5.1",
+ "prost 0.8.0",
+ "prost-types 0.8.0",
+ "tempfile",
+ "which",
 ]
 
 [[package]]
@@ -3465,7 +3739,7 @@ dependencies = [
  "lazy_static",
  "log",
  "multimap",
- "petgraph",
+ "petgraph 0.6.2",
  "prost 0.9.0",
  "prost-types 0.9.0",
  "regex",
@@ -3475,9 +3749,9 @@ dependencies = [
 
 [[package]]
 name = "prost-build"
-version = "0.10.0"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "328f9f29b82409216decb172d81e936415d21245befa79cd34c3f29d87d1c50b"
+checksum = "8ae5a4388762d5815a9fc0dea33c56b021cdc8dde0c55e0c9ca57197254b0cab"
 dependencies = [
  "bytes",
  "cfg-if 1.0.0",
@@ -3487,14 +3761,27 @@ dependencies = [
  "lazy_static",
  "log",
  "multimap",
- "petgraph",
- "prost 0.10.0",
- "prost-types 0.10.0",
+ "petgraph 0.6.2",
+ "prost 0.10.4",
+ "prost-types 0.10.1",
  "regex",
  "tempfile",
  "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.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
 [[package]]
 name = "prost-derive"
 version = "0.9.0"
@@ -3503,22 +3790,32 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
 dependencies = [
  "anyhow",
  "itertools",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "prost-derive"
-version = "0.10.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df35198f0777b75e9ff669737c6da5136b59dba33cf5a010a6d1cc4d56defc6f"
+checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc"
 dependencies = [
  "anyhow",
  "itertools",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[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]]
@@ -3533,12 +3830,12 @@ dependencies = [
 
 [[package]]
 name = "prost-types"
-version = "0.10.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "926681c118ae6e512a3ccefd4abbe5521a14f4cc1e207356d4d00c0b7f2006fd"
+checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68"
 dependencies = [
  "bytes",
- "prost 0.10.0",
+ "prost 0.10.4",
 ]
 
 [[package]]
@@ -3547,7 +3844,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]]
@@ -3564,9 +3861,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
 
 [[package]]
 name = "quinn"
-version = "0.8.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7542006acd6e057ff632307d219954c44048f818898da03113d6c0086bfddd9"
+checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -3574,7 +3871,7 @@ dependencies = [
  "fxhash",
  "quinn-proto",
  "quinn-udp",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "thiserror",
  "tokio",
  "tracing",
@@ -3583,16 +3880,16 @@ dependencies = [
 
 [[package]]
 name = "quinn-proto"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a13a5c0a674c1ce7150c9df7bc4a1e46c2fbbe7c710f56c0dc78b1a810e779e"
+checksum = "3fce546b9688f767a57530652488420d419a8b1f44a478b451c3d1ab6d992a55"
 dependencies = [
  "bytes",
  "fxhash",
- "rand 0.8.4",
+ "rand 0.8.5",
  "ring",
- "rustls 0.20.4",
- "rustls-native-certs",
+ "rustls 0.20.7",
+ "rustls-native-certs 0.6.2",
  "rustls-pemfile 0.2.1",
  "slab",
  "thiserror",
@@ -3603,13 +3900,12 @@ dependencies = [
 
 [[package]]
 name = "quinn-udp"
-version = "0.1.0"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f7996776e9ee3fc0e5c14476c1a640a17e993c847ae9c81191c2c102fbef903"
+checksum = "9f832d8958db3e84d2ec93b5eb2272b45aa23cf7f8fe6e79f578896f4e6c231b"
 dependencies = [
  "futures-util",
  "libc",
- "mio",
  "quinn-proto",
  "socket2",
  "tokio",
@@ -3627,19 +3923,13 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.10"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
 dependencies = [
- "proc-macro2 1.0.32",
+ "proc-macro2 1.0.47",
 ]
 
-[[package]]
-name = "radium"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
-
 [[package]]
 name = "rand"
 version = "0.4.6"
@@ -3663,20 +3953,19 @@ dependencies = [
  "libc",
  "rand_chacha 0.2.2",
  "rand_core 0.5.1",
- "rand_hc 0.2.0",
+ "rand_hc",
  "rand_pcg",
 ]
 
 [[package]]
 name = "rand"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha 0.3.1",
- "rand_core 0.6.3",
- "rand_hc 0.3.1",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -3696,7 +3985,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]]
@@ -3725,11 +4014,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.8",
 ]
 
 [[package]]
@@ -3741,15 +4030,6 @@ dependencies = [
  "rand_core 0.5.1",
 ]
 
-[[package]]
-name = "rand_hc"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
-dependencies = [
- "rand_core 0.6.3",
-]
-
 [[package]]
 name = "rand_pcg"
 version = "0.2.1"
@@ -3765,7 +4045,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]]
@@ -3774,7 +4054,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]]
@@ -3785,9 +4065,9 @@ checksum = "5cb37e7b5c272e9d7d75d3ab9d4f3a028edfbb4e99a2f35ec887057ea51656ad"
 
 [[package]]
 name = "rayon"
-version = "1.5.1"
+version = "1.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
 dependencies = [
  "autocfg",
  "crossbeam-deque",
@@ -3797,14 +4077,13 @@ dependencies = [
 
 [[package]]
 name = "rayon-core"
-version = "1.9.1"
+version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
  "crossbeam-utils",
- "lazy_static",
  "num_cpus",
 ]
 
@@ -3812,7 +4091,7 @@ dependencies = [
 name = "rbpf-cli"
 version = "1.13.5"
 dependencies = [
- "clap 3.1.6",
+ "clap 3.2.23",
  "serde",
  "serde_json",
  "solana-bpf-loader-program",
@@ -3824,13 +4103,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.7",
+ "time 0.3.16",
  "yasna",
 ]
 
@@ -3845,42 +4124,43 @@ 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.8",
  "redox_syscall",
+ "thiserror",
 ]
 
 [[package]]
 name = "reed-solomon-erasure"
-version = "5.0.1"
+version = "5.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170bac0d8306941e101df0caaa6518b10bc4232dd36c34f1cb78b8a063024db"
+checksum = "c2fe31452b684b8b33f65f8730c8b8812c3f5a0bb8a096934717edb1ac488641"
 dependencies = [
  "cc",
  "libc",
  "libm",
  "parking_lot 0.11.2",
  "smallvec",
- "spin 0.9.2",
+ "spin 0.9.4",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.5"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -3895,9 +4175,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
 
 [[package]]
 name = "remove_dir_all"
@@ -3910,12 +4190,12 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.11.10"
+version = "0.11.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
+checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
 dependencies = [
  "async-compression",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -3928,34 +4208,35 @@ dependencies = [
  "hyper-tls",
  "ipnet",
  "js-sys",
- "lazy_static",
  "log",
  "mime",
  "native-tls",
- "percent-encoding 2.1.0",
+ "once_cell",
+ "percent-encoding 2.2.0",
  "pin-project-lite",
- "rustls 0.20.4",
- "rustls-pemfile 0.3.0",
+ "rustls 0.20.7",
+ "rustls-pemfile 1.0.1",
  "serde",
  "serde_json",
  "serde_urlencoded",
  "tokio",
  "tokio-native-tls",
- "tokio-rustls 0.23.2",
- "tokio-util 0.6.9",
- "url 2.2.2",
+ "tokio-rustls 0.23.4",
+ "tokio-util 0.7.2",
+ "tower-service",
+ "url 2.3.1",
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "webpki-roots",
+ "webpki-roots 0.22.5",
  "winreg",
 ]
 
 [[package]]
 name = "retain_mut"
-version = "0.1.7"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086"
+checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
 
 [[package]]
 name = "ring"
@@ -4015,22 +4296,13 @@ dependencies = [
  "semver 0.9.0",
 ]
 
-[[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.6",
+ "semver 1.0.14",
 ]
 
 [[package]]
@@ -4039,21 +4311,21 @@ version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
 dependencies = [
- "nom 7.0.0",
+ "nom",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.33.3"
+version = "0.35.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9466f25b92a648960ac1042fd3baa6b0bf285e60f754d7e5070770c813a177a"
+checksum = "985947f9b6423159c4726323f373be0a21bdb514c5af06a849cb3d2dce2d01e8"
 dependencies = [
  "bitflags",
  "errno",
  "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "winapi 0.3.9",
+ "windows-sys 0.36.1",
 ]
 
 [[package]]
@@ -4062,7 +4334,7 @@ version = "0.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "log",
  "ring",
  "sct 0.6.1",
@@ -4071,9 +4343,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.20.4"
+version = "0.20.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921"
+checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
 dependencies = [
  "log",
  "ring",
@@ -4083,12 +4355,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",
 ]
@@ -4099,23 +4383,23 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
 ]
 
 [[package]]
 name = "rustls-pemfile"
-version = "0.3.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
+checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
 ]
 
 [[package]]
 name = "rustversion"
-version = "1.0.6"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
+checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
 
 [[package]]
 name = "rusty-fork"
@@ -4131,9 +4415,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"
@@ -4146,12 +4430,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 0.36.1",
 ]
 
 [[package]]
@@ -4160,6 +4444,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.10.2"
@@ -4175,9 +4465,9 @@ version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -4202,9 +4492,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",
@@ -4215,9 +4505,9 @@ 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",
@@ -4229,23 +4519,14 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
 dependencies = [
- "semver-parser 0.7.0",
-]
-
-[[package]]
-name = "semver"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
-dependencies = [
- "semver-parser 0.10.2",
+ "semver-parser",
 ]
 
 [[package]]
 name = "semver"
-version = "1.0.6"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d"
+checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
 dependencies = [
  "serde",
 ]
@@ -4256,29 +4537,20 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
-[[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.136"
+version = "1.0.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_bytes"
-version = "0.11.5"
+version = "0.11.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9"
+checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b"
 dependencies = [
  "serde",
 ]
@@ -4295,22 +4567,22 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.79"
+version = "1.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
 dependencies = [
- "itoa 1.0.1",
+ "itoa 1.0.4",
  "ryu",
  "serde",
 ]
@@ -4322,16 +4594,16 @@ 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",
 ]
 
 [[package]]
 name = "serde_yaml"
-version = "0.8.23"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
+checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
 dependencies = [
  "indexmap",
  "ryu",
@@ -4347,7 +4619,21 @@ checksum = "e5bcc41d18f7a1d50525d080fd3e953be87c4f9f1a974f3c21798ca00d54ec15"
 dependencies = [
  "lazy_static",
  "parking_lot 0.11.2",
- "serial_test_derive",
+ "serial_test_derive 0.6.0",
+]
+
+[[package]]
+name = "serial_test"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153"
+dependencies = [
+ "dashmap 5.2.0",
+ "futures 0.3.25",
+ "lazy_static",
+ "log",
+ "parking_lot 0.12.1",
+ "serial_test_derive 0.9.0",
 ]
 
 [[package]]
@@ -4357,22 +4643,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2881bccd7d60fb32dfa3d7b3136385312f8ad75e2674aab2852867a09790cae8"
 dependencies = [
  "proc-macro-error",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
-name = "sha-1"
-version = "0.8.2"
+name = "serial_test_derive"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5"
 dependencies = [
- "block-buffer 0.7.3",
- "digest 0.8.1",
- "fake-simd",
- "opaque-debug 0.2.3",
+ "proc-macro-error",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -4385,7 +4671,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures",
  "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -4396,14 +4682,34 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
 dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures",
- "digest 0.10.3",
+ "digest 0.10.5",
 ]
 
 [[package]]
 name = "sha1"
-version = "0.6.0"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+dependencies = [
+ "sha1_smol",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+dependencies = [
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest 0.10.5",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
 
 [[package]]
 name = "sha2"
@@ -4415,18 +4721,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.2"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
+checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
 dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures",
- "digest 0.10.3",
+ "digest 0.10.5",
 ]
 
 [[package]]
@@ -4438,16 +4744,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.1"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86"
+checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9"
 dependencies = [
- "digest 0.10.3",
+ "digest 0.10.5",
  "keccak",
 ]
 
@@ -4468,9 +4774,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
 
 [[package]]
 name = "signal-hook"
-version = "0.3.13"
+version = "0.3.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
+checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
 dependencies = [
  "libc",
  "signal-hook-registry",
@@ -4487,9 +4793,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"
@@ -4509,15 +4815,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"
@@ -4525,7 +4834,7 @@ version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4370044f8b20f944e05c35d77edd3518e6f21fc4de77e593919f287c6a3f428a"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "log",
  "openssl",
  "serde",
@@ -4537,9 +4846,9 @@ dependencies = [
 
 [[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",
@@ -4551,12 +4860,12 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
- "futures 0.3.21",
+ "futures 0.3.25",
  "httparse",
  "log",
- "rand 0.8.4",
+ "rand 0.8.5",
  "sha-1 0.9.8",
 ]
 
@@ -4565,9 +4874,9 @@ name = "solana-account-decoder"
 version = "1.13.5"
 dependencies = [
  "Inflector",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "bv",
  "lazy_static",
  "serde",
@@ -4586,7 +4895,7 @@ dependencies = [
 name = "solana-accounts-bench"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "log",
  "rayon",
  "solana-logger 1.13.5",
@@ -4600,7 +4909,7 @@ dependencies = [
 name = "solana-accounts-cluster-bench"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "log",
  "rand 0.7.3",
  "rayon",
@@ -4657,7 +4966,7 @@ dependencies = [
 name = "solana-banking-bench"
 version = "1.13.5"
 dependencies = [
- "clap 3.1.6",
+ "clap 3.2.23",
  "crossbeam-channel",
  "log",
  "rand 0.7.3",
@@ -4681,7 +4990,7 @@ name = "solana-banks-client"
 version = "1.13.5"
 dependencies = [
  "borsh",
- "futures 0.3.21",
+ "futures 0.3.25",
  "solana-banks-interface",
  "solana-banks-server",
  "solana-program 1.13.5",
@@ -4708,23 +5017,40 @@ version = "1.13.5"
 dependencies = [
  "bincode",
  "crossbeam-channel",
- "futures 0.3.21",
+ "futures 0.3.25",
  "solana-banks-interface",
  "solana-client",
+ "solana-gossip",
  "solana-runtime",
  "solana-sdk 1.13.5",
  "solana-send-transaction-service",
+ "solana-streamer",
  "tarpc",
  "tokio",
  "tokio-serde",
  "tokio-stream",
 ]
 
+[[package]]
+name = "solana-bench-batch-simulate-bundle"
+version = "1.13.5"
+dependencies = [
+ "clap 3.2.23",
+ "env_logger",
+ "log",
+ "num-traits",
+ "rayon",
+ "solana-client",
+ "solana-runtime",
+ "solana-sdk 1.13.5",
+ "solana-transaction-status",
+]
+
 [[package]]
 name = "solana-bench-streamer"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "solana-net-utils",
  "solana-streamer",
@@ -4735,13 +5061,13 @@ dependencies = [
 name = "solana-bench-tps"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "log",
  "rayon",
  "serde_json",
  "serde_yaml",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-clap-utils",
  "solana-cli-config",
  "solana-client",
@@ -4821,9 +5147,9 @@ version = "1.13.5"
 dependencies = [
  "bzip2",
  "cargo_metadata",
- "clap 3.1.6",
+ "clap 3.2.23",
  "regex",
- "serial_test",
+ "serial_test 0.9.0",
  "solana-download-utils",
  "solana-sdk 1.13.5",
  "tar",
@@ -4834,7 +5160,7 @@ name = "solana-cargo-test-bpf"
 version = "1.13.5"
 dependencies = [
  "cargo_metadata",
- "clap 3.1.6",
+ "clap 3.2.23",
 ]
 
 [[package]]
@@ -4842,7 +5168,7 @@ name = "solana-clap-utils"
 version = "1.13.5"
 dependencies = [
  "chrono",
- "clap 2.33.3",
+ "clap 2.34.0",
  "rpassword",
  "solana-perf",
  "solana-remote-wallet",
@@ -4851,7 +5177,7 @@ dependencies = [
  "thiserror",
  "tiny-bip39",
  "uriparse",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -4859,8 +5185,8 @@ name = "solana-cli"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "bs58",
- "clap 2.33.3",
+ "bs58 0.4.0",
+ "clap 2.34.0",
  "console",
  "const_format",
  "criterion-stats",
@@ -4871,7 +5197,7 @@ dependencies = [
  "num-traits",
  "pretty-hex",
  "reqwest",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_derive",
  "serde_json",
@@ -4911,7 +5237,7 @@ dependencies = [
  "serde_yaml",
  "solana-clap-utils",
  "solana-sdk 1.13.5",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -4919,14 +5245,14 @@ name = "solana-cli-output"
 version = "1.13.5"
 dependencies = [
  "Inflector",
- "base64 0.13.0",
+ "base64 0.13.1",
  "chrono",
- "clap 2.33.3",
+ "clap 2.34.0",
  "console",
  "ed25519-dalek",
  "humantime",
  "indicatif",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_json",
  "solana-account-decoder",
@@ -4946,14 +5272,14 @@ dependencies = [
  "assert_matches",
  "async-mutex",
  "async-trait",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "bytes",
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "enum_dispatch",
- "futures 0.3.21",
+ "futures 0.3.25",
  "futures-util",
  "indexmap",
  "indicatif",
@@ -4969,8 +5295,8 @@ dependencies = [
  "rand_chacha 0.2.2",
  "rayon",
  "reqwest",
- "rustls 0.20.4",
- "semver 1.0.6",
+ "rustls 0.20.7",
+ "semver 1.0.14",
  "serde",
  "serde_derive",
  "serde_json",
@@ -4993,7 +5319,7 @@ dependencies = [
  "tokio-stream",
  "tokio-tungstenite",
  "tungstenite",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -5002,7 +5328,7 @@ version = "1.13.5"
 dependencies = [
  "futures-util",
  "serde_json",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-client",
  "solana-ledger",
  "solana-logger 1.13.5",
@@ -5048,20 +5374,31 @@ name = "solana-core"
 version = "1.13.5"
 dependencies = [
  "ahash",
- "base64 0.13.0",
+ "anchor-lang",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
+ "bytes",
  "chrono",
+ "clap 3.2.23",
  "crossbeam-channel",
- "dashmap",
+ "dashmap 4.0.2",
  "etcd-client",
  "fs_extra",
+ "futures 0.3.25",
+ "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",
@@ -5072,7 +5409,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-address-lookup-table-program",
  "solana-bloom",
  "solana-client",
@@ -5106,8 +5443,14 @@ dependencies = [
  "systemstat",
  "tempfile",
  "thiserror",
+ "tip-distribution",
+ "tip-payment",
  "tokio",
+ "tokio-stream",
+ "tonic 0.5.2",
+ "tonic-build 0.5.2",
  "trees",
+ "uuid",
 ]
 
 [[package]]
@@ -5115,7 +5458,7 @@ name = "solana-dos"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "clap 3.1.6",
+ "clap 3.2.23",
  "log",
  "rand 0.7.3",
  "serde",
@@ -5182,7 +5525,7 @@ version = "1.13.5"
 dependencies = [
  "bincode",
  "byteorder",
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "log",
  "serde",
@@ -5200,13 +5543,11 @@ dependencies = [
 
 [[package]]
 name = "solana-frozen-abi"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49a5d3280421bb53fc12bdba1eaa505153fb4f99a06b5609dae22192652ead3b"
+version = "1.13.5"
 dependencies = [
- "bs58",
+ "bs58 0.4.0",
  "bv",
- "generic-array 0.14.5",
+ "generic-array",
  "im",
  "lazy_static",
  "log",
@@ -5215,60 +5556,74 @@ dependencies = [
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "solana-frozen-abi-macro 1.10.33",
+ "sha2 0.10.6",
+ "solana-frozen-abi-macro 1.13.5",
+ "solana-logger 1.13.5",
  "thiserror",
 ]
 
 [[package]]
 name = "solana-frozen-abi"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fae9453c906a52b00c6d668508214377163cf59776cfa8e09ef740a2a493f87d"
 dependencies = [
- "bs58",
+ "ahash",
+ "blake3",
+ "block-buffer 0.9.0",
+ "bs58 0.4.0",
  "bv",
- "generic-array 0.14.5",
+ "byteorder",
+ "cc",
+ "either",
+ "generic-array",
+ "getrandom 0.1.16",
+ "hashbrown 0.12.3",
  "im",
  "lazy_static",
  "log",
  "memmap2",
+ "once_cell",
+ "rand_core 0.6.4",
  "rustc_version 0.4.0",
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "solana-frozen-abi-macro 1.13.5",
- "solana-logger 1.13.5",
+ "serde_json",
+ "sha2 0.10.6",
+ "solana-frozen-abi-macro 1.14.6",
+ "subtle",
  "thiserror",
 ]
 
 [[package]]
 name = "solana-frozen-abi-macro"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "635c60ac96b1347af272c625465068b908aff919d19f29b5795a44310310494d"
+version = "1.13.5"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustc_version 0.4.0",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "solana-frozen-abi-macro"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e29cd0fef92aee046267cdd69d8aa8b31cd3549991ea6f2c6076b21064e235fb"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustc_version 0.4.0",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "solana-genesis"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
- "clap 2.33.3",
+ "base64 0.13.1",
+ "clap 2.34.0",
  "serde",
  "serde_json",
  "serde_yaml",
@@ -5308,7 +5663,7 @@ dependencies = [
 name = "solana-geyser-plugin-manager"
 version = "1.13.5"
 dependencies = [
- "bs58",
+ "bs58 0.4.0",
  "crossbeam-channel",
  "json5",
  "libloading",
@@ -5330,7 +5685,7 @@ version = "1.13.5"
 dependencies = [
  "bincode",
  "bv",
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "flate2",
  "indexmap",
@@ -5347,7 +5702,7 @@ dependencies = [
  "serde",
  "serde_bytes",
  "serde_derive",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-bloom",
  "solana-clap-utils",
  "solana-client",
@@ -5377,16 +5732,16 @@ dependencies = [
  "bincode",
  "bzip2",
  "chrono",
- "clap 2.33.3",
+ "clap 2.34.0",
  "console",
  "crossbeam-channel",
  "ctrlc",
  "dirs-next",
  "indicatif",
  "lazy_static",
- "nix",
+ "nix 0.23.1",
  "reqwest",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_yaml",
  "solana-clap-utils",
@@ -5397,7 +5752,7 @@ dependencies = [
  "solana-version",
  "tar",
  "tempfile",
- "url 2.2.2",
+ "url 2.3.1",
  "winapi 0.3.9",
  "winreg",
 ]
@@ -5406,8 +5761,8 @@ dependencies = [
 name = "solana-keygen"
 version = "1.13.5"
 dependencies = [
- "bs58",
- "clap 2.33.3",
+ "bs58 0.4.0",
+ "clap 2.34.0",
  "dirs-next",
  "num_cpus",
  "solana-clap-utils",
@@ -5430,7 +5785,7 @@ dependencies = [
  "chrono-humanize",
  "crossbeam-channel",
  "fs_extra",
- "futures 0.3.21",
+ "futures 0.3.25",
  "itertools",
  "lazy_static",
  "libc",
@@ -5439,7 +5794,7 @@ dependencies = [
  "matches",
  "num_cpus",
  "num_enum",
- "prost 0.10.0",
+ "prost 0.10.4",
  "rand 0.7.3",
  "rand_chacha 0.2.2",
  "rayon",
@@ -5448,7 +5803,7 @@ dependencies = [
  "rustc_version 0.4.0",
  "serde",
  "serde_bytes",
- "sha2 0.10.2",
+ "sha2 0.10.6",
  "solana-account-decoder",
  "solana-bpf-loader-program",
  "solana-entry",
@@ -5480,12 +5835,12 @@ name = "solana-ledger-tool"
 version = "1.13.5"
 dependencies = [
  "assert_cmd",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytecount",
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "csv",
- "dashmap",
+ "dashmap 4.0.2",
  "histogram",
  "itertools",
  "log",
@@ -5523,7 +5878,7 @@ dependencies = [
  "log",
  "rand 0.7.3",
  "rayon",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-client",
  "solana-config-program",
  "solana-core",
@@ -5545,7 +5900,7 @@ name = "solana-log-analyzer"
 version = "1.13.5"
 dependencies = [
  "byte-unit",
- "clap 2.33.3",
+ "clap 2.34.0",
  "serde",
  "serde_json",
  "solana-logger 1.13.5",
@@ -5554,9 +5909,7 @@ dependencies = [
 
 [[package]]
 name = "solana-logger"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b12cb6e6f1f9c9876d356c928b8c2ac532f6715e7cd2a1b4343d747bee3eca73"
+version = "1.13.5"
 dependencies = [
  "env_logger",
  "lazy_static",
@@ -5565,7 +5918,9 @@ dependencies = [
 
 [[package]]
 name = "solana-logger"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d255b73f0c0e1eaa34280094f1304a783ea9394dbf2f8b749a3661e948ca5551"
 dependencies = [
  "env_logger",
  "lazy_static",
@@ -5584,7 +5939,7 @@ dependencies = [
 name = "solana-merkle-root-bench"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "log",
  "solana-logger 1.13.5",
  "solana-measure",
@@ -5614,7 +5969,7 @@ dependencies = [
  "log",
  "rand 0.7.3",
  "reqwest",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-sdk 1.13.5",
 ]
 
@@ -5622,7 +5977,7 @@ dependencies = [
 name = "solana-net-shaper"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "rand 0.7.3",
  "serde",
  "serde_json",
@@ -5634,10 +5989,10 @@ name = "solana-net-utils"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "log",
- "nix",
+ "nix 0.23.1",
  "rand 0.7.3",
  "serde",
  "serde_derive",
@@ -5646,7 +6001,7 @@ dependencies = [
  "solana-sdk 1.13.5",
  "solana-version",
  "tokio",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -5674,7 +6029,7 @@ dependencies = [
  "libc",
  "log",
  "matches",
- "nix",
+ "nix 0.23.1",
  "rand 0.7.3",
  "rayon",
  "serde",
@@ -5711,7 +6066,7 @@ dependencies = [
 name = "solana-poh-bench"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "log",
  "rand 0.7.3",
  "rayon",
@@ -5725,17 +6080,17 @@ dependencies = [
 
 [[package]]
 name = "solana-program"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeecf504cee2821b006871f70e7a1f54db15f914cedf259eaf5976fe606470f0"
+version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "anyhow",
+ "assert_matches",
+ "base64 0.13.1",
  "bincode",
  "bitflags",
  "blake3",
  "borsh",
  "borsh-derive",
- "bs58",
+ "bs58 0.4.0",
  "bv",
  "bytemuck",
  "console_error_panic_hook",
@@ -5749,72 +6104,79 @@ dependencies = [
  "log",
  "num-derive",
  "num-traits",
- "parking_lot 0.12.0",
+ "parking_lot 0.12.1",
  "rand 0.7.3",
  "rustc_version 0.4.0",
  "rustversion",
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.10.33",
- "solana-frozen-abi-macro 1.10.33",
- "solana-sdk-macro 1.10.33",
+ "serde_json",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.13.5",
+ "solana-frozen-abi-macro 1.13.5",
+ "solana-logger 1.13.5",
+ "solana-sdk-macro 1.13.5",
+ "static_assertions",
  "thiserror",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "solana-program"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0eeda17e271e11864d48b35eeaefed9591305b4d47266caf3f8b11b9271833d"
 dependencies = [
- "anyhow",
- "assert_matches",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bitflags",
  "blake3",
  "borsh",
  "borsh-derive",
- "bs58",
+ "bs58 0.4.0",
  "bv",
  "bytemuck",
+ "cc",
  "console_error_panic_hook",
  "console_log",
  "curve25519-dalek",
- "getrandom 0.1.16",
+ "getrandom 0.2.8",
  "itertools",
  "js-sys",
  "lazy_static",
+ "libc",
  "libsecp256k1",
  "log",
+ "memoffset",
  "num-derive",
  "num-traits",
- "parking_lot 0.12.0",
+ "parking_lot 0.12.1",
  "rand 0.7.3",
+ "rand_chacha 0.2.2",
  "rustc_version 0.4.0",
  "rustversion",
  "serde",
  "serde_bytes",
  "serde_derive",
  "serde_json",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.13.5",
- "solana-frozen-abi-macro 1.13.5",
- "solana-logger 1.13.5",
- "solana-sdk-macro 1.13.5",
- "static_assertions",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.14.6",
+ "solana-frozen-abi-macro 1.14.6",
+ "solana-sdk-macro 1.14.6",
  "thiserror",
+ "tiny-bip39",
  "wasm-bindgen",
+ "zeroize",
 ]
 
 [[package]]
 name = "solana-program-runtime"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "enum-iterator",
  "itertools",
@@ -5838,7 +6200,7 @@ name = "solana-program-test"
 version = "1.13.5"
 dependencies = [
  "async-trait",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "chrono-humanize",
  "log",
@@ -5873,9 +6235,9 @@ dependencies = [
  "log",
  "num-derive",
  "num-traits",
- "parking_lot 0.12.0",
+ "parking_lot 0.12.1",
  "qstring",
- "semver 1.0.6",
+ "semver 1.0.14",
  "solana-sdk 1.13.5",
  "thiserror",
  "uriparse",
@@ -5888,24 +6250,24 @@ dependencies = [
  "crossbeam-channel",
  "futures-util",
  "log",
- "prost 0.10.0",
+ "prost 0.10.4",
  "solana-rpc",
  "solana-runtime",
  "solana-sdk 1.13.5",
  "tokio",
- "tonic 0.7.1",
- "tonic-build 0.7.0",
+ "tonic 0.7.2",
+ "tonic-build 0.7.2",
 ]
 
 [[package]]
 name = "solana-replica-node"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "crossbeam-channel",
  "log",
  "rand 0.7.3",
- "serial_test",
+ "serial_test 0.6.0",
  "solana-clap-utils",
  "solana-client",
  "solana-core",
@@ -5925,18 +6287,18 @@ dependencies = [
  "solana-validator",
  "solana-version",
  "tempfile",
- "tonic-build 0.7.0",
+ "tonic-build 0.7.2",
 ]
 
 [[package]]
 name = "solana-rpc"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "crossbeam-channel",
- "dashmap",
+ "dashmap 4.0.2",
  "itertools",
  "jsonrpc-core",
  "jsonrpc-core-client",
@@ -5950,7 +6312,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "serial_test",
+ "serial_test 0.6.0",
  "soketto",
  "solana-account-decoder",
  "solana-address-lookup-table-program",
@@ -5980,7 +6342,7 @@ dependencies = [
  "symlink",
  "thiserror",
  "tokio",
- "tokio-util 0.6.9",
+ "tokio-util 0.6.10",
 ]
 
 [[package]]
@@ -5988,7 +6350,7 @@ name = "solana-rpc-test"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "crossbeam-channel",
  "futures-util",
  "log",
@@ -6019,7 +6381,7 @@ dependencies = [
  "byteorder",
  "bzip2",
  "crossbeam-channel",
- "dashmap",
+ "dashmap 4.0.2",
  "dir-diff",
  "ed25519-dalek",
  "flate2",
@@ -6070,24 +6432,25 @@ dependencies = [
 
 [[package]]
 name = "solana-sdk"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "636f6c615aca6f75e22b6baceaf0ffed9d74367f9320b07ed57cd9b5ce2e4ff9"
+version = "1.13.5"
 dependencies = [
+ "anchor-lang",
+ "anyhow",
  "assert_matches",
- "base64 0.13.0",
+ "base64 0.13.1",
  "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.5",
+ "generic-array",
  "hmac 0.12.1",
  "itertools",
  "js-sys",
@@ -6107,38 +6470,40 @@ dependencies = [
  "serde_bytes",
  "serde_derive",
  "serde_json",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.10.33",
- "solana-frozen-abi-macro 1.10.33",
- "solana-logger 1.10.33",
- "solana-program 1.10.33",
- "solana-sdk-macro 1.10.33",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.13.5",
+ "solana-frozen-abi-macro 1.13.5",
+ "solana-logger 1.13.5",
+ "solana-program 1.13.5",
+ "solana-sdk-macro 1.13.5",
  "thiserror",
+ "tiny-bip39",
  "uriparse",
+ "uuid",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "solana-sdk"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57fe62f7fe938607437ed29b0e95b4cffc7db186bf04fea6a98d92319da941b1"
 dependencies = [
- "anyhow",
  "assert_matches",
- "base64 0.13.0",
+ "base64 0.13.1",
  "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.5",
+ "generic-array",
  "hmac 0.12.1",
  "itertools",
  "js-sys",
@@ -6148,7 +6513,7 @@ dependencies = [
  "memmap2",
  "num-derive",
  "num-traits",
- "pbkdf2 0.10.1",
+ "pbkdf2 0.11.0",
  "qstring",
  "rand 0.7.3",
  "rand_chacha 0.2.2",
@@ -6158,41 +6523,40 @@ dependencies = [
  "serde_bytes",
  "serde_derive",
  "serde_json",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.13.5",
- "solana-frozen-abi-macro 1.13.5",
- "solana-logger 1.13.5",
- "solana-program 1.13.5",
- "solana-sdk-macro 1.13.5",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.14.6",
+ "solana-frozen-abi-macro 1.14.6",
+ "solana-logger 1.14.6",
+ "solana-program 1.14.6",
+ "solana-sdk-macro 1.14.6",
  "thiserror",
- "tiny-bip39",
  "uriparse",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "solana-sdk-macro"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b8bcac4394644f21dc013e932a7df9f536fcecef3e5df43fe362b4ec532ce30"
+version = "1.13.5"
 dependencies = [
- "bs58",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "bs58 0.4.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "solana-sdk-macro"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f9abf0bdba679d93c5624043ae3dcbf529ed2251c281cc615fa55c8dc2f0bd"
 dependencies = [
- "bs58",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "bs58 0.4.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6202,18 +6566,20 @@ dependencies = [
  "crossbeam-channel",
  "log",
  "solana-client",
+ "solana-gossip",
  "solana-logger 1.13.5",
  "solana-measure",
  "solana-metrics",
  "solana-runtime",
  "solana-sdk 1.13.5",
+ "solana-streamer",
 ]
 
 [[package]]
 name = "solana-stake-accounts"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "solana-clap-utils",
  "solana-cli-config",
  "solana-client",
@@ -6256,15 +6622,15 @@ dependencies = [
  "bzip2",
  "enum-iterator",
  "flate2",
- "futures 0.3.21",
+ "futures 0.3.25",
  "goauth",
  "http",
  "hyper",
  "hyper-proxy",
  "log",
  "openssl",
- "prost 0.10.0",
- "prost-types 0.10.0",
+ "prost 0.10.4",
+ "prost-types 0.10.1",
  "serde",
  "serde_derive",
  "smpl_jwt",
@@ -6274,7 +6640,7 @@ dependencies = [
  "solana-transaction-status",
  "thiserror",
  "tokio",
- "tonic 0.7.1",
+ "tonic 0.7.2",
  "zstd",
 ]
 
@@ -6283,21 +6649,21 @@ name = "solana-storage-proto"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "enum-iterator",
- "prost 0.10.0",
+ "prost 0.10.4",
  "serde",
  "solana-account-decoder",
  "solana-sdk 1.13.5",
  "solana-transaction-status",
- "tonic-build 0.7.0",
+ "tonic-build 0.7.2",
 ]
 
 [[package]]
 name = "solana-store-tool"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "log",
  "solana-logger 1.13.5",
  "solana-runtime",
@@ -6315,14 +6681,14 @@ dependencies = [
  "itertools",
  "libc",
  "log",
- "nix",
+ "nix 0.23.1",
  "pem",
  "percentage",
  "pkcs8",
  "quinn",
  "rand 0.7.3",
  "rcgen",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "solana-logger 1.13.5",
  "solana-metrics",
  "solana-perf",
@@ -6336,10 +6702,10 @@ dependencies = [
 name = "solana-sys-tuner"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "libc",
  "log",
- "nix",
+ "nix 0.23.1",
  "solana-logger 1.13.5",
  "solana-version",
  "sysctl",
@@ -6351,7 +6717,7 @@ dependencies = [
 name = "solana-test-validator"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "log",
  "serde_derive",
  "serde_json",
@@ -6370,13 +6736,43 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "solana-tip-distributor"
+version = "1.13.5"
+dependencies = [
+ "anchor-lang",
+ "bigdecimal",
+ "clap 3.2.23",
+ "env_logger",
+ "futures 0.3.25",
+ "im",
+ "itertools",
+ "log",
+ "num-traits",
+ "serde",
+ "serde_json",
+ "solana-client",
+ "solana-genesis-utils",
+ "solana-ledger",
+ "solana-merkle-tree",
+ "solana-program 1.13.5",
+ "solana-rpc",
+ "solana-runtime",
+ "solana-sdk 1.13.5",
+ "solana-stake-program",
+ "thiserror",
+ "tip-distribution",
+ "tip-payment",
+ "tokio",
+]
+
 [[package]]
 name = "solana-tokens"
 version = "1.13.5"
 dependencies = [
  "bincode",
  "chrono",
- "clap 2.33.3",
+ "clap 2.34.0",
  "console",
  "csv",
  "ctrlc",
@@ -6406,7 +6802,7 @@ name = "solana-transaction-dos"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "clap 2.33.3",
+ "clap 2.34.0",
  "log",
  "rand 0.7.3",
  "rayon",
@@ -6432,10 +6828,10 @@ name = "solana-transaction-status"
 version = "1.13.5"
 dependencies = [
  "Inflector",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "borsh",
- "bs58",
+ "bs58 0.4.0",
  "lazy_static",
  "log",
  "serde",
@@ -6466,7 +6862,7 @@ name = "solana-validator"
 version = "1.13.5"
 dependencies = [
  "chrono",
- "clap 2.33.3",
+ "clap 2.34.0",
  "console",
  "core_affinity",
  "crossbeam-channel",
@@ -6511,6 +6907,7 @@ dependencies = [
  "solana-vote-program",
  "symlink",
  "tikv-jemallocator",
+ "tonic 0.5.2",
 ]
 
 [[package]]
@@ -6519,7 +6916,7 @@ version = "1.13.5"
 dependencies = [
  "log",
  "rustc_version 0.4.0",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_derive",
  "solana-frozen-abi 1.13.5",
@@ -6551,7 +6948,7 @@ dependencies = [
 name = "solana-watchtower"
 version = "1.13.5"
 dependencies = [
- "clap 2.33.3",
+ "clap 2.34.0",
  "humantime",
  "log",
  "solana-clap-utils",
@@ -6580,13 +6977,11 @@ dependencies = [
 
 [[package]]
 name = "solana-zk-token-sdk"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "410ee53a26ac91098c289c983863535d4fbb6604b229ae1159503f48fa4fc90f"
+version = "1.13.5"
 dependencies = [
  "aes-gcm-siv",
  "arrayref",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bytemuck",
  "byteorder",
@@ -6601,8 +6996,8 @@ dependencies = [
  "serde",
  "serde_json",
  "sha3 0.9.1",
- "solana-program 1.10.33",
- "solana-sdk 1.10.33",
+ "solana-program 1.13.5",
+ "solana-sdk 1.13.5",
  "subtle",
  "thiserror",
  "zeroize",
@@ -6610,17 +7005,20 @@ dependencies = [
 
 [[package]]
 name = "solana-zk-token-sdk"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5b9d83227b8bdfe5c7b73693aab59942fb3a18a47633284affadd28d65f454"
 dependencies = [
  "aes-gcm-siv",
  "arrayref",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bytemuck",
  "byteorder",
  "cipher 0.4.3",
  "curve25519-dalek",
  "getrandom 0.1.16",
+ "itertools",
  "lazy_static",
  "merlin",
  "num-derive",
@@ -6629,8 +7027,8 @@ dependencies = [
  "serde",
  "serde_json",
  "sha3 0.9.1",
- "solana-program 1.13.5",
- "solana-sdk 1.13.5",
+ "solana-program 1.14.6",
+ "solana-sdk 1.14.6",
  "subtle",
  "thiserror",
  "zeroize",
@@ -6652,7 +7050,7 @@ dependencies = [
  "rustc-demangle",
  "scroll",
  "thiserror",
- "time 0.1.43",
+ "time 0.1.44",
 ]
 
 [[package]]
@@ -6663,9 +7061,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"
@@ -6687,7 +7085,7 @@ dependencies = [
  "borsh",
  "num-derive",
  "num-traits",
- "solana-program 1.10.33",
+ "solana-program 1.14.6",
  "spl-token",
  "spl-token-2022",
  "thiserror",
@@ -6699,7 +7097,7 @@ version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325"
 dependencies = [
- "solana-program 1.10.33",
+ "solana-program 1.14.6",
 ]
 
 [[package]]
@@ -6713,7 +7111,7 @@ dependencies = [
  "num-derive",
  "num-traits",
  "num_enum",
- "solana-program 1.10.33",
+ "solana-program 1.14.6",
  "thiserror",
 ]
 
@@ -6728,8 +7126,8 @@ dependencies = [
  "num-derive",
  "num-traits",
  "num_enum",
- "solana-program 1.10.33",
- "solana-zk-token-sdk 1.10.33",
+ "solana-program 1.14.6",
+ "solana-zk-token-sdk 1.14.6",
  "spl-memo",
  "spl-token",
  "thiserror",
@@ -6776,11 +7174,11 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "serde",
  "serde_derive",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6790,13 +7188,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
 dependencies = [
  "base-x",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "serde",
  "serde_derive",
  "serde_json",
- "sha1",
- "syn 1.0.91",
+ "sha1 0.6.1",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6830,24 +7228,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.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6875,13 +7273,13 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.91"
+version = "1.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "unicode-xid 0.2.2",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -6896,10 +7294,10 @@ version = "0.12.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
- "unicode-xid 0.2.2",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+ "unicode-xid 0.2.4",
 ]
 
 [[package]]
@@ -6914,9 +7312,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",
@@ -6927,24 +7325,18 @@ dependencies = [
 
 [[package]]
 name = "systemstat"
-version = "0.1.10"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8862adb0fd5f4c5707b0eeb6c2ec7610bd7a8bf5e069150bd6dde04a7f40ebf7"
+checksum = "91a3cae256f8af5246c2daad51ff29c32de4b4b0b0222063920af445fa3e12ab"
 dependencies = [
  "bytesize",
  "chrono",
  "lazy_static",
  "libc",
- "nom 7.0.0",
+ "nom",
  "winapi 0.3.9",
 ]
 
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
 [[package]]
 name = "tar"
 version = "0.4.38"
@@ -6964,18 +7356,18 @@ checksum = "b85d0a9369a919ba0db919b142a2b704cd207dfc676f7a43c2d105d0bc225487"
 dependencies = [
  "anyhow",
  "fnv",
- "futures 0.3.21",
+ "futures 0.3.25",
  "humantime",
  "opentelemetry",
  "pin-project",
- "rand 0.8.4",
+ "rand 0.8.5",
  "serde",
  "static_assertions",
  "tarpc-plugins",
  "thiserror",
  "tokio",
  "tokio-serde",
- "tokio-util 0.6.9",
+ "tokio-util 0.6.10",
  "tracing",
  "tracing-opentelemetry",
 ]
@@ -6986,9 +7378,9 @@ version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -7007,9 +7399,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",
 ]
@@ -7026,9 +7418,9 @@ 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 = "textwrap"
@@ -7041,28 +7433,28 @@ dependencies = [
 
 [[package]]
 name = "textwrap"
-version = "0.15.0"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
 
 [[package]]
 name = "thiserror"
-version = "1.0.30"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -7082,9 +7474,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",
@@ -7093,9 +7485,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",
@@ -7103,11 +7495,12 @@ 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",
 ]
 
@@ -7128,16 +7521,24 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.7"
+version = "0.3.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
+checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca"
 dependencies = [
- "itoa 1.0.1",
+ "itoa 1.0.4",
  "libc",
  "num_threads",
- "time-macros 0.2.3",
+ "serde",
+ "time-core",
+ "time-macros 0.2.5",
 ]
 
+[[package]]
+name = "time-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
 [[package]]
 name = "time-macros"
 version = "0.1.1"
@@ -7150,9 +7551,12 @@ dependencies = [
 
 [[package]]
 name = "time-macros"
-version = "0.2.3"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
+checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b"
+dependencies = [
+ "time-core",
+]
 
 [[package]]
 name = "time-macros-impl"
@@ -7161,10 +7565,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
 dependencies = [
  "proc-macro-hack",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "standback",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -7188,9 +7592,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",
 ]
@@ -7201,6 +7605,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.13.5",
+]
+
+[[package]]
+name = "tip-payment"
+version = "0.1.0"
+dependencies = [
+ "anchor-lang",
+]
+
 [[package]]
 name = "tokio"
 version = "1.14.1"
@@ -7223,9 +7642,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",
@@ -7233,13 +7652,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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -7265,11 +7684,11 @@ 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.4",
+ "rustls 0.20.7",
  "tokio",
  "webpki 0.22.0",
 ]
@@ -7292,9 +7711,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.8"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
+checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -7303,25 +7722,25 @@ dependencies = [
 
 [[package]]
 name = "tokio-tungstenite"
-version = "0.17.1"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
+checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
 dependencies = [
  "futures-util",
  "log",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "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",
@@ -7335,9 +7754,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",
@@ -7349,13 +7768,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.1",
+ "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"
@@ -7364,7 +7817,7 @@ checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a"
 dependencies = [
  "async-stream",
  "async-trait",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
  "futures-core",
  "futures-util",
@@ -7373,14 +7826,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",
@@ -7390,14 +7843,14 @@ dependencies = [
 
 [[package]]
 name = "tonic"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30fb54bf1e446f44d870d260d99957e7d11fb9d0a0f5bd1a662ad1411cc103f9"
+checksum = "5be9d60db39854b30b835107500cf0aca0b0d14d6e1c3de124217c23a29c2ddb"
 dependencies = [
  "async-stream",
  "async-trait",
  "axum",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
  "futures-core",
  "futures-util",
@@ -7406,15 +7859,15 @@ dependencies = [
  "http-body",
  "hyper",
  "hyper-timeout",
- "percent-encoding 2.1.0",
+ "percent-encoding 2.2.0",
  "pin-project",
- "prost 0.10.0",
- "prost-derive 0.10.0",
- "rustls-pemfile 0.3.0",
+ "prost 0.10.4",
+ "prost-derive 0.10.1",
+ "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",
@@ -7422,46 +7875,58 @@ 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.47",
+ "prost-build 0.8.0",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
 [[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.32",
+ "proc-macro2 1.0.47",
  "prost-build 0.9.0",
- "quote 1.0.10",
- "syn 1.0.91",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "tonic-build"
-version = "0.7.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d17087af5c80e5d5fc8ba9878e60258065a0a757e35efe7a05b7904bece1943"
+checksum = "d9263bf4c9bfaae7317c1c2faf7f18491d2fe476f70c414b73bf5d445b00ffa1"
 dependencies = [
  "prettyplease",
- "proc-macro2 1.0.32",
- "prost-build 0.10.0",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "prost-build 0.10.4",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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",
  "indexmap",
  "pin-project",
  "pin-project-lite",
- "rand 0.8.4",
+ "rand 0.8.5",
  "slab",
  "tokio",
- "tokio-util 0.7.1",
+ "tokio-util 0.7.2",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -7469,9 +7934,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",
@@ -7488,21 +7953,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",
@@ -7513,22 +7978,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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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]]
@@ -7578,37 +8044,37 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
 [[package]]
 name = "tungstenite"
-version = "0.17.2"
+version = "0.17.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
+checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "byteorder",
  "bytes",
  "http",
  "httparse",
  "log",
- "rand 0.8.4",
- "rustls 0.20.4",
+ "rand 0.8.5",
+ "rustls 0.20.7",
  "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]]
 name = "typenum"
-version = "1.14.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
+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"
@@ -7621,30 +8087,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 = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+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"
@@ -7654,9 +8126,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"
@@ -7664,7 +8136,7 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
 dependencies = [
- "generic-array 0.14.5",
+ "generic-array",
  "subtle",
 ]
 
@@ -7694,9 +8166,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
 [[package]]
 name = "uriparse"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e515b1ada404168e145ac55afba3c42f04cf972201a8552d42e2abb17c1b7221"
+checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff"
 dependencies = [
  "fnv",
  "lazy_static",
@@ -7715,14 +8187,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]]
@@ -7743,9 +8214,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 = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
+checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
+dependencies = [
+ "getrandom 0.2.8",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
 name = "vcpkg"
@@ -7761,9 +8248,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"
@@ -7809,15 +8296,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.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
 dependencies = [
  "cfg-if 1.0.0",
  "wasm-bindgen-macro",
@@ -7825,24 +8318,24 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
 dependencies = [
  "bumpalo",
- "lazy_static",
  "log",
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "once_cell",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "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",
@@ -7852,38 +8345,38 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
 dependencies = [
- "quote 1.0.10",
+ "quote 1.0.21",
  "wasm-bindgen-macro-support",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
 dependencies = [
- "proc-macro2 1.0.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
+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",
@@ -7911,22 +8404,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]]
@@ -7974,89 +8476,103 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows-sys"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 dependencies = [
- "windows_aarch64_msvc 0.30.0",
- "windows_i686_gnu 0.30.0",
- "windows_i686_msvc 0.30.0",
- "windows_x86_64_gnu 0.30.0",
- "windows_x86_64_msvc 0.30.0",
+ "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",
 ]
 
 [[package]]
 name = "windows-sys"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
 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",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
 ]
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.32.0"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
 
 [[package]]
 name = "winreg"
@@ -8067,12 +8583,6 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
-[[package]]
-name = "wyz"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
-
 [[package]]
 name = "x509-parser"
 version = "0.14.0"
@@ -8080,22 +8590,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8"
 dependencies = [
  "asn1-rs",
- "base64 0.13.0",
+ "base64 0.13.1",
  "data-encoding",
  "der-parser",
  "lazy_static",
- "nom 7.0.0",
+ "nom",
  "oid-registry",
  "rusticata-macros",
  "thiserror",
- "time 0.3.7",
+ "time 0.3.16",
 ]
 
 [[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",
 ]
@@ -8109,13 +8619,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.7",
+ "time 0.3.16",
 ]
 
 [[package]]
@@ -8129,30 +8645,30 @@ 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.32",
- "quote 1.0.10",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "synstructure",
 ]
 
 [[package]]
 name = "zstd"
-version = "0.11.1+zstd.1.5.2"
+version = "0.11.2+zstd.1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a16b8414fde0414e90c612eba70985577451c4c504b99885ebed24762cb81a"
+checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
 dependencies = [
  "zstd-safe",
 ]
 
 [[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 2f6e9f66f5..c4c0b96976 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",
@@ -29,6 +31,7 @@ members = [
     "geyser-plugin-manager",
     "gossip",
     "install",
+    "jito-protos",
     "keygen",
     "ledger",
     "ledger-tool",
@@ -75,6 +78,7 @@ members = [
     "streamer",
     "sys-tuner",
     "test-validator",
+    "tip-distributor",
     "tokens",
     "transaction-dos",
     "transaction-status",
@@ -86,7 +90,10 @@ members = [
 ]
 
 exclude = [
+    "anchor",
+    "jito-programs",
     "programs/bpf",
+    "programs/sbf",
 ]
 
 # This prevents a Travis CI error when building for Windows.
diff --git a/README.md b/README.md
index 496f7dedb2..a01c4c8c88 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,14 @@
   </a>
 </p>
 
-[![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]&#40;https://img.shields.io/crates/v/solana-core.svg&#41;]&#40;https://crates.io/crates/solana-core&#41;)
+[//]: # ([![Solana documentation]&#40;https://docs.rs/solana-core/badge.svg&#41;]&#40;https://docs.rs/solana-core&#41;)
+[//]: # ([![codecov]&#40;https://codecov.io/gh/solana-labs/solana/branch/master/graph/badge.svg&#41;]&#40;https://codecov.io/gh/solana-labs/solana&#41;)
+
+# About
+This repository contains Jito Foundations'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/Cargo.toml b/banking-bench/Cargo.toml
index 459678a8e7..8922913c7c 100644
--- a/banking-bench/Cargo.toml
+++ b/banking-bench/Cargo.toml
@@ -9,7 +9,7 @@ homepage = "https://solana.com/"
 publish = false
 
 [dependencies]
-clap = {version = "3.1.5", features = ["derive", "cargo"]}
+clap = { version = "3.1.5", features = ["derive", "cargo"] }
 crossbeam-channel = "0.5"
 log = "0.4.14"
 rand = "0.7.0"
diff --git a/banking-bench/src/main.rs b/banking-bench/src/main.rs
index e23e881827..20de455b02 100644
--- a/banking-bench/src/main.rs
+++ b/banking-bench/src/main.rs
@@ -6,7 +6,7 @@ use {
     rand::{thread_rng, Rng},
     rayon::prelude::*,
     solana_client::connection_cache::{ConnectionCache, DEFAULT_TPU_CONNECTION_POOL_SIZE},
-    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 +30,7 @@ use {
     },
     solana_streamer::socket::SocketAddrSpace,
     std::{
+        collections::HashSet,
         sync::{atomic::Ordering, Arc, Mutex, RwLock},
         thread::sleep,
         time::{Duration, Instant},
@@ -45,9 +46,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::<usize>();
         }
         if total >= ref_tx_count {
             break;
@@ -358,6 +365,8 @@ fn main() {
             Arc::new(RwLock::new(CostModel::default())),
             Arc::new(connection_cache),
             bank_forks.clone(),
+            HashSet::default(),
+            BundleAccountLocker::default(),
         );
         poh_recorder.lock().unwrap().set_bank(&bank);
 
diff --git a/banks-server/Cargo.toml b/banks-server/Cargo.toml
index b578bff883..388a02a38b 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.13.5" }
 solana-client = { path = "../client", version = "=1.13.5" }
+solana-gossip = { path = "../gossip", version = "=1.13.5" }
 solana-runtime = { path = "../runtime", version = "=1.13.5" }
 solana-sdk = { path = "../sdk", version = "=1.13.5" }
 solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.13.5" }
@@ -23,6 +24,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.13.5" }
+
 [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 606cb50352..124f6f38f3 100644
--- a/banks-server/src/banks_server.rs
+++ b/banks-server/src/banks_server.rs
@@ -7,6 +7,7 @@ use {
         TransactionConfirmationStatus, TransactionSimulationDetails, TransactionStatus,
     },
     solana_client::connection_cache::ConnectionCache,
+    solana_gossip::cluster_info::ClusterInfo,
     solana_runtime::{
         bank::{Bank, TransactionSimulationResult},
         bank_forks::BankForks,
@@ -373,7 +374,7 @@ pub async fn start_local_server(
 
 pub async fn start_tcp_server(
     listen_addr: SocketAddr,
-    tpu_addr: SocketAddr,
+    cluster_info: Arc<ClusterInfo>,
     bank_forks: Arc<RwLock<BankForks>>,
     block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
     connection_cache: Arc<ConnectionCache>,
@@ -397,7 +398,7 @@ pub async fn start_tcp_server(
             let (sender, receiver) = unbounded();
 
             SendTransactionService::new::<NullTpuInfo>(
-                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 822798dd1f..8e0bfbeaaf 100644
--- a/banks-server/src/rpc_banks_service.rs
+++ b/banks-server/src/rpc_banks_service.rs
@@ -4,6 +4,7 @@ use {
     crate::banks_server::start_tcp_server,
     futures::{future::FutureExt, pin_mut, prelude::stream::StreamExt, select},
     solana_client::connection_cache::ConnectionCache,
+    solana_gossip::cluster_info::ClusterInfo,
     solana_runtime::{bank_forks::BankForks, commitment::BlockCommitmentCache},
     std::{
         net::SocketAddr,
@@ -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<ClusterInfo>,
     bank_forks: Arc<RwLock<BankForks>>,
     block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
     connection_cache: Arc<ConnectionCache>,
@@ -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<ClusterInfo>,
         bank_forks: Arc<RwLock<BankForks>>,
         block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
         connection_cache: Arc<ConnectionCache>,
@@ -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<ClusterInfo>,
         bank_forks: &Arc<RwLock<BankForks>>,
         block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
         connection_cache: &Arc<ConnectionCache>,
@@ -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<ClusterInfo> = 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..d048651908
--- /dev/null
+++ b/bench-batch-simulate-bundle/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "solana-bench-batch-simulate-bundle"
+version = "1.13.5"
+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<VersionedBundle>,
+    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::<Vec<VersionedBundle>>();
+    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<Arc<Simulator>> = (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<RwLock<BundleBatch>>,
+    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::<Vec<VersionedBundle>>();
+            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<AtomicBool>,
+) -> 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<VersionedTransaction>, 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<Self> for TransactionCost {
+    fn eq(&self, other: &Self) -> bool {
+        self.cost == other.cost
+    }
+}
+
+impl PartialOrd<Self> for TransactionCost {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        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<VersionedTransaction> {
+    let txs: Vec<VersionedTransaction> = 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<Reverse<TransactionCost>> = 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::<Vec<VersionedTransaction>>()
+}
+
+fn fetch_and_assert_slot_diff(
+    rpc_client_0: &RpcClient,
+    rpc_client_1: &RpcClient,
+    tolerable_diff: Option<i64>,
+) -> (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<LoadedAddresses, AddressLoaderError> {
+        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<RpcClient>,
+    stats: Arc<Stats>,
+    exit: Arc<AtomicBool>,
+}
+
+pub struct Stats {
+    pub total_rpc_errs: Arc<AtomicU64>,
+    pub total_sim_errs: Arc<AtomicU64>,
+    pub total_sim_success: Arc<AtomicU64>,
+}
+
+impl Simulator {
+    pub fn new(
+        rpc_client: RpcClient,
+        stats: Arc<Stats>,
+        n_threads: usize,
+        exit: Arc<AtomicBool>,
+    ) -> 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<RwLock<BundleBatch>>) {
+        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<VersionedBundle>,
+        simulation_slot: Slot,
+        rpc_client: &Arc<RpcClient>,
+    ) -> 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::<Vec<RpcSimulateBundleConfig>>();
+
+        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..d78a36faba
--- /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.13.5"
+edition = "2021"
+
+[dependencies]
+env_logger = "0.9.0"
+log = "0.4.17"
+solana-sdk = { path = "../sdk", version = "=1.13.5" }
+solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.13.5" }
+solana-transaction-status = { path = "../transaction-status", version = "=1.13.5" }
+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<u64> = 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<JoinHandle<()>> = (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 4d0832ef24..a4a3d99dad 100644
--- a/ci/buildkite-pipeline-in-disk.sh
+++ b/ci/buildkite-pipeline-in-disk.sh
@@ -185,7 +185,7 @@ all_test_steps() {
       - "queue=default"
 EOF
   else
-    annotate --style info \
+    annotate --style info --context test-stable-bpf \
       "Stable-BPF skipped as no relevant files were modified"
   fi
 
@@ -203,16 +203,18 @@ 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
 
@@ -237,7 +239,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
 
@@ -247,9 +249,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
 
@@ -308,7 +312,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
 
@@ -336,5 +340,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 cfc24d2528..7f59a691a6 100755
--- a/ci/buildkite-pipeline.sh
+++ b/ci/buildkite-pipeline.sh
@@ -190,7 +190,7 @@ all_test_steps() {
       - "queue=solana"
 EOF
   else
-    annotate --style info \
+    annotate --style info --context test-stable-bpf  \
       "Stable-BPF skipped as no relevant files were modified"
   fi
 
@@ -208,17 +208,19 @@ 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 \
+          "Stable perf skipped because CI doesn't have 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 \
-      "Stable-perf skipped as no relevant files were modified"
+    annotate --style info --context test-stable-perf \
+      "Stable perf skipped as no relevant files were modified"
   fi
 
   # Wasm support
@@ -227,9 +229,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
 
@@ -292,7 +296,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
 
@@ -320,5 +324,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 8cf29dc89e..ed8e4dc4a8 100644
--- a/ci/buildkite-solana-private.sh
+++ b/ci/buildkite-solana-private.sh
@@ -185,8 +185,8 @@ all_test_steps() {
       - "queue=sol-private"
 EOF
   else
-    annotate --style info \
-      "Stable-BPF skipped as no relevant files were modified"
+    annotate --style info --context test-stable-bpf \
+      "Stable-SBF skipped as no relevant files were modified"
   fi
 
   # Perf test suite
@@ -203,16 +203,18 @@ 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
 
@@ -239,7 +241,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
 
@@ -249,9 +251,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 7238a1615a..6ab67b8d79 100644
--- a/ci/docker-rust/Dockerfile
+++ b/ci/docker-rust/Dockerfile
@@ -39,6 +39,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/cli-output/Cargo.toml b/cli-output/Cargo.toml
index 1506c76950..579a209f2b 100644
--- a/cli-output/Cargo.toml
+++ b/cli-output/Cargo.toml
@@ -22,8 +22,8 @@ serde = "1.0.136"
 serde_json = "1.0.79"
 solana-account-decoder = { path = "../account-decoder", version = "=1.13.5" }
 solana-clap-utils = { path = "../clap-utils", version = "=1.13.5" }
-solana-client = { path = "../client", version = "=1.13.5" }
 solana-cli-config = { path = "../cli-config", version = "=1.13.5" }
+solana-client = { path = "../client", version = "=1.13.5" }
 solana-sdk = { path = "../sdk", version = "=1.13.5" }
 solana-transaction-status = { path = "../transaction-status", version = "=1.13.5" }
 solana-vote-program = { path = "../programs/vote", version = "=1.13.5" }
diff --git a/client/src/http_sender.rs b/client/src/http_sender.rs
index 84e1418d7a..c05e2673e1 100644
--- a/client/src/http_sender.rs
+++ b/client/src/http_sender.rs
@@ -70,6 +70,102 @@ impl HttpSender {
             stats: RwLock::new(RpcTransportStats::default()),
         }
     }
+
+    fn check_response(response: &serde_json::Value) -> Result<()> {
+        if response["error"].is_object() {
+            return match serde_json::from_value::<RpcErrorObject>(response["error"].clone()) {
+                Ok(rpc_error_object) => {
+                    let data = match rpc_error_object.code {
+                        rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => {
+                            match serde_json::from_value::<RpcSimulateTransactionResult>(response["error"]["data"].clone()) {
+                                Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data),
+                                Err(err) => {
+                                    debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err);
+                                    RpcResponseErrorData::Empty
+                                }
+                            }
+                        },
+                        rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY => {
+                            match serde_json::from_value::<rpc_custom_error::NodeUnhealthyErrorData>(response["error"]["data"].clone()) {
+                                Ok(rpc_custom_error::NodeUnhealthyErrorData {num_slots_behind}) => RpcResponseErrorData::NodeUnhealthy {num_slots_behind},
+                                Err(_err) => {
+                                    RpcResponseErrorData::Empty
+                                }
+                            }
+                        },
+                        _ => RpcResponseErrorData::Empty
+                    };
+                    Err(RpcError::RpcResponseError {
+                        request_id: response["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(&response["error"]).unwrap(),
+                    err
+                ))
+                .into()),
+            };
+        }
+
+        Ok(())
+    }
+
+    async fn do_send_with_retry(
+        &self,
+        request: serde_json::Value,
+    ) -> reqwest::Result<serde_json::Value> {
+        let mut stats_updater = StatsUpdater::new(&self.stats);
+        let mut too_many_requests_retries = 5;
+        loop {
+            let response = {
+                let client = self.client.clone();
+                let request = request.to_string();
+                client
+                    .post(&self.url)
+                    .header(CONTENT_TYPE, "application/json")
+                    .body(request)
+                    .send()
+                    .await
+            }?;
+
+            if !response.status().is_success() {
+                if response.status() == StatusCode::TOO_MANY_REQUESTS
+                    && too_many_requests_retries > 0
+                {
+                    let mut duration = Duration::from_millis(500);
+                    if let Some(retry_after) = response.headers().get(RETRY_AFTER) {
+                        if let Ok(retry_after) = retry_after.to_str() {
+                            if let Ok(retry_after) = retry_after.parse::<u64>() {
+                                if retry_after < 120 {
+                                    duration = Duration::from_secs(retry_after);
+                                }
+                            }
+                        }
+                    }
+
+                    too_many_requests_retries -= 1;
+                    debug!(
+                        "Too many requests: server responded with {:?}, {} retries left, pausing for {:?}",
+                        response, too_many_requests_retries, duration
+                    );
+
+                    sleep(duration).await;
+                    stats_updater.add_rate_limited_time(duration);
+
+                    continue;
+                }
+
+                return Err(response.error_for_status().unwrap_err());
+            }
+
+            return response.json::<serde_json::Value>().await;
+        }
+    }
 }
 
 #[derive(Deserialize, Debug)]
@@ -109,103 +205,38 @@ impl<'a> Drop for StatsUpdater<'a> {
 
 #[async_trait]
 impl RpcSender for HttpSender {
-    fn get_transport_stats(&self) -> RpcTransportStats {
-        self.stats.read().unwrap().clone()
-    }
-
     async fn send(
         &self,
         request: RpcRequest,
         params: serde_json::Value,
     ) -> Result<serde_json::Value> {
-        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 request = request.build_request_json(request_id, params);
+        let mut resp = self.do_send_with_retry(request).await?;
+        Self::check_response(&resp)?;
 
-        let mut too_many_requests_retries = 5;
-        loop {
-            let response = {
-                let client = self.client.clone();
-                let request_json = request_json.clone();
-                client
-                    .post(&self.url)
-                    .header(CONTENT_TYPE, "application/json")
-                    .body(request_json)
-                    .send()
-                    .await
-            }?;
+        Ok(resp["result"].take())
+    }
 
-            if !response.status().is_success() {
-                if response.status() == StatusCode::TOO_MANY_REQUESTS
-                    && too_many_requests_retries > 0
-                {
-                    let mut duration = Duration::from_millis(500);
-                    if let Some(retry_after) = response.headers().get(RETRY_AFTER) {
-                        if let Ok(retry_after) = retry_after.to_str() {
-                            if let Ok(retry_after) = retry_after.parse::<u64>() {
-                                if retry_after < 120 {
-                                    duration = Duration::from_secs(retry_after);
-                                }
-                            }
-                        }
-                    }
+    async fn send_batch(
+        &self,
+        requests_and_params: Vec<(RpcRequest, serde_json::Value)>,
+    ) -> Result<serde_json::Value> {
+        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));
+        }
 
-                    too_many_requests_retries -= 1;
-                    debug!(
-                                "Too many requests: server responded with {:?}, {} retries left, pausing for {:?}",
-                                response, too_many_requests_retries, duration
-                            );
+        let resp = self
+            .do_send_with_retry(serde_json::Value::Array(batch_request))
+            .await?;
 
-                    sleep(duration).await;
-                    stats_updater.add_rate_limited_time(duration);
-                    continue;
-                }
-                return Err(response.error_for_status().unwrap_err().into());
-            }
+        Ok(resp)
+    }
 
-            let mut json = response.json::<serde_json::Value>().await?;
-            if json["error"].is_object() {
-                return match serde_json::from_value::<RpcErrorObject>(json["error"].clone()) {
-                    Ok(rpc_error_object) => {
-                        let data = match rpc_error_object.code {
-                                    rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => {
-                                        match serde_json::from_value::<RpcSimulateTransactionResult>(json["error"]["data"].clone()) {
-                                            Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data),
-                                            Err(err) => {
-                                                debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err);
-                                                RpcResponseErrorData::Empty
-                                            }
-                                        }
-                                    },
-                                    rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY => {
-                                        match serde_json::from_value::<rpc_custom_error::NodeUnhealthyErrorData>(json["error"]["data"].clone()) {
-                                            Ok(rpc_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 Ok(json["result"].take());
-        }
+    fn get_transport_stats(&self) -> RpcTransportStats {
+        self.stats.read().unwrap().clone()
     }
 
     fn url(&self) -> String {
diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs
index a43ea736cd..3485fb3738 100644
--- a/client/src/mock_sender.rs
+++ b/client/src/mock_sender.rs
@@ -473,4 +473,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<Value> {
+        todo!()
+    }
 }
diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs
index c03dfec1be..22489311fd 100644
--- a/client/src/nonblocking/rpc_client.rs
+++ b/client/src/nonblocking/rpc_client.rs
@@ -33,6 +33,7 @@ use {
     },
     solana_sdk::{
         account::Account,
+        bundle::VersionedBundle,
         clock::{Epoch, Slot, UnixTimestamp, DEFAULT_MS_PER_SLOT, MAX_HASH_AGE_IN_SECONDS},
         commitment_config::{CommitmentConfig, CommitmentLevel},
         epoch_info::EpochInfo,
@@ -42,7 +43,7 @@ use {
         message::Message,
         pubkey::Pubkey,
         signature::Signature,
-        transaction::{self, uses_durable_nonce, Transaction},
+        transaction::{self, uses_durable_nonce, Transaction, VersionedTransaction},
     },
     solana_transaction_status::{
         EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
@@ -943,6 +944,7 @@ impl RpcClient {
                     code,
                     message,
                     data,
+                    ..
                 }) = &err.kind
                 {
                     debug!("{} {}", code, message);
@@ -1388,6 +1390,111 @@ impl RpcClient {
         .await
     }
 
+    pub async fn batch_simulate_bundle(
+        &self,
+        bundles: Vec<VersionedBundle>,
+    ) -> BatchRpcResult<RpcSimulateBundleResult> {
+        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::<Vec<RpcSimulateBundleConfig>>();
+
+        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<RpcSimulateBundleResult> {
+        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::<VersionedTransaction>(tx, transaction_encoding))
+                .collect::<Result<Vec<String>, 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<RpcSimulateBundleResult> {
+        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<RpcSimulateBundleResult> {
+        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::<VersionedTransaction>(tx, transaction_encoding))
+            .collect::<ClientResult<Vec<String>>>()?;
+        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
@@ -5339,6 +5446,21 @@ impl RpcClient {
             .map_err(|err| ClientError::new_with_request(err.into(), request))
     }
 
+    pub async fn send_batch<T>(
+        &self,
+        requests_and_params: Vec<(RpcRequest, Value)>,
+    ) -> ClientResult<T>
+    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/client/src/rpc_client.rs b/client/src/rpc_client.rs
index 89959de20e..58e5b734a1 100644
--- a/client/src/rpc_client.rs
+++ b/client/src/rpc_client.rs
@@ -27,6 +27,7 @@ use {
     },
     solana_sdk::{
         account::Account,
+        bundle::VersionedBundle,
         clock::{Epoch, Slot, UnixTimestamp},
         commitment_config::CommitmentConfig,
         epoch_info::EpochInfo,
@@ -1111,6 +1112,35 @@ impl RpcClient {
         )
     }
 
+    pub fn batch_simulate_bundle(
+        &self,
+        bundles: Vec<VersionedBundle>,
+    ) -> BatchRpcResult<RpcSimulateBundleResult> {
+        self.invoke(self.rpc_client.batch_simulate_bundle(bundles))
+    }
+
+    pub fn batch_simulate_bundle_with_config(
+        &self,
+        bundles_and_configs: Vec<(VersionedBundle, RpcSimulateBundleConfig)>,
+    ) -> BatchRpcResult<RpcSimulateBundleResult> {
+        self.invoke(
+            self.rpc_client
+                .batch_simulate_bundle_with_config(bundles_and_configs),
+        )
+    }
+
+    pub fn simulate_bundle(&self, bundle: &VersionedBundle) -> RpcResult<RpcSimulateBundleResult> {
+        self.invoke(self.rpc_client.simulate_bundle(bundle))
+    }
+
+    pub fn simulate_bundle_with_config(
+        &self,
+        bundle: &VersionedBundle,
+        config: RpcSimulateBundleConfig,
+    ) -> RpcResult<RpcSimulateBundleResult> {
+        self.invoke(self.rpc_client.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/client/src/rpc_config.rs b/client/src/rpc_config.rs
index 4c21b8284e..5daf68fdb8 100644
--- a/client/src/rpc_config.rs
+++ b/client/src/rpc_config.rs
@@ -46,6 +46,51 @@ pub struct RpcSimulateTransactionConfig {
     pub min_context_slot: Option<Slot>,
 }
 
+#[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<String>,
+}
+
+#[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<Option<RpcSimulateTransactionAccountsConfig>>,
+    pub post_execution_accounts_configs: Vec<Option<RpcSimulateTransactionAccountsConfig>>,
+
+    /// Specifies the encoding scheme of the contained transactions.
+    pub transaction_encoding: Option<UiTransactionEncoding>,
+
+    /// Specifies the bank to run simulation against.
+    #[serde(flatten)]
+    pub simulation_bank: Option<SimulationSlotConfig>,
+
+    /// 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 {
diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs
index 03f4b39100..2009f409ee 100644
--- a/client/src/rpc_request.rs
+++ b/client/src/rpc_request.rs
@@ -110,6 +110,7 @@ pub enum RpcRequest {
     RequestAirdrop,
     SendTransaction,
     SimulateTransaction,
+    SimulateBundle,
     SignVote,
 }
 
@@ -183,6 +184,7 @@ impl fmt::Display for RpcRequest {
             RpcRequest::RequestAirdrop => "requestAirdrop",
             RpcRequest::SendTransaction => "sendTransaction",
             RpcRequest::SimulateTransaction => "simulateTransaction",
+            RpcRequest::SimulateBundle => "simulateBundle",
             RpcRequest::SignVote => "signVote",
         };
 
@@ -247,6 +249,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/client/src/rpc_response.rs b/client/src/rpc_response.rs
index 008ed518fb..65104ee3ba 100644
--- a/client/src/rpc_response.rs
+++ b/client/src/rpc_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,
@@ -16,6 +17,7 @@ use {
     thiserror::Error,
 };
 
+pub type BatchRpcResult<T> = client_error::Result<Vec<BatchResponse<T>>>;
 pub type RpcResult<T> = client_error::Result<Response<T>>;
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -26,7 +28,16 @@ pub struct RpcResponseContext {
     pub api_version: Option<RpcApiVersion>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BatchRpcResponseContext {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub slot: Option<Slot>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub api_version: Option<RpcApiVersion>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct RpcApiVersion(semver::Version);
 
 impl std::ops::Deref for RpcApiVersion {
@@ -72,6 +83,12 @@ impl RpcResponseContext {
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct BatchResponse<T> {
+    pub id: u64,
+    pub result: Response<T>,
+}
+
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Response<T> {
     pub context: RpcResponseContext,
@@ -339,6 +356,24 @@ pub struct RpcIdentity {
 
 #[derive(Serialize, Deserialize, Clone, Debug)]
 #[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<RpcSimulateBundleTransactionResult>,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
 pub struct RpcVote {
     /// Vote account address, as base-58 encoded string
     pub vote_pubkey: String,
@@ -391,7 +426,18 @@ pub struct RpcSignatureConfirmation {
     pub status: Result<()>,
 }
 
-#[derive(Serialize, Deserialize, Clone, Debug)]
+// TODO: consolidate with [RpcSimulateTransactionResult]
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct RpcSimulateBundleTransactionResult {
+    pub err: Option<TransactionError>,
+    pub logs: Option<Vec<String>>,
+    pub pre_execution_accounts: Option<Vec<UiAccount>>,
+    pub post_execution_accounts: Option<Vec<UiAccount>>,
+    pub units_consumed: Option<u64>,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
 #[serde(rename_all = "camelCase")]
 pub struct RpcSimulateTransactionResult {
     pub err: Option<TransactionError>,
diff --git a/client/src/rpc_sender.rs b/client/src/rpc_sender.rs
index dded04dfc6..d76923af52 100644
--- a/client/src/rpc_sender.rs
+++ b/client/src/rpc_sender.rs
@@ -31,6 +31,10 @@ pub trait RpcSender {
         request: RpcRequest,
         params: serde_json::Value,
     ) -> Result<serde_json::Value>;
+    async fn send_batch(
+        &self,
+        requests_and_params: Vec<(RpcRequest, serde_json::Value)>,
+    ) -> Result<serde_json::Value>;
     fn get_transport_stats(&self) -> RpcTransportStats;
     fn url(&self) -> String;
 }
diff --git a/core/Cargo.toml b/core/Cargo.toml
index fed7cece2d..63f83430f8 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -15,19 +15,30 @@ 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.11", features = ["serde"] }
+clap = { version = "3.1.15", features = ["derive"] }
 crossbeam-channel = "0.5"
 dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] }
 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.3"
+jito-protos = { path = "../jito-protos", version = "=1.13.5" }
+lazy_static = "1.4.0"
 log = "0.4.14"
 lru = "0.7.5"
 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.1"
@@ -62,12 +73,17 @@ solana-vote-program = { path = "../programs/vote", version = "=1.13.5" }
 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"
-raptorq = "1.6.5"
+raptorq = "=1.6.5"
 reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "brotli", "deflate", "gzip", "rustls-tls", "json"] }
 serde_json = "1.0.79"
 serial_test = "0.6.0"
@@ -82,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 f27fb3abd4..47d00914cb 100644
--- a/core/benches/banking_stage.rs
+++ b/core/benches/banking_stage.rs
@@ -11,6 +11,7 @@ use {
     solana_client::connection_cache::ConnectionCache,
     solana_core::{
         banking_stage::{BankingStage, BankingStageStats},
+        bundle_account_locker::BundleAccountLocker,
         leader_slot_banking_stage_metrics::LeaderSlotMetricsTracker,
         qos_service::QosService,
         unprocessed_packet_batches::*,
@@ -38,6 +39,7 @@ use {
     },
     solana_streamer::socket::SocketAddrSpace,
     std::{
+        collections::HashSet,
         sync::{atomic::Ordering, Arc, RwLock},
         time::{Duration, Instant},
     },
@@ -48,8 +50,15 @@ fn check_txs(receiver: &Arc<Receiver<WorkingBankEntry>>, 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::<usize>();
         }
         if total >= ref_tx_count {
             break;
@@ -99,6 +108,8 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
                 &mut LeaderSlotMetricsTracker::new(0),
                 10,
+                &HashSet::default(),
+                &BundleAccountLocker::default(),
             );
         });
 
@@ -234,6 +245,8 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
             Arc::new(RwLock::new(CostModel::default())),
             Arc::new(ConnectionCache::default()),
             bank_forks,
+            HashSet::new(),
+            BundleAccountLocker::default(),
         );
         poh_recorder.lock().unwrap().set_bank(&bank);
 
diff --git a/core/benches/cluster_info.rs b/core/benches/cluster_info.rs
index b1de0380f4..264fd20f72 100644
--- a/core/benches/cluster_info.rs
+++ b/core/benches/cluster_info.rs
@@ -78,6 +78,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::<Vec<Packet>>(),
+        );
+    });
+}
diff --git a/core/benches/retransmit_stage.rs b/core/benches/retransmit_stage.rs
index 5fbb66b225..5c24615df9 100644
--- a/core/benches/retransmit_stage.rs
+++ b/core/benches/retransmit_stage.rs
@@ -116,6 +116,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 02dea4cb47..14f28ac6a2 100644
--- a/core/src/banking_stage.rs
+++ b/core/src/banking_stage.rs
@@ -3,6 +3,7 @@
 //! can do its processing in parallel with signature verification on the GPU.
 use {
     crate::{
+        bundle_account_locker::BundleAccountLocker,
         forward_packet_batches_by_accounts::ForwardPacketBatchesByAccounts,
         leader_slot_banking_stage_metrics::{LeaderSlotMetricsTracker, ProcessTransactionsSummary},
         leader_slot_banking_stage_timing_metrics::{
@@ -62,7 +63,7 @@ use {
     solana_transaction_status::token_balances::TransactionTokenBalancesSet,
     std::{
         cmp,
-        collections::HashMap,
+        collections::{HashMap, HashSet},
         env,
         net::{SocketAddr, UdpSocket},
         rc::Rc,
@@ -398,6 +399,8 @@ impl BankingStage {
         cost_model: Arc<RwLock<CostModel>>,
         connection_cache: Arc<ConnectionCache>,
         bank_forks: Arc<RwLock<BankForks>>,
+        blacklisted_accounts: HashSet<Pubkey>,
+        bundle_account_locker: BundleAccountLocker,
     ) -> Self {
         Self::new_num_threads(
             cluster_info,
@@ -411,6 +414,8 @@ impl BankingStage {
             cost_model,
             connection_cache,
             bank_forks,
+            blacklisted_accounts,
+            bundle_account_locker,
         )
     }
 
@@ -427,6 +432,8 @@ impl BankingStage {
         cost_model: Arc<RwLock<CostModel>>,
         connection_cache: Arc<ConnectionCache>,
         bank_forks: Arc<RwLock<BankForks>>,
+        blacklisted_accounts: HashSet<Pubkey>,
+        bundle_account_locker: BundleAccountLocker,
     ) -> Self {
         assert!(num_threads >= MIN_TOTAL_THREADS);
         // Single thread to generate entries from many banks.
@@ -459,6 +466,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!("solana-banking-stage-tx-{}", i))
@@ -477,6 +487,8 @@ impl BankingStage {
                             cost_model,
                             connection_cache,
                             &bank_forks,
+                            blacklisted_accounts,
+                            bundle_account_locker,
                         );
                     })
                     .unwrap()
@@ -645,6 +657,8 @@ impl BankingStage {
         qos_service: &QosService,
         slot_metrics_tracker: &mut LeaderSlotMetricsTracker,
         num_packets_to_process_per_iteration: usize,
+        blacklisted_accounts: &HashSet<Pubkey>,
+        bundle_account_locker: &BundleAccountLocker,
     ) {
         let mut rebuffered_packet_count = 0;
         let mut consumed_buffered_packets_count = 0;
@@ -694,6 +708,8 @@ impl BankingStage {
                                     banking_stage_stats,
                                     qos_service,
                                     slot_metrics_tracker,
+                                    blacklisted_accounts,
+                                    bundle_account_locker,
                                 )
                             },
                             (),
@@ -889,6 +905,8 @@ impl BankingStage {
         connection_cache: &ConnectionCache,
         tracer_packet_stats: &mut TracerPacketStats,
         bank_forks: &Arc<RwLock<BankForks>>,
+        blacklisted_accounts: &HashSet<Pubkey>,
+        bundle_account_locker: &BundleAccountLocker,
     ) {
         let (decision, make_decision_time) = Measure::this(
             |_| {
@@ -942,6 +960,8 @@ impl BankingStage {
                             qos_service,
                             slot_metrics_tracker,
                             UNPROCESSED_BUFFER_STEP_SIZE,
+                            blacklisted_accounts,
+                            bundle_account_locker,
                         )
                     },
                     (),
@@ -1103,6 +1123,8 @@ impl BankingStage {
         cost_model: Arc<RwLock<CostModel>>,
         connection_cache: Arc<ConnectionCache>,
         bank_forks: &Arc<RwLock<BankForks>>,
+        blacklisted_accounts: HashSet<Pubkey>,
+        bundle_account_locker: BundleAccountLocker,
     ) {
         let recorder = poh_recorder.lock().unwrap().recorder();
         let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
@@ -1138,6 +1160,8 @@ impl BankingStage {
                             &connection_cache,
                             &mut tracer_packet_stats,
                             bank_forks,
+                            &blacklisted_accounts,
+                            &bundle_account_locker,
                         )
                     },
                     (),
@@ -1263,7 +1287,7 @@ impl BankingStage {
 
             // record and unlock will unlock all the successful transactions
             let (res, poh_record_time) = Measure::this(
-                |_| recorder.record(bank_slot, hash, processed_transactions),
+                |_| recorder.record(bank_slot, vec![(hash, processed_transactions)]),
                 (),
                 "hash",
             );
@@ -1319,7 +1343,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![]
                 };
@@ -1467,7 +1491,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);
                         transaction_status_sender.send_transaction_status_batch(
                             bank.clone(),
                             txs,
@@ -1525,6 +1549,7 @@ impl BankingStage {
         transaction_status_sender: Option<TransactionStatusSender>,
         gossip_vote_sender: &ReplayVoteSender,
         qos_service: &QosService,
+        bundle_account_locker: &BundleAccountLocker,
     ) -> ProcessTransactionBatchOutput {
         let mut cost_model_time = Measure::start("cost_model");
 
@@ -1547,7 +1572,18 @@ impl BankingStage {
         // Once accounts are locked, other threads cannot encode transactions that will modify the
         // 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
@@ -1716,6 +1752,7 @@ impl BankingStage {
         transaction_status_sender: Option<TransactionStatusSender>,
         gossip_vote_sender: &ReplayVoteSender,
         qos_service: &QosService,
+        bundle_account_locker: &BundleAccountLocker,
     ) -> ProcessTransactionsSummary {
         let mut chunk_start = 0;
         let mut all_retryable_tx_indexes = vec![];
@@ -1747,6 +1784,7 @@ impl BankingStage {
                 transaction_status_sender.clone(),
                 gossip_vote_sender,
                 qos_service,
+                bundle_account_locker,
             );
 
             let ProcessTransactionBatchOutput {
@@ -1932,6 +1970,8 @@ impl BankingStage {
         banking_stage_stats: &'a BankingStageStats,
         qos_service: &'a QosService,
         slot_metrics_tracker: &'a mut LeaderSlotMetricsTracker,
+        blacklisted_accounts: &HashSet<Pubkey>,
+        bundle_account_locker: &BundleAccountLocker,
     ) -> ProcessTransactionsSummary {
         // Convert packets to transactions
         let ((transactions, transaction_to_packet_indexes), packet_conversion_time): (
@@ -1948,6 +1988,13 @@ impl BankingStage {
                             bank.vote_only_bank(),
                             bank.as_ref(),
                         )
+                        .filter(|tx| {
+                            // prevent blacklisted transactions from being processed
+                            !tx.message()
+                                .account_keys()
+                                .iter()
+                                .any(|acc| blacklisted_accounts.contains(acc))
+                        })
                         .map(|transaction| (transaction, i))
                     })
                     .unzip()
@@ -1974,6 +2021,7 @@ impl BankingStage {
                     transaction_status_sender,
                     gossip_vote_sender,
                     qos_service,
+                    bundle_account_locker,
                 )
             },
             (),
@@ -2251,7 +2299,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},
@@ -2338,6 +2386,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,
@@ -2349,6 +2399,8 @@ mod tests {
                 Arc::new(RwLock::new(CostModel::default())),
                 Arc::new(ConnectionCache::default()),
                 bank_forks,
+                HashSet::default(),
+                bundle_locker,
             );
             drop(verified_sender);
             drop(gossip_verified_vote_sender);
@@ -2363,6 +2415,7 @@ mod tests {
     #[test]
     fn test_banking_stage_tick() {
         solana_logger::setup();
+
         let GenesisConfigInfo {
             mut genesis_config, ..
         } = create_genesis_config(2);
@@ -2391,6 +2444,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,
@@ -2402,6 +2457,8 @@ mod tests {
                 Arc::new(RwLock::new(CostModel::default())),
                 Arc::new(ConnectionCache::default()),
                 bank_forks,
+                HashSet::default(),
+                bundle_locker,
             );
             trace!("sending bank");
             drop(verified_sender);
@@ -2414,7 +2471,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);
@@ -2469,6 +2531,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,
@@ -2480,6 +2543,8 @@ mod tests {
                 Arc::new(RwLock::new(CostModel::default())),
                 Arc::new(ConnectionCache::default()),
                 bank_forks,
+                HashSet::default(),
+                bundle_locker,
             );
 
             // fund another account so we can send 2 good transactions in a single batch.
@@ -2531,9 +2596,14 @@ mod tests {
             bank.process_transaction(&fund_tx).unwrap();
             //receive entries + ticks
             loop {
-                let entries: Vec<Entry> = 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));
@@ -2623,6 +2693,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,
@@ -2635,6 +2708,8 @@ mod tests {
                     Arc::new(RwLock::new(CostModel::default())),
                     Arc::new(ConnectionCache::default()),
                     bank_forks,
+                    HashSet::default(),
+                    bundle_locker,
                 );
 
                 // wait for banking_stage to eat the packets
@@ -2653,7 +2728,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);
@@ -2721,8 +2801,11 @@ mod tests {
 
             let mut results = vec![new_execution_result(Ok(())); 2];
             let _ = BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
-            let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
-            assert_eq!(entry.transactions.len(), txs.len());
+            let WorkingBankEntry {
+                bank: _,
+                entries_ticks,
+            } = entry_receiver.recv().unwrap();
+            assert_eq!(entries_ticks[0].0.transactions.len(), txs.len());
 
             // InstructionErrors should still be recorded
             results[0] = new_execution_result(Err(TransactionError::InstructionError(
@@ -2736,8 +2819,11 @@ mod tests {
             } = BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
             result.unwrap();
             assert!(retryable_indexes.is_empty());
-            let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
-            assert_eq!(entry.transactions.len(), txs.len());
+            let WorkingBankEntry {
+                bank: _,
+                entries_ticks,
+            } = entry_receiver.recv().unwrap();
+            assert_eq!(entries_ticks[0].0.transactions.len(), txs.len());
 
             // Other TransactionErrors should not be recorded
             results[0] = TransactionExecutionResult::NotExecuted(TransactionError::AccountNotFound);
@@ -2748,7 +2834,11 @@ mod tests {
             } = BankingStage::record_transactions(bank.slot(), &txs, &results, &recorder);
             result.unwrap();
             assert!(retryable_indexes.is_empty());
-            let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
+            let WorkingBankEntry {
+                bank: _,
+                entries_ticks,
+            } = entry_receiver.recv().unwrap();
+            let entry = entries_ticks.get(0).unwrap().0.clone();
             assert_eq!(entry.transactions.len(), txs.len() - 1);
 
             // Once bank is set to a new bank (setting bank.slot() + 1 in record_transactions),
@@ -2816,7 +2906,7 @@ mod tests {
                     (Ok(()), None),
                     (Ok(()), None),
                 ],
-                &[2, 4, 5, 9, 11, 13]
+                &[2, 4, 5, 9, 11, 13],
             ),
             [5, 11, 13]
         );
@@ -2831,7 +2921,7 @@ mod tests {
                     (Ok(()), None),
                     (Ok(()), None),
                 ],
-                &[1, 6, 7, 9, 31, 43]
+                &[1, 6, 7, 9, 31, 43],
             ),
             [1, 9, 31, 43]
         );
@@ -2968,6 +3058,8 @@ mod tests {
             poh_recorder.lock().unwrap().set_bank(&bank);
             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,
@@ -2976,6 +3068,7 @@ mod tests {
                 None,
                 &gossip_vote_sender,
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+                &bundle_locker,
             );
 
             let ExecuteAndCommitTransactionsOutput {
@@ -2998,7 +3091,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());
@@ -3020,6 +3119,8 @@ mod tests {
                 genesis_config.hash(),
             )]);
 
+            let bundle_locker = BundleAccountLocker::default();
+
             let process_transactions_batch_output = BankingStage::process_and_record_transactions(
                 &bank,
                 &transactions,
@@ -3028,6 +3129,7 @@ mod tests {
                 None,
                 &gossip_vote_sender,
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+                &bundle_locker,
             );
 
             let ExecuteAndCommitTransactionsOutput {
@@ -3113,6 +3215,8 @@ mod tests {
                 genesis_config.hash(),
             )]);
 
+            let bundle_locker = BundleAccountLocker::default();
+
             let process_transactions_batch_output = BankingStage::process_and_record_transactions(
                 &bank,
                 &transactions,
@@ -3121,6 +3225,7 @@ mod tests {
                 None,
                 &gossip_vote_sender,
                 &qos_service,
+                &bundle_locker,
             );
 
             let ExecuteAndCommitTransactionsOutput {
@@ -3160,6 +3265,7 @@ mod tests {
                 None,
                 &gossip_vote_sender,
                 &qos_service,
+                &bundle_locker,
             );
 
             let ExecuteAndCommitTransactionsOutput {
@@ -3247,6 +3353,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,
@@ -3256,6 +3363,7 @@ mod tests {
                 None,
                 &gossip_vote_sender,
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+                &bundle_locker,
             );
 
             poh_recorder
@@ -3372,6 +3480,7 @@ mod tests {
     #[test]
     fn test_process_transactions_returns_unprocessed_txs() {
         solana_logger::setup();
+
         let GenesisConfigInfo {
             genesis_config,
             mint_keypair,
@@ -3413,6 +3522,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(),
@@ -3421,6 +3532,7 @@ mod tests {
                 None,
                 &gossip_vote_sender,
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+                &bundle_locker,
             );
 
             let ProcessTransactionsSummary {
@@ -3479,6 +3591,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(),
@@ -3487,6 +3601,7 @@ mod tests {
             None,
             &gossip_vote_sender,
             &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+            &bundle_locker,
         );
 
         poh_recorder
@@ -3580,7 +3695,7 @@ mod tests {
                 &mint_keypair,
                 &Pubkey::new_unique(),
                 1,
-                genesis_config.hash()
+                genesis_config.hash(),
             );
             MAX_NUM_TRANSACTIONS_PER_BATCH
         ];
@@ -3699,6 +3814,8 @@ mod tests {
 
             let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
 
+            let bundle_locker = BundleAccountLocker::default();
+
             let _ = BankingStage::process_and_record_transactions(
                 &bank,
                 &transactions,
@@ -3709,6 +3826,7 @@ mod tests {
                 }),
                 &gossip_vote_sender,
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+                &bundle_locker,
             );
 
             transaction_status_service.join().unwrap();
@@ -3860,6 +3978,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()],
@@ -3870,6 +3990,7 @@ mod tests {
                 }),
                 &gossip_vote_sender,
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
+                &bundle_locker,
             );
 
             transaction_status_service.join().unwrap();
@@ -3976,6 +4097,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.lock().unwrap().has_bank());
             let max_tx_processing_ns = std::u128::MAX;
@@ -3992,6 +4115,8 @@ mod tests {
                 &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
                 &mut LeaderSlotMetricsTracker::new(0),
                 num_conflicting_transactions,
+                &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.
@@ -4012,6 +4137,8 @@ mod tests {
                     &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
                     &mut LeaderSlotMetricsTracker::new(0),
                     num_packets_to_process_per_iteration,
+                    &HashSet::default(),
+                    &bundle_locker,
                 );
                 if num_expected_unprocessed == 0 {
                     assert!(buffered_packet_batches.is_empty())
@@ -4072,6 +4199,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,
@@ -4085,6 +4215,8 @@ mod tests {
                         &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
                         &mut LeaderSlotMetricsTracker::new(0),
                         num_packets_to_process_per_iteration,
+                        &HashSet::default(),
+                        &bundle_locker,
                     );
 
                     // Check everything is correct. All indexes after `interrupted_iteration`
diff --git a/core/src/broadcast_stage.rs b/core/src/broadcast_stage.rs
index 309466c4c1..ae3fcc82ef 100644
--- a/core/src/broadcast_stage.rs
+++ b/core/src/broadcast_stage.rs
@@ -33,7 +33,7 @@ use {
     std::{
         collections::{HashMap, HashSet},
         iter::repeat,
-        net::UdpSocket,
+        net::{SocketAddr, UdpSocket},
         sync::{
             atomic::{AtomicBool, Ordering},
             Arc, Mutex, RwLock,
@@ -84,6 +84,7 @@ impl BroadcastStageType {
         blockstore: Arc<Blockstore>,
         bank_forks: Arc<RwLock<BankForks>>,
         shred_version: u16,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> BroadcastStage {
         match self {
             BroadcastStageType::Standard => BroadcastStage::new(
@@ -95,6 +96,7 @@ impl BroadcastStageType {
                 blockstore,
                 bank_forks,
                 StandardBroadcastRun::new(shred_version),
+                shred_receiver_addr,
             ),
 
             BroadcastStageType::FailEntryVerification => BroadcastStage::new(
@@ -106,6 +108,7 @@ impl BroadcastStageType {
                 blockstore,
                 bank_forks,
                 FailEntryVerificationBroadcastRun::new(shred_version),
+                None,
             ),
 
             BroadcastStageType::BroadcastFakeShreds => BroadcastStage::new(
@@ -117,6 +120,7 @@ impl BroadcastStageType {
                 blockstore,
                 bank_forks,
                 BroadcastFakeShredsRun::new(0, shred_version),
+                None,
             ),
 
             BroadcastStageType::BroadcastDuplicates(config) => BroadcastStage::new(
@@ -128,6 +132,7 @@ impl BroadcastStageType {
                 blockstore,
                 bank_forks,
                 BroadcastDuplicatesRun::new(shred_version, config.clone()),
+                None,
             ),
         }
     }
@@ -148,6 +153,7 @@ trait BroadcastRun {
         cluster_info: &ClusterInfo,
         sock: &UdpSocket,
         bank_forks: &RwLock<BankForks>,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> Result<()>;
     fn record(&mut self, receiver: &Mutex<RecordReceiver>, blockstore: &Blockstore) -> Result<()>;
 }
@@ -243,6 +249,7 @@ impl BroadcastStage {
         blockstore: Arc<Blockstore>,
         bank_forks: Arc<RwLock<BankForks>>,
         broadcast_stage_run: impl BroadcastRun + Send + 'static + Clone,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> Self {
         let (socket_sender, socket_receiver) = unbounded();
         let (blockstore_sender, blockstore_receiver) = unbounded();
@@ -277,8 +284,13 @@ impl BroadcastStage {
             let t = Builder::new()
                 .name("solana-broadcaster-transmit".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;
@@ -394,6 +406,7 @@ pub fn broadcast_shreds(
     cluster_info: &ClusterInfo,
     bank_forks: &RwLock<BankForks>,
     socket_addr_space: &SocketAddrSpace,
+    shred_receiver_addr: Option<SocketAddr>,
 ) -> Result<()> {
     let mut result = Ok(());
     let mut shred_select = Measure::start("shred_select");
@@ -403,22 +416,27 @@ 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);
-            let root_bank = root_bank.clone();
-            shreds.flat_map(move |shred| {
-                repeat(&shred.payload).zip(cluster_nodes.get_broadcast_addrs(
-                    shred,
-                    &root_bank,
-                    DATA_PLANE_FANOUT,
-                    socket_addr_space,
-                ))
-            })
-        })
+        .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);
+                    let root_bank = root_bank.clone();
+                    shreds.flat_map(move |shred| {
+                        repeat(&shred.payload).zip(cluster_nodes.get_broadcast_addrs(
+                            shred,
+                            &root_bank,
+                            DATA_PLANE_FANOUT,
+                            socket_addr_space,
+                        ))
+                    })
+                }),
+        )
         .collect();
     shred_select.stop();
     transmit_stats.shred_select += shred_select.as_us();
@@ -614,6 +632,7 @@ pub mod test {
             blockstore.clone(),
             bank_forks,
             StandardBroadcastRun::new(0),
+            None,
         );
 
         MockBroadcastStage {
@@ -653,7 +672,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 a6e96409a0..d370f994d8 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;
@@ -242,6 +242,7 @@ impl BroadcastRun for BroadcastDuplicatesRun {
         cluster_info: &ClusterInfo,
         sock: &UdpSocket,
         bank_forks: &RwLock<BankForks>,
+        _shred_receiver_addr: Option<SocketAddr>,
     ) -> 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 d1ff80bee5..9d7d599fda 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::Shredder,
     solana_sdk::{hash::Hash, signature::Keypair},
+    std::net::SocketAddr,
 };
 
 #[derive(Clone)]
@@ -124,6 +125,7 @@ impl BroadcastRun for BroadcastFakeShredsRun {
         cluster_info: &ClusterInfo,
         sock: &UdpSocket,
         _bank_forks: &RwLock<BankForks>,
+        _shred_receiver_addr: Option<SocketAddr>,
     ) -> 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 c9135d0a60..4df4395b36 100644
--- a/core/src/broadcast_stage/broadcast_utils.rs
+++ b/core/src/broadcast_stage/broadcast_utils.rs
@@ -39,16 +39,24 @@ const RECEIVE_ENTRY_COUNT_THRESHOLD: usize = 8;
 pub(super) fn recv_slot_entries(receiver: &Receiver<WorkingBankEntry>) -> Result<ReceiveResults> {
     let timer = Duration::new(1, 0);
     let recv_start = Instant::now();
-    let (mut bank, (entry, mut last_tick_height)) = receiver.recv_timeout(timer)?;
+    let WorkingBankEntry {
+        mut bank,
+        entries_ticks,
+    } = receiver.recv_timeout(timer)?;
 
-    let mut entries = vec![entry];
+    let mut last_tick_height = entries_ticks.iter().last().unwrap().1;
+    let mut entries: Vec<Entry> = entries_ticks.into_iter().map(|(entry, _)| entry).collect();
     let mut slot = bank.slot();
     let mut max_tick_height = bank.max_tick_height();
 
     assert!(last_tick_height <= max_tick_height);
 
     if last_tick_height != max_tick_height {
-        while let Ok((try_bank, (entry, tick_height))) = receiver.try_recv() {
+        while let Ok(WorkingBankEntry {
+            bank: try_bank,
+            entries_ticks: new_entries_ticks,
+        }) = receiver.try_recv()
+        {
             // If the bank changed, that implies the previous slot was interrupted and we do not have to
             // broadcast its entries.
             if try_bank.slot() != slot {
@@ -58,8 +66,8 @@ pub(super) fn recv_slot_entries(receiver: &Receiver<WorkingBankEntry>) -> Result
                 slot = bank.slot();
                 max_tick_height = bank.max_tick_height();
             }
-            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));
 
             if entries.len() >= RECEIVE_ENTRY_COUNT_THRESHOLD {
                 break;
@@ -123,7 +131,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();
@@ -157,11 +169,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 a39fd90b23..3044972929 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::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;
@@ -143,6 +143,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
         cluster_info: &ClusterInfo,
         sock: &UdpSocket,
         bank_forks: &RwLock<BankForks>,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> Result<()> {
         let (shreds, _) = receiver.lock().unwrap().recv()?;
         broadcast_shreds(
@@ -154,6 +155,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
             cluster_info,
             bank_forks,
             cluster_info.socket_addr_space(),
+            shred_receiver_addr,
         )
     }
     fn record(&mut self, receiver: &Mutex<RecordReceiver>, blockstore: &Blockstore) -> Result<()> {
diff --git a/core/src/broadcast_stage/standard_broadcast_run.rs b/core/src/broadcast_stage/standard_broadcast_run.rs
index cfdc0af547..83e4dae827 100644
--- a/core/src/broadcast_stage/standard_broadcast_run.rs
+++ b/core/src/broadcast_stage/standard_broadcast_run.rs
@@ -17,7 +17,7 @@ use {
         signature::Keypair,
         timing::{duration_as_us, AtomicInterval},
     },
-    std::{sync::RwLock, time::Duration},
+    std::{net::SocketAddr, sync::RwLock, time::Duration},
 };
 
 #[derive(Clone)]
@@ -173,10 +173,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(())
     }
@@ -368,6 +368,7 @@ impl StandardBroadcastRun {
         shreds: Arc<Vec<Shred>>,
         broadcast_shred_batch_info: Option<BroadcastShredBatchInfo>,
         bank_forks: &RwLock<BankForks>,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> Result<()> {
         trace!("Broadcasting {:?} shreds", shreds.len());
         let mut transmit_stats = TransmitShredsStats::default();
@@ -383,6 +384,7 @@ impl StandardBroadcastRun {
             cluster_info,
             bank_forks,
             cluster_info.socket_addr_space(),
+            shred_receiver_addr,
         )?;
         transmit_time.stop();
 
@@ -495,9 +497,17 @@ impl BroadcastRun for StandardBroadcastRun {
         cluster_info: &ClusterInfo,
         sock: &UdpSocket,
         bank_forks: &RwLock<BankForks>,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> 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<RecordReceiver>, 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<T> = Result<T, BundleAccountLockerError>;
+
+pub struct LockedBundle<'a, 'b> {
+    bundle_account_locker: &'a BundleAccountLocker,
+    sanitized_bundle: &'b SanitizedBundle,
+    bank: Arc<Bank>,
+}
+
+impl<'a, 'b> LockedBundle<'a, 'b> {
+    pub fn new(
+        bundle_account_locker: &'a BundleAccountLocker,
+        sanitized_bundle: &'b SanitizedBundle,
+        bank: &Arc<Bank>,
+    ) -> 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<Pubkey, u64>,
+    write_locks: HashMap<Pubkey, u64>,
+}
+
+impl BundleAccountLocks {
+    pub fn read_locks(&self) -> HashSet<Pubkey> {
+        self.read_locks.keys().cloned().collect()
+    }
+
+    pub fn write_locks(&self) -> HashSet<Pubkey> {
+        self.write_locks.keys().cloned().collect()
+    }
+
+    pub fn lock_accounts(
+        &mut self,
+        read_locks: HashMap<Pubkey, u64>,
+        write_locks: HashMap<Pubkey, u64>,
+    ) {
+        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<Pubkey, u64>,
+        write_locks: HashMap<Pubkey, u64>,
+    ) {
+        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<Mutex<BundleAccountLocks>>,
+}
+
+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<Pubkey> {
+        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<Pubkey> {
+        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<BundleAccountLocks> {
+        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<Bank>,
+    ) -> BundleAccountLockerResult<LockedBundle<'a, 'b>> {
+        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<Pubkey, u64>, HashMap<Pubkey, u64>)> {
+        let transaction_locks: Vec<TransactionAccountLocks> = 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..f27fe9be2a
--- /dev/null
+++ b/core/src/bundle_sanitizer.rs
@@ -0,0 +1,612 @@
+use crate::unprocessed_packet_batches::ImmutableDeserializedPacket;
+///! Turns packets into SanitizedTransactions and ensure they pass sanity checks
+use {
+    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<T> = Result<T, BundleSanitizerError>;
+
+/// 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<Bank>,
+    consensus_accounts_cache: &HashSet<Pubkey>,
+    blacklisted_accounts: &HashSet<Pubkey>,
+    transaction_error_metrics: &mut TransactionErrorMetrics,
+) -> BundleSanitizationResult<SanitizedBundle> {
+    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::<Vec<usize>>();
+    let deserialized_packets = deserialize_packets(&packet_bundle.batch, &packet_indexes);
+    let transactions: Vec<SanitizedTransaction> = 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<FeatureSet>,
+    address_loader: impl AddressLoader,
+) -> Option<SanitizedTransaction> {
+    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..455f09a244
--- /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,
+        proxy::block_engine_stage::BlockBuilderFeeInfo,
+        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, Mutex, 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<T> = Result<T, BundleExecutionError>;
+
+// 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<SanitizedTransaction>,
+    pub pre_balances: (TransactionBalances, TransactionTokenBalances),
+    pub post_balances: (TransactionBalances, TransactionTokenBalances),
+}
+
+pub struct BundleStage {
+    bundle_thread: JoinHandle<()>,
+}
+
+impl BundleStage {
+    #[allow(clippy::new_ret_no_self)]
+    #[allow(clippy::too_many_arguments)]
+    pub fn new(
+        cluster_info: &Arc<ClusterInfo>,
+        poh_recorder: &Arc<Mutex<PohRecorder>>,
+        transaction_status_sender: Option<TransactionStatusSender>,
+        gossip_vote_sender: ReplayVoteSender,
+        cost_model: Arc<RwLock<CostModel>>,
+        bundle_receiver: Receiver<Vec<PacketBundle>>,
+        exit: Arc<AtomicBool>,
+        tip_manager: TipManager,
+        bundle_account_locker: BundleAccountLocker,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> 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,
+            block_builder_fee_info,
+        )
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn start_bundle_thread(
+        cluster_info: &Arc<ClusterInfo>,
+        poh_recorder: &Arc<Mutex<PohRecorder>>,
+        transaction_status_sender: Option<TransactionStatusSender>,
+        gossip_vote_sender: ReplayVoteSender,
+        cost_model: Arc<RwLock<CostModel>>,
+        bundle_receiver: Receiver<Vec<PacketBundle>>,
+        exit: Arc<AtomicBool>,
+        tip_manager: TipManager,
+        bundle_account_locker: BundleAccountLocker,
+        max_bundle_retry_duration: Duration,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> Self {
+        const BUNDLE_STAGE_ID: u32 = 10_000;
+        let poh_recorder = poh_recorder.clone();
+        let cluster_info = cluster_info.clone();
+        let block_builder_fee_info = block_builder_fee_info.clone();
+
+        let bundle_thread = Builder::new()
+            .name("solana-bundle-stage".to_string())
+            .spawn(move || {
+                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,
+                    block_builder_fee_info,
+                );
+            })
+            .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<Item = &'a TransactionCost>,
+        transaction_results: impl Iterator<Item = &'a transaction::Result<()>>,
+    ) -> 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<TransactionStatusSender>,
+        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<TransactionStatusSender>,
+        bank_start: &BankStart,
+        bundle_stage_leader_stats: &mut BundleStageLeaderStats,
+        max_bundle_retry_duration: &Duration,
+    ) -> BundleStageResult<Vec<AllExecutionResults>> {
+        let mut account_overrides = AccountOverrides::default();
+
+        let mut execution_results = Vec::new();
+        let mut mint_decimals: HashMap<Pubkey, u8> = 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(),
+                    &mut bundle_stage_leader_stats
+                        .execute_and_commit_timings()
+                        .execute_timings,
+                    Some(&account_overrides),
+                ),
+                "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<TransactionStatusSender>,
+        gossip_vote_sender: &ReplayVoteSender,
+        bank_start: &BankStart,
+        bundle_stage_leader_stats: &mut BundleStageLeaderStats,
+        max_bundle_retry_duration: &Duration,
+    ) -> BundleStageResult<Vec<CommitTransactionDetails>> {
+        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<AllExecutionResults>,
+        bank: &Arc<Bank>,
+        recorder: &TransactionRecorder,
+        bundle_stage_leader_stats: &mut BundleStageLeaderStats,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        gossip_vote_sender: &ReplayVoteSender,
+    ) -> BundleStageResult<Vec<CommitTransactionDetails>> {
+        // *********************************************************************************
+        // 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 (_, record_elapsed) = measure!(
+            Self::try_record(recorder, slot, mixins).map_err(|e| {
+                error!("error recording bundle: {:?}", e);
+                e
+            })?,
+            "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 {
+                        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(),
+                        );
+                    }
+                },
+                "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<Pubkey>,
+    ) -> 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<Bank>,
+        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<Bank>,
+        batch: &TransactionBatch,
+        cached_accounts: &AccountOverrides,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        mint_decimals: &mut HashMap<Pubkey, u8>,
+    ) -> (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<ClusterInfo>,
+    ) -> BundleStageResult<Vec<SanitizedTransaction>> {
+        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::<Vec<SanitizedTransaction>>();
+
+        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<PacketBundle>,
+        blacklisted_accounts: &HashSet<Pubkey>,
+        bank_start: &BankStart,
+        consensus_accounts_cache: &HashSet<Pubkey>,
+        cluster_info: &Arc<ClusterInfo>,
+        recorder: &TransactionRecorder,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        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,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) {
+        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::<VecDeque<(PacketBundle, SanitizedBundle)>>(),
+            "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::<Vec<BundleAccountLockerResult<LockedBundle>>>(),
+            "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,
+                block_builder_fee_info
+            ),
+            "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<ClusterInfo>,
+        recorder: &TransactionRecorder,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        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<ClusterInfo>,
+        recorder: &TransactionRecorder,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        gossip_vote_sender: &ReplayVoteSender,
+        qos_service: &QosService,
+        tip_manager: &TipManager,
+        max_bundle_retry_duration: &Duration,
+        bundle_stage_leader_stats: &mut BundleStageLeaderStats,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> 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 bb_info = block_builder_fee_info.lock().unwrap();
+            let change_tip_receiver_tx = tip_manager.change_tip_receiver_and_block_builder_tx(
+                &my_tip_distribution_pda,
+                &bank_start.working_bank,
+                &cluster_info.keypair(),
+                &bb_info.block_builder,
+                bb_info.block_builder_commission,
+            )?;
+
+            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<BundleAccountLockerResult<LockedBundle>>,
+        bank_start: &BankStart,
+        cluster_info: &Arc<ClusterInfo>,
+        recorder: &TransactionRecorder,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        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,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> Vec<BundleStageResult<()>> {
+        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,
+                            block_builder_fee_info,
+                        )?;
+
+                        *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<Vec<PacketBundle>>,
+        unprocessed_bundles: &mut VecDeque<PacketBundle>,
+        timeout: Duration,
+    ) -> Result<usize, RecvTimeoutError> {
+        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<PacketBundle>,
+        blacklisted_accounts: &HashSet<Pubkey>,
+        consensus_cache_updater: &mut ConsensusCacheUpdater,
+        cluster_info: &Arc<ClusterInfo>,
+        recorder: &TransactionRecorder,
+        poh_recorder: &Arc<Mutex<PohRecorder>>,
+        transaction_status_sender: &Option<TransactionStatusSender>,
+        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,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) {
+        const DROP_BUNDLE_SLOT_OFFSET: u64 = 4;
+
+        let r_poh_recorder = poh_recorder.lock().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(),
+                    block_builder_fee_info,
+                );
+            }
+            // 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<ClusterInfo>,
+        poh_recorder: &Arc<Mutex<PohRecorder>>,
+        transaction_status_sender: Option<TransactionStatusSender>,
+        bundle_receiver: Receiver<Vec<PacketBundle>>,
+        gossip_vote_sender: ReplayVoteSender,
+        id: u32,
+        cost_model: Arc<RwLock<CostModel>>,
+        exit: Arc<AtomicBool>,
+        tip_manager: TipManager,
+        bundle_account_locker: BundleAccountLocker,
+        max_bundle_retry_duration: Duration,
+        block_builder_fee_info: Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) {
+        const LOOP_STATS_METRICS_PERIOD: Duration = Duration::from_secs(1);
+
+        let recorder = poh_recorder.lock().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<PacketBundle> = 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,
+                        &block_builder_fee_info
+                    ),
+                    "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<VersionedTransaction>)>) {
+        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::<Vec<VersionedTransaction>>()
+                    },
+                    "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<VersionedTransaction>)>,
+    ) -> BundleStageResult<()> {
+        match recorder.record(bank_slot, mixins_txs) {
+            Ok(()) => Ok(()),
+            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<Vec<TestOption>>,
+    ) -> 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.lock().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.lock().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.lock().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.lock().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<Slot>,
+    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<Pubkey>,
+}
+
+impl ConsensusCacheUpdater {
+    pub(crate) fn consensus_accounts_cache(&self) -> &HashSet<Pubkey> {
+        &self.consensus_accounts_cache
+    }
+
+    /// Builds a HashSet of all consensus related accounts for the Bank's epoch
+    fn get_consensus_accounts(bank: &Bank) -> HashSet<Pubkey> {
+        let mut consensus_accounts: HashSet<Pubkey> = 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 513c457ce5..87cc8a3615 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -9,8 +9,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;
@@ -20,6 +25,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;
@@ -36,9 +42,11 @@ 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_hasher;
 pub mod packet_threshold;
 pub mod progress_map;
+pub mod proxy;
 pub mod qos_service;
 pub mod repair_generic_traversal;
 pub mod repair_response;
@@ -61,6 +69,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;
@@ -92,3 +101,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<Mutex<Token>>,
+}
+
+impl AuthInterceptor {
+    pub(crate) fn new(access_token: Arc<Mutex<Token>>) -> Self {
+        Self { access_token }
+    }
+}
+
+impl Interceptor for AuthInterceptor {
+    fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, 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<Mutex<Token>>,
+        cluster_info: Arc<ClusterInfo>,
+        exit: Arc<AtomicBool>,
+    ) {
+        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<Channel>,
+        url: String,
+        (access_token, mut refresh_token): (Arc<Mutex<Token>>, Token),
+        cluster_info: Arc<ClusterInfo>,
+        sleep_interval: Duration,
+        exit: Arc<AtomicBool>,
+    ) -> 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<Channel>,
+        refresh_token: Token,
+    ) -> crate::proxy::Result<Token> {
+        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<Channel>,
+        // 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<Token>) -> crate::proxy::Result<Token> {
+        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..954bdf153f
--- /dev/null
+++ b/core/src/proxy/block_engine_stage.rs
@@ -0,0 +1,383 @@
+//! 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,
+            BlockBuilderFeeInfoRequest,
+        },
+    },
+    solana_gossip::cluster_info::ClusterInfo,
+    solana_perf::packet::PacketBatch,
+    solana_sdk::{pubkey::Pubkey, 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)
+        );
+    }
+}
+
+pub struct BlockBuilderFeeInfo {
+    pub block_builder: Pubkey,
+    pub block_builder_commission: u64,
+}
+
+#[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<JoinHandle<()>>,
+}
+
+impl BlockEngineStage {
+    pub fn new(
+        block_engine_config: BlockEngineConfig,
+        // Channel that bundles get piped through.
+        bundle_tx: Sender<Vec<PacketBundle>>,
+        // The keypair stored here is used to sign auth challenges.
+        cluster_info: Arc<ClusterInfo>,
+        // Channel that non-trusted packets get piped through.
+        packet_tx: Sender<PacketBatch>,
+        // Channel that trusted packets get piped through.
+        verified_packet_tx: Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        exit: Arc<AtomicBool>,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> Self {
+        let BlockEngineConfig {
+            auth_service_endpoint,
+            backend_endpoint,
+            trust_packets,
+        } = block_engine_config;
+
+        let access_token = Arc::new(Mutex::new(Token::default()));
+        let block_builder_fee_info = block_builder_fee_info.clone();
+
+        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,
+                    block_builder_fee_info,
+                ));
+            })
+            .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<Mutex<Token>>,
+        block_engine_endpoint: Endpoint,
+        bundle_tx: Sender<Vec<PacketBundle>>,
+        packet_tx: Sender<PacketBatch>,
+        trust_packets: bool,
+        verified_packet_tx: Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        exit: Arc<AtomicBool>,
+        block_builder_fee_info: Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) {
+        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,
+                        &block_builder_fee_info,
+                    )
+                    .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<Vec<PacketBundle>>,
+        mut client: BlockEngineValidatorClient<InterceptedService<Channel, AuthInterceptor>>,
+        packet_tx: &Sender<PacketBatch>,
+        trust_packets: bool,
+        verified_packet_tx: &Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        exit: &Arc<AtomicBool>,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> 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();
+
+        let block_builder_info = client
+            .get_block_builder_fee_info(BlockBuilderFeeInfoRequest {})
+            .await?
+            .into_inner();
+        {
+            let mut bb_fee = block_builder_fee_info.lock().unwrap();
+            bb_fee.block_builder_commission = block_builder_info.commission;
+            bb_fee.block_builder =
+                Pubkey::from_str(&block_builder_info.pubkey).unwrap_or(bb_fee.block_builder);
+        }
+
+        backoff.reset();
+
+        Self::consume_bundle_and_packet_stream(
+            client,
+            (subscribe_bundles_stream, subscribe_packets_stream),
+            bundle_tx,
+            packet_tx,
+            trust_packets,
+            verified_packet_tx,
+            exit,
+            block_builder_fee_info,
+        )
+        .await
+    }
+
+    async fn consume_bundle_and_packet_stream(
+        mut client: BlockEngineValidatorClient<InterceptedService<Channel, AuthInterceptor>>,
+        (mut bundle_stream, mut packet_stream): (
+            Streaming<block_engine::SubscribeBundlesResponse>,
+            Streaming<block_engine::SubscribePacketsResponse>,
+        ),
+        bundle_tx: &Sender<Vec<PacketBundle>>,
+        packet_tx: &Sender<PacketBatch>,
+        trust_packets: bool,
+        verified_packet_tx: &Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        exit: &Arc<AtomicBool>,
+        block_builder_fee_info: &Arc<Mutex<BlockBuilderFeeInfo>>,
+    ) -> crate::proxy::Result<()> {
+        const METRICS_TICK: Duration = Duration::from_secs(1);
+        const MAINTENANCE_TICK: Duration = Duration::from_secs(10 * 60);
+
+        let mut block_engine_stats = BlockEngineStageStats::default();
+        let mut metrics_tick = interval(METRICS_TICK);
+        let mut maintenance_tick = interval(MAINTENANCE_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();
+                }
+                _ = maintenance_tick.tick() => {
+                    let block_builder_info = client.get_block_builder_fee_info(BlockBuilderFeeInfoRequest{}).await?.into_inner();
+                    let mut bb_fee = block_builder_fee_info.lock().unwrap();
+                    bb_fee.block_builder_commission = block_builder_info.commission;
+                    bb_fee.block_builder = Pubkey::from_str(&block_builder_info.pubkey).unwrap_or(bb_fee.block_builder);
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    fn handle_block_engine_maybe_bundles(
+        maybe_bundles_response: Result<Option<block_engine::SubscribeBundlesResponse>, Status>,
+        bundle_sender: &Sender<Vec<PacketBundle>>,
+        block_engine_stats: &mut BlockEngineStageStats,
+    ) -> crate::proxy::Result<()> {
+        let bundles_response = maybe_bundles_response?.ok_or(ProxyError::GrpcStreamDisconnected)?;
+        let bundles: Vec<PacketBundle> = 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<PacketBatch>,
+        verified_packet_tx: &Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        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<ClusterInfo>,
+        // Channel that heartbeats are received from. Entirely responsible for triggering switch-overs.
+        heartbeat_rx: Receiver<HeartbeatEvent>,
+        // Channel that packets from FetchStage are intercepted from.
+        packet_intercept_rx: Receiver<PacketBatch>,
+        // Intercepted packets get piped through here.
+        packet_tx: Sender<PacketBatch>,
+        exit: Arc<AtomicBool>,
+    ) -> 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<ClusterInfo>,
+        heartbeat_rx: Receiver<HeartbeatEvent>,
+        packet_intercept_rx: Receiver<PacketBatch>,
+        packet_tx: Sender<PacketBatch>,
+        exit: Arc<AtomicBool>,
+    ) -> 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<ClusterInfo>,
+        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<T> = result::Result<T, ProxyError>;
+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<JoinHandle<()>>,
+}
+
+impl RelayerStage {
+    pub fn new(
+        relayer_config: RelayerConfig,
+        // The keypair stored here is used to sign auth challenges.
+        cluster_info: Arc<ClusterInfo>,
+        // Channel that server-sent heartbeats are piped through.
+        heartbeat_tx: Sender<HeartbeatEvent>,
+        // Channel that non-trusted streamed packets are piped through.
+        packet_tx: Sender<PacketBatch>,
+        // Channel that trusted streamed packets are piped through.
+        verified_packet_tx: Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        exit: Arc<AtomicBool>,
+    ) -> 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<Mutex<Token>>,
+        heartbeat_tx: Sender<HeartbeatEvent>,
+        expected_heartbeat_interval: Duration,
+        oldest_allowed_heartbeat: Duration,
+        packet_tx: Sender<PacketBatch>,
+        relayer_endpoint: Endpoint,
+        verified_packet_tx: Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        trust_packets: bool,
+        exit: Arc<AtomicBool>,
+    ) {
+        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<InterceptedService<Channel, AuthInterceptor>>,
+        heartbeat_tx: &Sender<HeartbeatEvent>,
+        expected_heartbeat_interval: Duration,
+        oldest_allowed_heartbeat: Duration,
+        packet_tx: &Sender<PacketBatch>,
+        verified_packet_tx: &Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        trust_packets: bool,
+        exit: &Arc<AtomicBool>,
+    ) -> 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::<Ipv4Addr>()?);
+            let tpu_forward_ip = IpAddr::from(tpu_forward_addr.ip.parse::<Ipv4Addr>()?);
+
+            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<HeartbeatEvent>,
+        expected_heartbeat_interval: Duration,
+        oldest_allowed_heartbeat: Duration,
+        mut packet_stream: Streaming<relayer::SubscribePacketsResponse>,
+        packet_tx: &Sender<PacketBatch>,
+        trust_packets: bool,
+        verified_packet_tx: &Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        exit: &Arc<AtomicBool>,
+    ) -> 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<HeartbeatEvent>,
+        last_heartbeat_ts: &mut Instant,
+        packet_tx: &Sender<PacketBatch>,
+        trust_packets: bool,
+        verified_packet_tx: &Sender<(Vec<PacketBatch>, Option<SigverifyTracerPacketStats>)>,
+        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 fb493f6743..e9b67591ed 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<Item = &'a TransactionCost>,
         transaction_qos_results: impl Iterator<Item = &'a transaction::Result<()>>,
         bank: &Arc<Bank>,
diff --git a/core/src/retransmit_stage.rs b/core/src/retransmit_stage.rs
index 07d36a2b4e..189fa77b2c 100644
--- a/core/src/retransmit_stage.rs
+++ b/core/src/retransmit_stage.rs
@@ -35,7 +35,7 @@ use {
     solana_streamer::sendmmsg::{multi_target_send, SendPktsError},
     std::{
         collections::{BTreeSet, HashMap, HashSet},
-        net::UdpSocket,
+        net::{SocketAddr, UdpSocket},
         ops::{AddAssign, DerefMut},
         sync::{
             atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
@@ -206,6 +206,7 @@ fn retransmit(
     max_slots: &MaxSlots,
     first_shreds_received: &Mutex<BTreeSet<Slot>>,
     rpc_subscriptions: Option<&RpcSubscriptions>,
+    shred_receiver_addr: Option<SocketAddr>,
 ) -> Result<(), RecvTimeoutError> {
     const RECV_TIMEOUT: Duration = Duration::from_secs(1);
     let mut shreds = shreds_receiver.recv_timeout(RECV_TIMEOUT)?;
@@ -264,8 +265,11 @@ fn retransmit(
             };
         let cluster_nodes =
             cluster_nodes_cache.get(shred_slot, &root_bank, &working_bank, cluster_info);
-        let (root_distance, addrs) =
+        let (root_distance, mut addrs) =
             cluster_nodes.get_retransmit_addrs(slot_leader, shred, &root_bank, DATA_PLANE_FANOUT);
+        if let Some(shred_receiver_addr) = shred_receiver_addr {
+            addrs.push(shred_receiver_addr);
+        }
         let addrs: Vec<_> = addrs
             .into_iter()
             .filter(|addr| ContactInfo::is_valid_address(addr, socket_addr_space))
@@ -341,6 +345,7 @@ pub fn retransmitter(
     shreds_receiver: Receiver<Vec<Shred>>,
     max_slots: Arc<MaxSlots>,
     rpc_subscriptions: Option<Arc<RpcSubscriptions>>,
+    shred_receiver_addr: Option<SocketAddr>,
 ) -> JoinHandle<()> {
     let cluster_nodes_cache = ClusterNodesCache::<RetransmitStage>::new(
         CLUSTER_NODES_CACHE_NUM_EPOCH_CAP,
@@ -375,6 +380,7 @@ pub fn retransmitter(
                     &max_slots,
                     &first_shreds_received,
                     rpc_subscriptions.as_deref(),
+                    shred_receiver_addr,
                 ) {
                     Ok(()) => (),
                     Err(RecvTimeoutError::Timeout) => (),
@@ -418,6 +424,7 @@ impl RetransmitStage {
         rpc_subscriptions: Option<Arc<RpcSubscriptions>>,
         duplicate_slots_sender: Sender<Slot>,
         ancestor_hashes_replay_update_receiver: AncestorHashesReplayUpdateReceiver,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> Self {
         let (retransmit_sender, retransmit_receiver) = unbounded();
 
@@ -429,6 +436,7 @@ impl RetransmitStage {
             retransmit_receiver,
             max_slots,
             rpc_subscriptions,
+            shred_receiver_addr,
         );
 
         let cluster_slots_service = ClusterSlotsService::new(
diff --git a/core/src/tip_manager.rs b/core/src/tip_manager.rs
new file mode 100644
index 0000000000..06198331ff
--- /dev/null
+++ b/core/src/tip_manager.rs
@@ -0,0 +1,473 @@
+use {
+    anchor_lang::{
+        solana_program::hash::Hash, AccountDeserialize, InstructionData, ToAccountMetas,
+    },
+    log::warn,
+    solana_runtime::bank::Bank,
+    solana_sdk::{
+        account::ReadableAccount,
+        bundle::error::TipPaymentError,
+        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<T> = std::result::Result<T, TipPaymentError>;
+
+#[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<Keypair>,
+
+    /// 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<Mutex<()>>,
+}
+
+#[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<Pubkey> {
+        Ok(self.get_tip_payment_config_account(bank)?.tip_receiver)
+    }
+
+    pub fn get_tip_accounts(&self) -> HashSet<Pubkey> {
+        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<Config> {
+        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: 10,
+                max_validator_commission_bps: 10_000,
+                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_and_block_builder_tx(
+        &self,
+        new_tip_receiver: &Pubkey,
+        bank: &Bank,
+        keypair: &Keypair,
+        block_builder: &Pubkey,
+        block_builder_commission: u64,
+    ) -> Result<SanitizedTransaction> {
+        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,
+                block_builder: config.block_builder,
+                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),
+        };
+        let change_block_builder_ix = Instruction {
+            program_id: self.tip_payment_program_info.program_id,
+            data: tip_payment::instruction::ChangeBlockBuilder {
+                block_builder_commission,
+            }
+            .data(),
+            accounts: tip_payment::accounts::ChangeBlockBuilder {
+                config: self.tip_payment_program_info.config_pda_bump.0,
+                tip_receiver: *new_tip_receiver, // tip receiver will have just changed in previous ix
+                old_block_builder: config.block_builder,
+                new_block_builder: *block_builder,
+                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(
+                &[change_tip_ix, change_block_builder_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<Bank>) -> 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<Bank>,
+    ) -> 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 7623babda0..59b069720c 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::{BlockBuilderFeeInfo, 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::{bounded, unbounded, Receiver, RecvTimeoutError},
     solana_client::connection_cache::ConnectionCache,
@@ -29,13 +37,14 @@ use {
         cost_model::CostModel,
         vote_sender_types::{ReplayVoteReceiver, ReplayVoteSender},
     },
-    solana_sdk::signature::Keypair,
+    solana_sdk::signature::{Keypair, Signer},
     solana_streamer::{
         quic::{spawn_server, StreamStats, MAX_STAKED_CONNECTIONS, MAX_UNSTAKED_CONNECTIONS},
         streamer::StakedNodes,
     },
     std::{
-        net::UdpSocket,
+        collections::HashSet,
+        net::{SocketAddr, UdpSocket},
         sync::{atomic::AtomicBool, Arc, Mutex, RwLock},
         thread,
         time::Duration,
@@ -63,6 +72,9 @@ pub struct Tpu {
     fetch_stage: FetchStage,
     sigverify_stage: SigVerifyStage,
     vote_sigverify_stage: SigVerifyStage,
+    maybe_relayer_stage: Option<RelayerStage>,
+    maybe_block_engine_stage: Option<BlockEngineStage>,
+    maybe_fetch_stage_manager: Option<FetchStageManager>,
     banking_stage: BankingStage,
     cluster_info_vote_listener: ClusterInfoVoteListener,
     broadcast_stage: BroadcastStage,
@@ -71,6 +83,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 {
@@ -101,6 +114,10 @@ impl Tpu {
         keypair: &Keypair,
         staked_nodes: &Arc<RwLock<StakedNodes>>,
         tpu_enable_udp: bool,
+        maybe_block_engine_config: Option<BlockEngineConfig>,
+        maybe_relayer_config: Option<RelayerConfig>,
+        tip_manager_config: TipManagerConfig,
+        shred_receiver_address: Option<SocketAddr>,
     ) -> Self {
         let TpuSockets {
             transactions: transactions_sockets,
@@ -111,7 +128,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(
@@ -119,7 +146,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 +189,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 +214,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 +230,45 @@ impl Tpu {
             )
         };
 
+        let block_builder_fee_info = Arc::new(Mutex::new(BlockBuilderFeeInfo {
+            block_builder: cluster_info.keypair().pubkey(),
+            block_builder_commission: 0,
+        }));
+
+        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(),
+                &block_builder_fee_info,
+            )
+        });
+
+        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,17 +287,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(),
             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,
+            &block_builder_fee_info,
         );
 
         let broadcast_stage = broadcast_type.new_broadcast_stage(
@@ -243,12 +333,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,
@@ -257,6 +351,7 @@ impl Tpu {
             find_packet_sender_stake_stage,
             vote_find_packet_sender_stake_stage,
             staked_nodes_updater_service,
+            bundle_stage,
         }
     }
 
@@ -288,7 +383,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 78d8e76e0d..b3349b68dd 100644
--- a/core/src/tvu.rs
+++ b/core/src/tvu.rs
@@ -61,9 +61,9 @@ use {
     solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Keypair},
     std::{
         collections::HashSet,
-        net::UdpSocket,
+        net::{SocketAddr, UdpSocket},
         sync::{atomic::AtomicBool, Arc, Mutex, RwLock},
-        thread,
+        thread::{self},
         time::Duration,
     },
 };
@@ -157,6 +157,7 @@ impl Tvu {
         wait_to_vote_slot: Option<Slot>,
         pruned_banks_receiver: DroppedSlotsReceiver,
         connection_cache: &Arc<ConnectionCache>,
+        shred_receiver_addr: Option<SocketAddr>,
     ) -> Self {
         let TvuSockets {
             repair: repair_socket,
@@ -225,6 +226,7 @@ impl Tvu {
             Some(rpc_subscriptions.clone()),
             duplicate_slots_sender,
             ancestor_hashes_replay_update_receiver,
+            shred_receiver_addr,
         );
 
         let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = unbounded();
@@ -544,6 +546,7 @@ pub mod tests {
             None,
             pruned_banks_receiver,
             &Arc::new(ConnectionCache::default()),
+            None,
         );
         exit.store(true, Ordering::Relaxed);
         tvu.join().unwrap();
diff --git a/core/src/validator.rs b/core/src/validator.rs
index 86a6aa5f57..47424c8400 100644
--- a/core/src/validator.rs
+++ b/core/src/validator.rs
@@ -8,6 +8,7 @@ use {
         cluster_info_vote_listener::VoteTracker,
         completed_data_sets_service::CompletedDataSetsService,
         consensus::{reconcile_blockstore_roots_with_tower, Tower},
+        proxy::{block_engine_stage::BlockEngineConfig, relayer_stage::RelayerConfig},
         rewards_recorder_service::{RewardsRecorderSender, RewardsRecorderService},
         sample_performance_service::SamplePerformanceService,
         serve_repair::ServeRepair,
@@ -16,6 +17,7 @@ use {
         snapshot_packager_service::SnapshotPackagerService,
         stats_reporter_service::StatsReporterService,
         system_monitor_service::{verify_udp_stats_access, SystemMonitorService},
+        tip_manager::TipManagerConfig,
         tower_storage::TowerStorage,
         tpu::{Tpu, TpuSockets, DEFAULT_TPU_COALESCE_MS},
         tvu::{Tvu, TvuConfig, TvuSockets},
@@ -172,6 +174,10 @@ pub struct ValidatorConfig {
     pub accounts_shrink_ratio: AccountShrinkThreshold,
     pub wait_to_vote_slot: Option<Slot>,
     pub ledger_column_options: LedgerColumnOptions,
+    pub maybe_relayer_config: Option<RelayerConfig>,
+    pub maybe_block_engine_config: Option<BlockEngineConfig>,
+    pub shred_receiver_address: Option<SocketAddr>,
+    pub tip_manager_config: TipManagerConfig,
 }
 
 impl Default for ValidatorConfig {
@@ -235,6 +241,10 @@ impl Default for ValidatorConfig {
             accounts_db_config: None,
             wait_to_vote_slot: None,
             ledger_column_options: LedgerColumnOptions::default(),
+            maybe_relayer_config: None,
+            maybe_block_engine_config: None,
+            shred_receiver_address: None,
+            tip_manager_config: TipManagerConfig::default(),
         }
     }
 }
@@ -962,6 +972,7 @@ impl Validator {
             config.wait_to_vote_slot,
             pruned_banks_receiver,
             &connection_cache,
+            config.shred_receiver_address,
         );
 
         let tpu = Tpu::new(
@@ -997,12 +1008,20 @@ impl Validator {
             &identity_keypair,
             &staked_nodes,
             tpu_enable_udp,
+            config.maybe_block_engine_config.clone(),
+            config.maybe_relayer_config.clone(),
+            config.tip_manager_config.clone(),
+            config.shred_receiver_address,
         );
 
         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;
@@ -1884,7 +1903,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/snapshots.rs b/core/tests/snapshots.rs
index d86d26bc92..3abb534ecf 100644
--- a/core/tests/snapshots.rs
+++ b/core/tests/snapshots.rs
@@ -837,6 +837,7 @@ mod tests {
             false,
             Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
             None,
+            None,
         )?;
 
         assert_eq!(bank, &deserialized_bank);
@@ -1016,6 +1017,7 @@ mod tests {
             false,
             Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
             None,
+            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..0f3c3e38f8
--- /dev/null
+++ b/dev/Dockerfile
@@ -0,0 +1,44 @@
+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 --release && cp target/release/solana* ./docker-output && \
+    cargo build --release --manifest-path solana-accountsdb-connector/Cargo.toml && \
+    cp solana-accountsdb-connector/target/release/libsolana* ./docker-output
+
diff --git a/dos/Cargo.toml b/dos/Cargo.toml
index 3fafa13806..527c572a86 100644
--- a/dos/Cargo.toml
+++ b/dos/Cargo.toml
@@ -11,7 +11,7 @@ description = "Tool to send various requests to cluster in order to evaluate the
 
 [dependencies]
 bincode = "1.3.3"
-clap = {version = "3.1.5", features = ["derive", "cargo"]}
+clap = { version = "3.1.5", features = ["derive", "cargo"] }
 log = "0.4.14"
 rand = "0.7.0"
 serde = "1.0.136"
diff --git a/entry/src/entry.rs b/entry/src/entry.rs
index 637ad52f1b..9fc337731f 100644
--- a/entry/src/entry.rs
+++ b/entry/src/entry.rs
@@ -202,7 +202,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 28126276f3..3fbebe30df 100644
--- a/entry/src/poh.rs
+++ b/entry/src/poh.rs
@@ -75,19 +75,30 @@ impl Poh {
     }
 
     pub fn record(&mut self, mixin: Hash) -> Option<PohEntry> {
-        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<Vec<PohEntry>> {
+        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<PohEntry> {
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/frozen-abi/Cargo.toml b/frozen-abi/Cargo.toml
index 4f29e8a3c5..4c26c150a1 100644
--- a/frozen-abi/Cargo.toml
+++ b/frozen-abi/Cargo.toml
@@ -15,8 +15,8 @@ bv = { version = "0.11.1", features = ["serde"] }
 lazy_static = "1.4.0"
 log = "0.4.14"
 serde = "1.0.136"
-serde_derive = "1.0.103"
 serde_bytes = "0.11"
+serde_derive = "1.0.103"
 sha2 = "0.10.2"
 solana-frozen-abi-macro = { path = "macro", version = "=1.13.5" }
 thiserror = "1.0"
diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs
index c73df028d7..771a4e2684 100644
--- a/gossip/src/cluster_info.rs
+++ b/gossip/src/cluster_info.rs
@@ -508,6 +508,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..b68df80171
--- /dev/null
+++ b/jito-programs
@@ -0,0 +1 @@
+Subproject commit b68df80171328fbde0d6c8dc67495efc6d25e4b7
diff --git a/jito-protos/Cargo.toml b/jito-protos/Cargo.toml
new file mode 100644
index 0000000000..e9ce1904d0
--- /dev/null
+++ b/jito-protos/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "jito-protos"
+version = "1.13.5"
+edition = "2021"
+publish = false
+
+[dependencies]
+bytes = "1.1.0"
+crossbeam-epoch = "=0.9.5" # TODO (LB): this allows compliation on rust_nightly in ci
+lock_api = "=0.4.6" # TODO (LB): this allows compliation on rust_nightly in ci due to
+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..05d210980f
--- /dev/null
+++ b/jito-protos/protos
@@ -0,0 +1 @@
+Subproject commit 05d210980f34a7c974d7ed1a4dbcb2ce1fca00b3
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 674a19778f..f494cded01 100644
--- a/ledger-tool/src/main.rs
+++ b/ledger-tool/src/main.rs
@@ -824,6 +824,11 @@ fn load_bank_forks(
         vec![non_primary_accounts_path]
     };
 
+    info!(
+        "load_bank_forks halt_at_slot: {:?}",
+        process_options.halt_at_slot
+    );
+
     let (accounts_package_sender, _) = unbounded();
     bank_forks_utils::load(
         genesis_config,
@@ -2328,6 +2333,7 @@ fn main() {
                         dev_halt_at_slot: Some(snapshot_slot),
                         new_hard_forks,
                         poh_verify: false,
+                        halt_at_slot: Some(snapshot_slot),
                         ..ProcessOptions::default()
                     },
                     snapshot_archive_path,
diff --git a/ledger/src/bank_forks_utils.rs b/ledger/src/bank_forks_utils.rs
index efaf5aca99..068ac54c19 100644
--- a/ledger/src/bank_forks_utils.rs
+++ b/ledger/src/bank_forks_utils.rs
@@ -102,6 +102,7 @@ pub fn load_bank_forks(
 
         if snapshot_utils::get_highest_full_snapshot_archive_info(
             &snapshot_config.snapshot_archives_dir,
+            process_options.halt_at_slot,
         )
         .is_some()
         {
@@ -198,7 +199,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<PathBuf>,
     shrink_paths: Option<Vec<PathBuf>>,
@@ -212,6 +213,13 @@ fn bank_forks_from_snapshot(
         process::exit(1);
     }
 
+    info!(
+        "halt_at_slot: {:?} bank_snapshots_dir: {:?}, snapshot_archives_dir: {:?}",
+        process_options.halt_at_slot,
+        snapshot_config.bank_snapshots_dir,
+        snapshot_config.snapshot_archives_dir
+    );
+
     let (deserialized_bank, full_snapshot_archive_info, incremental_snapshot_archive_info) =
         snapshot_utils::bank_from_latest_snapshot_archives(
             &snapshot_config.bank_snapshots_dir,
@@ -229,6 +237,7 @@ fn bank_forks_from_snapshot(
             process_options.verify_index,
             process_options.accounts_db_config.clone(),
             accounts_update_notifier,
+            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 6ea3684f21..b2f8085200 100644
--- a/ledger/src/blockstore_processor.rs
+++ b/ledger/src/blockstore_processor.rs
@@ -168,7 +168,7 @@ fn execute_batch(
     let mut mint_decimals: HashMap<Pubkey, u8> = 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![]
     };
@@ -225,7 +225,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![]
         };
@@ -570,6 +570,7 @@ pub struct ProcessOptions {
     pub accounts_db_config: Option<AccountsDbConfig>,
     pub verify_index: bool,
     pub shrink_ratio: AccountShrinkThreshold,
+    pub halt_at_slot: Option<Slot>,
 }
 
 pub fn test_process_blockstore(
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<Pubkey, u8>,
+    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<Pubkey, u8>,
+    account_overrides: Option<&AccountOverrides>,
 ) -> Option<TokenBalanceData> {
-    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 1cdf9ef938..ed27f969a8 100644
--- a/local-cluster/src/local_cluster_snapshot_utils.rs
+++ b/local-cluster/src/local_cluster_snapshot_utils.rs
@@ -69,7 +69,7 @@ impl LocalCluster {
         );
         loop {
             if let Some(full_snapshot_archive_info) =
-                snapshot_utils::get_highest_full_snapshot_archive_info(&snapshot_archives_dir)
+                snapshot_utils::get_highest_full_snapshot_archive_info(&snapshot_archives_dir, None)
             {
                 match next_snapshot_type {
                     NextSnapshotType::FullSnapshot => {
@@ -82,6 +82,7 @@ impl LocalCluster {
                             snapshot_utils::get_highest_incremental_snapshot_archive_info(
                                 &snapshot_archives_dir,
                                 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 bd7bb45dda..9d531c42e0 100644
--- a/local-cluster/src/validator_configs.rs
+++ b/local-cluster/src/validator_configs.rs
@@ -65,6 +65,10 @@ pub fn safe_clone_config(config: &ValidatorConfig) -> ValidatorConfig {
         accounts_db_config: config.accounts_db_config.clone(),
         wait_to_vote_slot: config.wait_to_vote_slot,
         ledger_column_options: config.ledger_column_options.clone(),
+        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 f672e17d2c..b3def2c572 100644
--- a/local-cluster/tests/local_cluster.rs
+++ b/local-cluster/tests/local_cluster.rs
@@ -785,6 +785,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st
     let downloaded_full_snapshot_archive_info =
         snapshot_utils::get_highest_full_snapshot_archive_info(
             validator_snapshot_test_config.snapshot_archives_dir.path(),
+            None,
         )
         .unwrap();
     debug!(
@@ -821,6 +822,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st
         snapshot_utils::get_highest_incremental_snapshot_archive_info(
             validator_snapshot_test_config.snapshot_archives_dir.path(),
             full_snapshot_archive_info.slot(),
+            None,
         )
         .unwrap();
     debug!(
@@ -940,6 +942,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st
     let leader_full_snapshot_archive_info_for_comparison = {
         let validator_full_snapshot = snapshot_utils::get_highest_full_snapshot_archive_info(
             validator_snapshot_test_config.snapshot_archives_dir.path(),
+            None,
         )
         .unwrap();
 
@@ -982,6 +985,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st
     let validator_full_snapshot_slot_at_startup =
         snapshot_utils::get_highest_full_snapshot_archive_slot(
             validator_snapshot_test_config.snapshot_archives_dir.path(),
+            None,
         )
         .unwrap();
 
@@ -1003,6 +1007,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st
         if let Some(highest_full_snapshot_info) =
             snapshot_utils::get_highest_full_snapshot_archive_info(
                 validator_snapshot_test_config.snapshot_archives_dir.path(),
+                None,
             )
         {
             if highest_full_snapshot_info.slot() > validator_full_snapshot_slot_at_startup {
@@ -1010,6 +1015,7 @@ fn test_incremental_snapshot_download_with_crossing_full_snapshot_interval_at_st
                     snapshot_utils::get_highest_incremental_snapshot_archive_info(
                         validator_snapshot_test_config.snapshot_archives_dir.path(),
                         highest_full_snapshot_info.slot(),
+                        None,
                     )
                 {
                     info!("Success! Made new full and incremental snapshots!");
@@ -1195,7 +1201,7 @@ fn test_snapshots_blockstore_floor() {
 
     let archive_info = loop {
         let archive =
-            snapshot_utils::get_highest_full_snapshot_archive_info(&snapshot_archives_dir);
+            snapshot_utils::get_highest_full_snapshot_archive_info(&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 82b68e7968..5d10992f42 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.14"
 
diff --git a/measure/src/lib.rs b/measure/src/lib.rs
index 5b42bfa9d1..8fd0b24444 100644
--- a/measure/src/lib.rs
+++ b/measure/src/lib.rs
@@ -1,2 +1,3 @@
 #![allow(clippy::integer_arithmetic)]
+pub mod macros;
 pub mod measure;
diff --git a/measure/src/macros.rs b/measure/src/macros.rs
new file mode 100644
index 0000000000..e688a631a6
--- /dev/null
+++ b/measure/src/macros.rs
@@ -0,0 +1,143 @@
+/// Measure this expression
+///
+/// Use `measure!()` when you have an expression that you want to measure.  `measure!()` will start
+/// a new [`Measure`], evaluate your expression, stop the [`Measure`], and then return the
+/// [`Measure`] object along with your expression's return value.
+///
+/// [`Measure`]: crate::measure::Measure
+///
+/// # Examples
+///
+/// ```
+/// // Measure functions
+/// # use solana_measure::measure;
+/// # fn foo() {}
+/// # fn bar(x: i32) {}
+/// # fn add(x: i32, y: i32) -> i32 {x + y}
+/// let (result, measure) = measure!(foo(), "foo takes no parameters");
+/// let (result, measure) = measure!(bar(42), "bar takes one parameter");
+/// let (result, measure) = measure!(add(1, 2), "add takes two parameters and returns a value");
+/// # assert_eq!(result, 1 + 2);
+/// ```
+///
+/// ```
+/// // Measure methods
+/// # use solana_measure::measure;
+/// # struct Foo {
+/// #     f: i32,
+/// # }
+/// # impl Foo {
+/// #     fn frobnicate(&self, bar: i32) -> i32 {
+/// #         self.f * bar
+/// #     }
+/// # }
+/// let foo = Foo { f: 42 };
+/// let (result, measure) = measure!(foo.frobnicate(2), "measure methods");
+/// # assert_eq!(result, 42 * 2);
+/// ```
+///
+/// ```
+/// // Measure expression blocks
+/// # use solana_measure::measure;
+/// # fn complex_calculation() -> i32 { 42 }
+/// # fn complex_transform(x: i32) -> i32 { x + 3 }
+/// # fn record_result(y: i32) {}
+/// let (result, measure) = measure!(
+///     {
+///         let x = complex_calculation();
+///         # assert_eq!(x, 42);
+///         let y = complex_transform(x);
+///         # assert_eq!(y, 42 + 3);
+///         record_result(y);
+///         y
+///     },
+///     "measure a block of many operations",
+/// );
+/// # assert_eq!(result, 42 + 3);
+/// ```
+///
+/// ```
+/// // The `name` parameter is optional
+/// # use solana_measure::measure;
+/// # fn meow() {};
+/// let (result, measure) = measure!(meow());
+/// ```
+#[macro_export]
+macro_rules! measure {
+    ($val:expr, $name:tt $(,)?) => {{
+        let mut measure = $crate::measure::Measure::start($name);
+        let result = $val;
+        measure.stop();
+        (result, measure)
+    }};
+    ($val:expr) => {
+        measure!($val, "")
+    };
+}
+
+#[cfg(test)]
+mod tests {
+    use std::{thread::sleep, time::Duration};
+
+    fn my_multiply(x: i32, y: i32) -> i32 {
+        x * y
+    }
+
+    fn square(x: i32) -> i32 {
+        my_multiply(x, x)
+    }
+
+    struct SomeStruct {
+        x: i32,
+    }
+    impl SomeStruct {
+        fn add_to(&self, x: i32) -> i32 {
+            x + self.x
+        }
+    }
+
+    #[test]
+    fn test_measure_macro() {
+        // Ensure that the measurement side actually works
+        {
+            let (_result, measure) = measure!(sleep(Duration::from_secs(1)), "test");
+            assert!(measure.as_s() >= 0.99f32 && measure.as_s() <= 1.01f32);
+            assert!(measure.as_ms() >= 990 && measure.as_ms() <= 1_010);
+            assert!(measure.as_us() >= 999_000 && measure.as_us() <= 1_010_000);
+        }
+
+        // Ensure that the macro can be called with functions
+        {
+            let (result, _measure) = measure!(my_multiply(3, 4), "test");
+            assert_eq!(result, 3 * 4);
+
+            let (result, _measure) = measure!(square(5), "test");
+            assert_eq!(result, 5 * 5)
+        }
+
+        // Ensure that the macro can be called with methods
+        {
+            let some_struct = SomeStruct { x: 42 };
+            let (result, _measure) = measure!(some_struct.add_to(4), "test");
+            assert_eq!(result, 42 + 4);
+        }
+
+        // Ensure that the macro can be called with blocks
+        {
+            let (result, _measure) = measure!({ 1 + 2 }, "test");
+            assert_eq!(result, 3);
+        }
+
+        // Ensure that the macro can be called with a trailing comma
+        {
+            let (result, _measure) = measure!(square(5), "test",);
+            assert_eq!(result, 5 * 5)
+        }
+
+        // Ensure that the macro can be called without a name
+        {
+            let (result, _measure) = measure!(square(5));
+            assert_eq!(result, 5 * 5)
+        }
+    }
+}
diff --git a/merkle-tree/src/merkle_tree.rs b/merkle-tree/src/merkle_tree.rs
index 2834fbee90..c79c177477 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<Hash>,
@@ -36,6 +36,14 @@ impl<'a> ProofEntry<'a> {
         assert!((None == left_sibling) ^ (None == right_sibling));
         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)]
@@ -60,6 +68,10 @@ impl<'a> Proof<'a> {
         });
         matches!(result, Some(_))
     }
+
+    pub fn get_proof_entries(self) -> Vec<ProofEntry<'a>> {
+        self.0
+    }
 }
 
 impl MerkleTree {
@@ -95,7 +107,7 @@ impl MerkleTree {
         }
     }
 
-    pub fn new<T: AsRef<[u8]>>(items: &[T]) -> Self {
+    pub fn new<T: AsRef<[u8]>>(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 c11d9fd995..84790e4bcb 100755
--- a/multinode-demo/bootstrap-validator.sh
+++ b/multinode-demo/bootstrap-validator.sh
@@ -103,6 +103,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
     else
       echo "Unknown argument: $1"
       $program --help
@@ -117,6 +147,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
@@ -138,6 +169,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
@@ -146,6 +179,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 7c766a1beb..9862e08330 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
@@ -258,6 +285,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 d812b170c6..b7dbe5cbf8 100644
--- a/perf/src/sigverify.rs
+++ b/perf/src/sigverify.rs
@@ -120,7 +120,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 480d377d2a..509a9b266d 100644
--- a/poh/src/poh_recorder.rs
+++ b/poh/src/poh_recorder.rs
@@ -56,9 +56,14 @@ pub enum PohRecorderError {
     SendError(#[from] SendError<WorkingBankEntry>),
 }
 
-type Result<T> = std::result::Result<T, PohRecorderError>;
+pub type Result<T> = std::result::Result<T, PohRecorderError>;
 
-pub type WorkingBankEntry = (Arc<Bank>, (Entry, u64));
+#[derive(Clone, Debug)]
+pub struct WorkingBankEntry {
+    pub bank: Arc<Bank>,
+    // normal entries have len == 1, bundles have len > 1
+    pub entries_ticks: Vec<(Entry, u64)>,
+}
 
 #[derive(Clone)]
 pub struct BankStart {
@@ -80,21 +85,19 @@ impl BankStart {
 }
 
 pub struct Record {
-    pub mixin: Hash,
-    pub transactions: Vec<VersionedTransaction>,
+    // non-bundles shall have mixins_txs.len() == 1, bundles shall have mixins_txs.len() > 1
+    pub mixins_txs: Vec<(Hash, Vec<VersionedTransaction>)>,
     pub slot: Slot,
     pub sender: Sender<Result<()>>,
 }
 impl Record {
     pub fn new(
-        mixin: Hash,
-        transactions: Vec<VersionedTransaction>,
+        mixins_txs: Vec<(Hash, Vec<VersionedTransaction>)>,
         slot: Slot,
         sender: Sender<Result<()>>,
     ) -> Self {
         Self {
-            mixin,
-            transactions,
+            mixins_txs,
             slot,
             sender,
         }
@@ -125,14 +128,13 @@ impl TransactionRecorder {
     pub fn record(
         &self,
         bank_slot: Slot,
-        mixin: Hash,
-        transactions: Vec<VersionedTransaction>,
+        mixins_txs: Vec<(Hash, Vec<VersionedTransaction>)>,
     ) -> 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.
@@ -510,7 +512,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;
                 }
@@ -634,17 +639,24 @@ impl PohRecorder {
     pub fn record(
         &mut self,
         bank_slot: Slot,
-        mixin: Hash,
-        transactions: Vec<VersionedTransaction>,
+        mixins_txs: &[(Hash, Vec<VersionedTransaction>)],
     ) -> 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::this(|_| self.report_metrics(bank_slot), (), "report_metrics");
         self.report_metrics_us += report_metrics_time.as_us();
 
+        let mixins: Vec<Hash> = mixins_txs.iter().map(|(m, _)| *m).collect();
+        let transactions: Vec<Vec<VersionedTransaction>> =
+            mixins_txs.iter().map(|(_, tx)| tx.clone()).collect();
+
         loop {
             let (flush_cache_res, flush_cache_time) =
                 Measure::this(|_| self.flush_cache(false), (), "flush_cache");
@@ -664,21 +676,34 @@ impl PohRecorder {
             self.record_lock_contention_us += poh_lock_time.as_us();
 
             let (record_mixin_res, record_mixin_time) =
-                Measure::this(|_| poh_lock.record(mixin), (), "record_mixin");
+                Measure::this(|_| 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 {
+            if let Some(entries) = record_mixin_res {
+                assert_eq!(entries.len(), transactions.len());
                 let (send_entry_res, send_entry_time) = Measure::this(
                     |_| {
-                        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",
@@ -1046,13 +1071,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();
     }
@@ -1141,7 +1170,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());
@@ -1184,7 +1213,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)
             );
         }
@@ -1231,17 +1260,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();
@@ -1277,10 +1316,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());
             }
         }
@@ -1536,7 +1581,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 7ce52844bf..4301f23f49 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.lock().unwrap().record(
-                    record.slot,
-                    record.mixin,
-                    record.transactions,
-                ))
+                .send(
+                    poh_recorder
+                        .lock()
+                        .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::this(|_| 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.lock().unwrap().record(
-                                bank_slot,
-                                h1,
-                                vec![tx.clone()],
-                            );
+                            let _ = poh_recorder
+                                .lock()
+                                .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 2d2feb7b81..e6e696e98b 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.5",
+ "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.8",
  "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,39 +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.47",
+ "quote 1.0.21",
+ "regex",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-account"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "bs58 0.4.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "rustversion",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-constant"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "proc-macro2 1.0.47",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-error"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-event"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-interface"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "heck 0.3.3",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-program"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-attribute-state"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "anchor-derive-accounts"
+version = "0.24.2"
+dependencies = [
+ "anchor-syn",
+ "anyhow",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[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.1",
+ "bincode",
+ "borsh",
+ "bytemuck",
+ "solana-program 1.13.5",
+ "thiserror",
+]
+
+[[package]]
+name = "anchor-syn"
+version = "0.24.2"
+dependencies = [
+ "anyhow",
+ "bs58 0.3.1",
+ "heck 0.3.3",
+ "proc-macro2 1.0.47",
+ "proc-macro2-diagnostics",
+ "quote 1.0.21",
+ "serde",
+ "serde_json",
+ "sha2 0.9.9",
+ "syn 1.0.103",
+ "thiserror",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "ansi_term"
-version = "0.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.56"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
 
 [[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"
@@ -124,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"
@@ -147,7 +295,7 @@ dependencies = [
  "num-traits",
  "rusticata-macros",
  "thiserror",
- "time 0.3.7",
+ "time 0.3.16",
 ]
 
 [[package]]
@@ -156,9 +304,9 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "synstructure",
 ]
 
@@ -168,9 +316,9 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -181,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",
@@ -218,20 +366,20 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "async-trait"
-version = "0.1.52"
+version = "0.1.58"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
+checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -247,15 +395,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.0"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "axum"
-version = "0.5.3"
+version = "0.5.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f523b4e98ba6897ae90994bc18423d9877c54f9047b06a00ddc8122a957b1c70"
+checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43"
 dependencies = [
  "async-trait",
  "axum-core",
@@ -269,7 +417,7 @@ dependencies = [
  "matchit",
  "memchr",
  "mime",
- "percent-encoding 2.1.0",
+ "percent-encoding 2.2.0",
  "pin-project-lite",
  "serde",
  "sync_wrapper",
@@ -282,9 +430,9 @@ dependencies = [
 
 [[package]]
 name = "axum-core"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b"
+checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc"
 dependencies = [
  "async-trait",
  "bytes",
@@ -303,18 +451,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
 dependencies = [
  "futures-core",
- "getrandom 0.2.4",
+ "getrandom 0.2.8",
  "instant",
  "pin-project-lite",
- "rand 0.8.2",
+ "rand 0.8.5",
  "tokio",
 ]
 
 [[package]]
 name = "base-x"
-version = "0.2.8"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
+checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
 
 [[package]]
 name = "base64"
@@ -324,15 +472,15 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
 
 [[package]]
 name = "base64"
-version = "0.13.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "base64ct"
-version = "1.4.1"
+version = "1.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71acf5509fc522cce1b100ac0121c635129bfd4d91cdf036bcc9b9935f97ccf5"
+checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
 
 [[package]]
 name = "bincode"
@@ -355,8 +503,8 @@ dependencies = [
  "lazy_static",
  "lazycell",
  "peeking_take_while",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "regex",
  "rustc-hash",
  "shlex",
@@ -388,19 +536,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.3",
+ "digest 0.10.5",
 ]
 
 [[package]]
@@ -409,26 +545,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.5",
+ "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.5",
-]
-
-[[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]]
@@ -444,7 +571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa"
 dependencies = [
  "borsh-derive",
- "hashbrown",
+ "hashbrown 0.11.2",
 ]
 
 [[package]]
@@ -456,8 +583,8 @@ dependencies = [
  "borsh-derive-internal",
  "borsh-schema-derive-internal",
  "proc-macro-crate 0.1.5",
- "proc-macro2 1.0.36",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -466,9 +593,9 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -477,9 +604,9 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -503,6 +630,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"
@@ -520,9 +653,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.3.0"
+version = "3.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
 
 [[package]]
 name = "bv"
@@ -534,30 +667,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.8.0"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead"
+checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
 dependencies = [
  "bytemuck_derive",
 ]
 
 [[package]]
 name = "bytemuck_derive"
-version = "1.0.1"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54"
+checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -574,9 +701,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.1.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
 
 [[package]]
 name = "bzip2"
@@ -601,20 +728,19 @@ dependencies = [
 
 [[package]]
 name = "caps"
-version = "0.5.3"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61bf7211aad104ce2769ec05efcdfabf85ee84ac92461d142f22cf8badd0e54c"
+checksum = "938c50180feacea622ef3b8f4a496057c868dcf8ac7a64d781dd8f3f51a9c143"
 dependencies = [
- "errno",
  "libc",
  "thiserror",
 ]
 
 [[package]]
 name = "cc"
-version = "1.0.67"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 dependencies = [
  "jobserver",
 ]
@@ -642,23 +768,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
 dependencies = [
- "libc",
+ "iana-time-zone",
+ "js-sys",
  "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",
 ]
@@ -669,7 +797,7 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
 dependencies = [
- "generic-array 0.14.5",
+ "generic-array",
 ]
 
 [[package]]
@@ -684,9 +812,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",
@@ -695,28 +823,77 @@ 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",
  "bitflags",
- "strsim",
- "textwrap",
+ "strsim 0.8.0",
+ "textwrap 0.11.0",
  "unicode-width",
  "vec_map",
 ]
 
+[[package]]
+name = "clap"
+version = "3.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
+ "once_cell",
+ "strsim 0.10.0",
+ "termcolor",
+ "textwrap 0.16.0",
+]
+
+[[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.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[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 = "cmake"
-version = "0.1.46"
+version = "0.1.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7b858541263efe664aead4a5209a4ae5c5d2811167d4ed4ee0944503f8d2089"
+checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a"
 dependencies = [
  "cc",
 ]
 
+[[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]]
 name = "combine"
 version = "3.8.1"
@@ -732,14 +909,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",
@@ -819,27 +995,27 @@ 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]]
 name = "crossbeam-channel"
-version = "0.5.3"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdbfe11fe19ff083c48923cf179540e8cd0535903dc35e178a1fdeeb59aef51f"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-utils",
@@ -847,9 +1023,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",
@@ -871,12 +1047,11 @@ dependencies = [
 
 [[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]]
@@ -887,11 +1062,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.5",
+ "generic-array",
  "typenum",
 ]
 
@@ -901,7 +1076,7 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
 dependencies = [
- "generic-array 0.14.5",
+ "generic-array",
  "subtle",
 ]
 
@@ -928,6 +1103,50 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "cxx"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "scratch",
+ "syn 1.0.103",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
+dependencies = [
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
 [[package]]
 name = "dashmap"
 version = "4.0.2"
@@ -963,7 +1182,7 @@ dependencies = [
  "asn1-rs",
  "displaydoc",
  "nom",
- "num-bigint 0.4.2",
+ "num-bigint 0.4.3",
  "num-traits",
  "rusticata-macros",
 ]
@@ -981,48 +1200,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
 dependencies = [
  "convert_case",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustc_version 0.4.0",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "dialoguer"
-version = "0.10.0"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349d6b4fabcd9e97e1df1ae15395ac7e49fb144946a0d453959dc2696273b9da"
+checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
 dependencies = [
  "console",
  "tempfile",
  "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.3",
-]
-
 [[package]]
 name = "digest"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
 dependencies = [
- "generic-array 0.14.5",
+ "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",
 ]
@@ -1069,9 +1279,9 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1099,9 +1309,9 @@ dependencies = [
 
 [[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",
 ]
@@ -1129,26 +1339,26 @@ dependencies = [
  "derivation-path",
  "ed25519-dalek",
  "hmac 0.12.1",
- "sha2 0.10.2",
+ "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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "either"
-version = "1.6.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
 
 [[package]]
 name = "elf"
@@ -1167,11 +1377,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]]
@@ -1189,22 +1399,23 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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.2",
+ "num-bigint 0.4.3",
  "num-traits",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "rustc_version 0.4.0",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1214,9 +1425,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb"
 dependencies = [
  "once_cell",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -1270,15 +1481,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"
@@ -1291,22 +1496,22 @@ 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",
 ]
 
 [[package]]
 name = "fd-lock"
-version = "3.0.5"
+version = "3.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca"
+checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517"
 dependencies = [
  "cfg-if 1.0.0",
  "rustix",
- "windows-sys 0.30.0",
+ "windows-sys 0.36.1",
 ]
 
 [[package]]
@@ -1317,31 +1522,35 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
 
 [[package]]
 name = "filetime"
-version = "0.2.10"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695"
+checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
 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 0.42.0",
 ]
 
 [[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"
-version = "1.0.22"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
 dependencies = [
- "cfg-if 1.0.0",
  "crc32fast",
- "libc",
  "miniz_oxide",
 ]
 
@@ -1368,12 +1577,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]]
@@ -1390,9 +1598,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
 
 [[package]]
 name = "futures"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
+checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1405,9 +1613,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1415,15 +1623,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
+checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1433,38 +1641,38 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
+checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
 
 [[package]]
 name = "futures-task"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
 
 [[package]]
 name = "futures-util"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
 dependencies = [
  "futures 0.1.31",
  "futures-channel",
@@ -1490,18 +1698,9 @@ dependencies = [
 
 [[package]]
 name = "generic-array"
-version = "0.12.3"
+version = "0.14.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
-dependencies = [
- "typenum",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
 dependencies = [
  "serde",
  "typenum",
@@ -1520,11 +1719,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",
@@ -1532,13 +1732,15 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.4"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
 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",
 ]
 
 [[package]]
@@ -1549,9 +1751,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",
@@ -1567,7 +1769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38f3d68c8343245dc047982651b5afb8bd659c9959ed72efe5a73bf22684e5fd"
 dependencies = [
  "arc-swap",
- "futures 0.3.21",
+ "futures 0.3.25",
  "log",
  "reqwest",
  "serde",
@@ -1575,15 +1777,15 @@ dependencies = [
  "serde_json",
  "simpl",
  "smpl_jwt",
- "time 0.3.7",
+ "time 0.3.16",
  "tokio",
 ]
 
 [[package]]
 name = "goblin"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1800b95efee8ad4ef04517d4d69f8e209e763b1668f1179aeeedd0e454da55"
+checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926"
 dependencies = [
  "log",
  "plain",
@@ -1592,9 +1794,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.3.11"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e"
+checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
 dependencies = [
  "bytes",
  "fnv",
@@ -1605,7 +1807,7 @@ dependencies = [
  "indexmap",
  "slab",
  "tokio",
- "tokio-util 0.6.4",
+ "tokio-util 0.7.2",
  "tracing",
 ]
 
@@ -1615,32 +1817,41 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
 dependencies = [
- "byteorder 1.4.3",
+ "byteorder 1.4.3",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.11.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 dependencies = [
  "ahash",
 ]
 
 [[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",
+ "base64 0.13.1",
  "bitflags",
  "bytes",
  "headers-core",
  "http",
  "httpdate",
  "mime",
- "sha-1 0.10.0",
+ "sha1 0.10.5",
 ]
 
 [[package]]
@@ -1669,9 +1880,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",
 ]
@@ -1698,7 +1909,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]]
@@ -1708,7 +1919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1"
 dependencies = [
  "digest 0.9.0",
- "generic-array 0.14.5",
+ "generic-array",
  "hmac 0.8.1",
 ]
 
@@ -1742,27 +1953,27 @@ 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"
-version = "0.14.18"
+version = "0.14.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
+checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -1789,7 +2000,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc"
 dependencies = [
  "bytes",
- "futures 0.3.21",
+ "futures 0.3.25",
  "headers",
  "http",
  "hyper",
@@ -1808,9 +2019,9 @@ checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
 dependencies = [
  "http",
  "hyper",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "tokio",
- "tokio-rustls 0.23.2",
+ "tokio-rustls 0.23.4",
 ]
 
 [[package]]
@@ -1838,6 +2049,30 @@ dependencies = [
  "tokio-native-tls",
 ]
 
+[[package]]
+name = "iana-time-zone"
+version = "0.1.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
 [[package]]
 name = "idna"
 version = "0.1.5"
@@ -1851,11 +2086,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",
 ]
@@ -1873,7 +2107,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",
@@ -1890,12 +2124,12 @@ checksum = "5a9d968042a4902e08810946fc7cd5851eb75e80301342305af755ca06cb82ce"
 
 [[package]]
 name = "indexmap"
-version = "1.8.1"
+version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
 dependencies = [
  "autocfg",
- "hashbrown",
+ "hashbrown 0.12.3",
  "rayon",
 ]
 
@@ -1913,63 +2147,76 @@ 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.5",
+ "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",
 ]
 
 [[package]]
 name = "io-lifetimes"
-version = "0.6.1"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504"
+checksum = "e6e481ccbe3dea62107216d0d1138bb8ad8e5e5c43009a098bd1990272c497b0"
 
 [[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"
-version = "0.10.3"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
 dependencies = [
  "either",
 ]
 
 [[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.13.5"
+dependencies = [
+ "bytes",
+ "crossbeam-epoch",
+ "lock_api",
+ "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.55"
+version = "0.3.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1992,7 +2239,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a"
 dependencies = [
  "derive_more",
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-core",
  "jsonrpc-pubsub",
  "jsonrpc-server-utils",
@@ -2010,7 +2257,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "futures-executor",
  "futures-util",
  "log",
@@ -2025,7 +2272,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-client-transports",
 ]
 
@@ -2036,9 +2283,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2"
 dependencies = [
  "proc-macro-crate 0.1.5",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2047,7 +2294,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "hyper",
  "jsonrpc-core",
  "jsonrpc-server-utils",
@@ -2063,7 +2310,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "382bb0206323ca7cda3dcd7e245cea86d37d02457a02a975e3378fb149a48845"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-core",
  "jsonrpc-server-utils",
  "log",
@@ -2078,7 +2325,7 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "jsonrpc-core",
  "lazy_static",
  "log",
@@ -2094,22 +2341,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4"
 dependencies = [
  "bytes",
- "futures 0.3.21",
+ "futures 0.3.25",
  "globset",
  "jsonrpc-core",
  "lazy_static",
  "log",
  "tokio",
  "tokio-stream",
- "tokio-util 0.6.4",
+ "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"
@@ -2135,9 +2382,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.120"
+version = "0.2.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
 
 [[package]]
 name = "libloading"
@@ -2151,9 +2398,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"
@@ -2219,26 +2466,35 @@ 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"
-version = "0.0.42"
+version = "0.0.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7"
+checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
 
 [[package]]
 name = "lock_api"
@@ -2251,20 +2507,20 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 dependencies = [
  "cfg-if 1.0.0",
 ]
 
 [[package]]
 name = "lru"
-version = "0.7.5"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
+checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.12.3",
 ]
 
 [[package]]
@@ -2287,12 +2543,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"
@@ -2307,24 +2557,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",
 ]
@@ -2337,7 +2587,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
 dependencies = [
  "byteorder 1.4.3",
  "keccak",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
  "zeroize",
 ]
 
@@ -2361,12 +2611,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.4.4"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
 dependencies = [
  "adler",
- "autocfg",
 ]
 
 [[package]]
@@ -2407,9 +2656,9 @@ version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2438,9 +2687,9 @@ dependencies = [
 
 [[package]]
 name = "net2"
-version = "0.2.37"
+version = "0.2.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -2472,9 +2721,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",
 ]
@@ -2506,9 +2755,9 @@ dependencies = [
 
 [[package]]
 name = "num-bigint"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
 dependencies = [
  "autocfg",
  "num-integer",
@@ -2527,20 +2776,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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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",
@@ -2571,9 +2820,9 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
  "autocfg",
 ]
@@ -2603,17 +2852,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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro-crate 1.2.1",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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",
 ]
@@ -2635,15 +2884,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
-
-[[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"
@@ -2653,9 +2896,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
 name = "openssl"
-version = "0.10.40"
+version = "0.10.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e"
+checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
 dependencies = [
  "bitflags",
  "cfg-if 1.0.0",
@@ -2672,9 +2915,9 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2694,9 +2937,9 @@ dependencies = [
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.74"
+version = "0.9.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
+checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
 dependencies = [
  "autocfg",
  "cc",
@@ -2714,15 +2957,21 @@ checksum = "e1cf9b1c4e9a6c4de793c632496fa490bdc0e1eea73f0c91394f7b6990935d22"
 dependencies = [
  "async-trait",
  "crossbeam-channel",
- "futures 0.3.21",
+ "futures 0.3.25",
  "js-sys",
  "lazy_static",
- "percent-encoding 2.1.0",
+ "percent-encoding 2.2.0",
  "pin-project",
- "rand 0.8.2",
+ "rand 0.8.5",
  "thiserror",
 ]
 
+[[package]]
+name = "os_str_bytes"
+version = "6.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
+
 [[package]]
 name = "ouroboros"
 version = "0.14.2"
@@ -2742,9 +2991,9 @@ checksum = "ed9a247206016d424fe8497bc611e510887af5c261fbbf977877c4bb55ca4d82"
 dependencies = [
  "Inflector",
  "proc-macro-error",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -2753,7 +3002,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6"
 dependencies = [
- "futures 0.3.21",
+ "futures 0.3.25",
  "libc",
  "log",
  "rand 0.7.3",
@@ -2774,12 +3023,12 @@ dependencies = [
 
 [[package]]
 name = "parking_lot"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
  "lock_api",
- "parking_lot_core 0.9.1",
+ "parking_lot_core 0.9.4",
 ]
 
 [[package]]
@@ -2791,22 +3040,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.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
+checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "redox_syscall 0.2.10",
+ "redox_syscall",
  "smallvec",
- "windows-sys 0.32.0",
+ "windows-sys 0.42.0",
 ]
 
 [[package]]
@@ -2824,7 +3073,16 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7"
 dependencies = [
- "digest 0.10.3",
+ "digest 0.10.5",
+]
+
+[[package]]
+name = "pbkdf2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
+dependencies = [
+ "digest 0.10.5",
 ]
 
 [[package]]
@@ -2835,11 +3093,11 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 
 [[package]]
 name = "pem"
-version = "1.0.2"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947"
+checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
 ]
 
 [[package]]
@@ -2850,9 +3108,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"
@@ -2865,18 +3123,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",
@@ -2884,63 +3143,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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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 0.10.5",
 ]
 
 [[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",
 ]
 
 [[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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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"
@@ -2961,9 +3230,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"
@@ -2979,24 +3248,24 @@ 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 = "prettyplease"
-version = "0.1.9"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18"
+checksum = "c142c0e46b57171fe0c528bee8c5b7569e80f0c17e377cd0e30ea57dbc11bb51"
 dependencies = [
- "proc-macro2 1.0.36",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -3010,10 +3279,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",
 ]
@@ -3025,9 +3295,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 dependencies = [
  "proc-macro-error-attr",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "version_check",
 ]
 
@@ -3037,8 +3307,8 @@ version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "version_check",
 ]
 
@@ -3059,11 +3329,34 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+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.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+ "version_check",
+ "yansi",
+]
+
+[[package]]
+name = "prost"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020"
 dependencies = [
- "unicode-xid 0.2.0",
+ "bytes",
+ "prost-derive 0.8.0",
 ]
 
 [[package]]
@@ -3078,14 +3371,32 @@ dependencies = [
 
 [[package]]
 name = "prost"
-version = "0.10.1"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a07b0857a71a8cb765763950499cae2413c3f9cede1133478c43600d9e146890"
+checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e"
 dependencies = [
  "bytes",
  "prost-derive 0.10.1",
 ]
 
+[[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"
@@ -3098,7 +3409,7 @@ dependencies = [
  "lazy_static",
  "log",
  "multimap",
- "petgraph",
+ "petgraph 0.6.2",
  "prost 0.9.0",
  "prost-types 0.9.0",
  "regex",
@@ -3108,9 +3419,9 @@ dependencies = [
 
 [[package]]
 name = "prost-build"
-version = "0.10.1"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120fbe7988713f39d780a58cf1a7ef0d7ef66c6d87e5aa3438940c05357929f4"
+checksum = "8ae5a4388762d5815a9fc0dea33c56b021cdc8dde0c55e0c9ca57197254b0cab"
 dependencies = [
  "bytes",
  "cfg-if 1.0.0",
@@ -3120,14 +3431,27 @@ dependencies = [
  "lazy_static",
  "log",
  "multimap",
- "petgraph",
- "prost 0.10.1",
+ "petgraph 0.6.2",
+ "prost 0.10.4",
  "prost-types 0.10.1",
  "regex",
  "tempfile",
  "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.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
 [[package]]
 name = "prost-derive"
 version = "0.9.0"
@@ -3136,9 +3460,9 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
 dependencies = [
  "anyhow",
  "itertools",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -3149,9 +3473,19 @@ checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc"
 dependencies = [
  "anyhow",
  "itertools",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
+[[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]]
@@ -3171,7 +3505,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68"
 dependencies = [
  "bytes",
- "prost 0.10.1",
+ "prost 0.10.4",
 ]
 
 [[package]]
@@ -3180,14 +3514,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.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7542006acd6e057ff632307d219954c44048f818898da03113d6c0086bfddd9"
+checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -3195,7 +3529,7 @@ dependencies = [
  "fxhash",
  "quinn-proto",
  "quinn-udp",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "thiserror",
  "tokio",
  "tracing",
@@ -3204,16 +3538,16 @@ dependencies = [
 
 [[package]]
 name = "quinn-proto"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a13a5c0a674c1ce7150c9df7bc4a1e46c2fbbe7c710f56c0dc78b1a810e779e"
+checksum = "3fce546b9688f767a57530652488420d419a8b1f44a478b451c3d1ab6d992a55"
 dependencies = [
  "bytes",
  "fxhash",
- "rand 0.8.2",
+ "rand 0.8.5",
  "ring",
- "rustls 0.20.4",
- "rustls-native-certs",
+ "rustls 0.20.7",
+ "rustls-native-certs 0.6.2",
  "rustls-pemfile 0.2.1",
  "slab",
  "thiserror",
@@ -3224,13 +3558,12 @@ dependencies = [
 
 [[package]]
 name = "quinn-udp"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df185e5e5f7611fa6e628ed8f9633df10114b03bbaecab186ec55822c44ac727"
+checksum = "9f832d8958db3e84d2ec93b5eb2272b45aa23cf7f8fe6e79f578896f4e6c231b"
 dependencies = [
  "futures-util",
  "libc",
- "mio",
  "quinn-proto",
  "socket2",
  "tokio",
@@ -3248,11 +3581,11 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.6"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
 dependencies = [
- "proc-macro2 1.0.36",
+ "proc-macro2 1.0.47",
 ]
 
 [[package]]
@@ -3261,24 +3594,23 @@ 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",
- "rand_hc 0.2.0",
+ "rand_hc",
  "rand_pcg",
 ]
 
 [[package]]
 name = "rand"
-version = "0.8.2"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
- "rand_chacha 0.3.0",
- "rand_core 0.6.3",
- "rand_hc 0.3.0",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -3293,12 +3625,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]]
@@ -3307,16 +3639,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.8",
 ]
 
 [[package]]
@@ -3328,15 +3660,6 @@ dependencies = [
  "rand_core 0.5.1",
 ]
 
-[[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core 0.6.3",
-]
-
 [[package]]
 name = "rand_pcg"
 version = "0.2.1"
@@ -3352,14 +3675,14 @@ 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]]
 name = "rayon"
-version = "1.5.1"
+version = "1.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
 dependencies = [
  "autocfg",
  "crossbeam-deque",
@@ -3369,73 +3692,67 @@ dependencies = [
 
 [[package]]
 name = "rayon-core"
-version = "1.9.1"
+version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
  "crossbeam-utils",
- "lazy_static",
  "num_cpus",
 ]
 
 [[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.7",
+ "time 0.3.16",
  "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.8",
+ "redox_syscall",
+ "thiserror",
 ]
 
 [[package]]
 name = "reed-solomon-erasure"
-version = "5.0.1"
+version = "5.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170bac0d8306941e101df0caaa6518b10bc4232dd36c34f1cb78b8a063024db"
+checksum = "c2fe31452b684b8b33f65f8730c8b8812c3f5a0bb8a096934717edb1ac488641"
 dependencies = [
  "cc",
  "libc",
  "libm",
  "parking_lot 0.11.2",
  "smallvec",
- "spin 0.9.3",
+ "spin 0.9.4",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.5"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -3444,27 +3761,27 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+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",
 ]
 
 [[package]]
 name = "reqwest"
-version = "0.11.10"
+version = "0.11.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
+checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
 dependencies = [
  "async-compression",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -3477,34 +3794,35 @@ dependencies = [
  "hyper-tls",
  "ipnet",
  "js-sys",
- "lazy_static",
  "log",
  "mime",
  "native-tls",
- "percent-encoding 2.1.0",
+ "once_cell",
+ "percent-encoding 2.2.0",
  "pin-project-lite",
- "rustls 0.20.4",
- "rustls-pemfile 0.3.0",
+ "rustls 0.20.7",
+ "rustls-pemfile 1.0.1",
  "serde",
  "serde_json",
  "serde_urlencoded",
  "tokio",
  "tokio-native-tls",
- "tokio-rustls 0.23.2",
- "tokio-util 0.6.4",
- "url 2.2.2",
+ "tokio-rustls 0.23.4",
+ "tokio-util 0.7.2",
+ "tower-service",
+ "url 2.3.1",
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "webpki-roots",
+ "webpki-roots 0.22.5",
  "winreg",
 ]
 
 [[package]]
 name = "retain_mut"
-version = "0.1.7"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086"
+checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
 
 [[package]]
 name = "ring"
@@ -3545,9 +3863,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"
@@ -3570,7 +3888,7 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
 dependencies = [
- "semver 1.0.6",
+ "semver 1.0.14",
 ]
 
 [[package]]
@@ -3584,16 +3902,16 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.34.4"
+version = "0.35.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f5d1c6ed6d1c6915aa64749b809fc1bafff49d160f5d927463658093d7d62ab"
+checksum = "985947f9b6423159c4726323f373be0a21bdb514c5af06a849cb3d2dce2d01e8"
 dependencies = [
  "bitflags",
  "errno",
  "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "winapi 0.3.9",
+ "windows-sys 0.36.1",
 ]
 
 [[package]]
@@ -3602,7 +3920,7 @@ version = "0.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "log",
  "ring",
  "sct 0.6.1",
@@ -3611,9 +3929,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.20.4"
+version = "0.20.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921"
+checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
 dependencies = [
  "log",
  "ring",
@@ -3623,12 +3941,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",
 ]
@@ -3639,29 +3969,29 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
 ]
 
 [[package]]
 name = "rustls-pemfile"
-version = "0.3.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
+checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
 ]
 
 [[package]]
 name = "rustversion"
-version = "1.0.6"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
+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"
@@ -3674,12 +4004,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 0.36.1",
 ]
 
 [[package]]
@@ -3688,24 +4018,30 @@ 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.10.1"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"
+checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
 dependencies = [
  "scroll_derive",
 ]
 
 [[package]]
 name = "scroll_derive"
-version = "0.10.2"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9"
+checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -3730,9 +4066,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",
@@ -3762,9 +4098,9 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.6"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d"
+checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
 
 [[package]]
 name = "semver-parser"
@@ -3774,38 +4110,38 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
 [[package]]
 name = "serde"
-version = "1.0.136"
+version = "1.0.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_bytes"
-version = "0.11.5"
+version = "0.11.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9"
+checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.79"
+version = "1.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
 dependencies = [
  "itoa",
  "ryu",
@@ -3826,26 +4162,14 @@ dependencies = [
 
 [[package]]
 name = "serde_yaml"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
-dependencies = [
- "indexmap",
- "ryu",
- "serde",
- "yaml-rust",
-]
-
-[[package]]
-name = "sha-1"
-version = "0.8.2"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
 dependencies = [
- "block-buffer 0.7.3",
- "digest 0.8.1",
- "fake-simd",
- "opaque-debug 0.2.3",
+ "indexmap",
+ "ryu",
+ "serde",
+ "yaml-rust",
 ]
 
 [[package]]
@@ -3858,7 +4182,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures",
  "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -3869,7 +4193,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
 dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures",
- "digest 0.10.3",
+ "digest 0.10.5",
 ]
 
 [[package]]
@@ -3881,6 +4205,17 @@ dependencies = [
  "sha1_smol",
 ]
 
+[[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]]
 name = "sha1_smol"
 version = "1.0.0"
@@ -3897,18 +4232,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.2"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
+checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
 dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures",
- "digest 0.10.3",
+ "digest 0.10.5",
 ]
 
 [[package]]
@@ -3920,24 +4255,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.1"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86"
+checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9"
 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",
 ]
@@ -3950,9 +4285,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
 
 [[package]]
 name = "signal-hook"
-version = "0.3.13"
+version = "0.3.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
+checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
 dependencies = [
  "libc",
  "signal-hook-registry",
@@ -3969,9 +4304,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"
@@ -3991,15 +4326,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"
@@ -4007,7 +4345,7 @@ version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4370044f8b20f944e05c35d77edd3518e6f21fc4de77e593919f287c6a3f428a"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "log",
  "openssl",
  "serde",
@@ -4019,9 +4357,9 @@ dependencies = [
 
 [[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",
@@ -4033,12 +4371,12 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
- "futures 0.3.21",
+ "futures 0.3.25",
  "httparse",
  "log",
- "rand 0.8.2",
+ "rand 0.8.5",
  "sha-1 0.9.8",
 ]
 
@@ -4047,9 +4385,9 @@ name = "solana-account-decoder"
 version = "1.13.5"
 dependencies = [
  "Inflector",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "bv",
  "lazy_static",
  "serde",
@@ -4088,7 +4426,7 @@ name = "solana-banks-client"
 version = "1.13.5"
 dependencies = [
  "borsh",
- "futures 0.3.21",
+ "futures 0.3.25",
  "solana-banks-interface",
  "solana-program 1.13.5",
  "solana-sdk 1.13.5",
@@ -4113,9 +4451,10 @@ version = "1.13.5"
 dependencies = [
  "bincode",
  "crossbeam-channel",
- "futures 0.3.21",
+ "futures 0.3.25",
  "solana-banks-interface",
  "solana-client",
+ "solana-gossip",
  "solana-runtime",
  "solana-sdk 1.13.5",
  "solana-send-transaction-service",
@@ -4408,7 +4747,7 @@ dependencies = [
 name = "solana-bpf-rust-rand"
 version = "1.13.5"
 dependencies = [
- "getrandom 0.1.14",
+ "getrandom 0.1.16",
  "rand 0.7.3",
  "solana-program 1.13.5",
 ]
@@ -4556,7 +4895,7 @@ name = "solana-clap-utils"
 version = "1.13.5"
 dependencies = [
  "chrono",
- "clap",
+ "clap 2.34.0",
  "rpassword",
  "solana-perf",
  "solana-remote-wallet",
@@ -4564,7 +4903,7 @@ dependencies = [
  "thiserror",
  "tiny-bip39",
  "uriparse",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -4578,7 +4917,7 @@ dependencies = [
  "serde_yaml",
  "solana-clap-utils",
  "solana-sdk 1.13.5",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -4586,13 +4925,13 @@ name = "solana-cli-output"
 version = "1.13.5"
 dependencies = [
  "Inflector",
- "base64 0.13.0",
+ "base64 0.13.1",
  "chrono",
- "clap",
+ "clap 2.34.0",
  "console",
  "humantime",
  "indicatif",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_json",
  "solana-account-decoder",
@@ -4611,14 +4950,14 @@ version = "1.13.5"
 dependencies = [
  "async-mutex",
  "async-trait",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "bytes",
- "clap",
+ "clap 2.34.0",
  "crossbeam-channel",
  "enum_dispatch",
- "futures 0.3.21",
+ "futures 0.3.25",
  "futures-util",
  "indexmap",
  "indicatif",
@@ -4633,8 +4972,8 @@ dependencies = [
  "rand_chacha 0.2.2",
  "rayon",
  "reqwest",
- "rustls 0.20.4",
- "semver 1.0.6",
+ "rustls 0.20.7",
+ "semver 1.0.14",
  "serde",
  "serde_derive",
  "serde_json",
@@ -4655,7 +4994,7 @@ dependencies = [
  "tokio-stream",
  "tokio-tungstenite",
  "tungstenite",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -4683,19 +5022,30 @@ name = "solana-core"
 version = "1.13.5"
 dependencies = [
  "ahash",
- "base64 0.13.0",
+ "anchor-lang",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
+ "bytes",
  "chrono",
+ "clap 3.2.23",
  "crossbeam-channel",
  "dashmap",
  "etcd-client",
  "fs_extra",
+ "futures 0.3.25",
+ "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",
@@ -4732,8 +5082,14 @@ dependencies = [
  "sysctl",
  "tempfile",
  "thiserror",
+ "tip-distribution",
+ "tip-payment",
  "tokio",
+ "tokio-stream",
+ "tonic 0.5.2",
+ "tonic-build 0.5.2",
  "trees",
+ "uuid",
 ]
 
 [[package]]
@@ -4774,7 +5130,7 @@ version = "1.13.5"
 dependencies = [
  "bincode",
  "byteorder 1.4.3",
- "clap",
+ "clap 2.34.0",
  "crossbeam-channel",
  "log",
  "serde",
@@ -4792,13 +5148,11 @@ dependencies = [
 
 [[package]]
 name = "solana-frozen-abi"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49a5d3280421bb53fc12bdba1eaa505153fb4f99a06b5609dae22192652ead3b"
+version = "1.13.5"
 dependencies = [
- "bs58",
+ "bs58 0.4.0",
  "bv",
- "generic-array 0.14.5",
+ "generic-array",
  "im",
  "lazy_static",
  "log",
@@ -4807,51 +5161,65 @@ dependencies = [
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "solana-frozen-abi-macro 1.10.33",
+ "sha2 0.10.6",
+ "solana-frozen-abi-macro 1.13.5",
  "thiserror",
 ]
 
 [[package]]
 name = "solana-frozen-abi"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fae9453c906a52b00c6d668508214377163cf59776cfa8e09ef740a2a493f87d"
 dependencies = [
- "bs58",
+ "ahash",
+ "blake3",
+ "block-buffer 0.9.0",
+ "bs58 0.4.0",
  "bv",
- "generic-array 0.14.5",
+ "byteorder 1.4.3",
+ "cc",
+ "either",
+ "generic-array",
+ "getrandom 0.1.16",
+ "hashbrown 0.12.3",
  "im",
  "lazy_static",
  "log",
  "memmap2",
+ "once_cell",
+ "rand_core 0.6.4",
  "rustc_version 0.4.0",
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "solana-frozen-abi-macro 1.13.5",
+ "serde_json",
+ "sha2 0.10.6",
+ "solana-frozen-abi-macro 1.14.6",
+ "subtle",
  "thiserror",
 ]
 
 [[package]]
 name = "solana-frozen-abi-macro"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "635c60ac96b1347af272c625465068b908aff919d19f29b5795a44310310494d"
+version = "1.13.5"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustc_version 0.4.0",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "solana-frozen-abi-macro"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e29cd0fef92aee046267cdd69d8aa8b31cd3549991ea6f2c6076b21064e235fb"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustc_version 0.4.0",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -4877,7 +5245,7 @@ dependencies = [
 name = "solana-geyser-plugin-manager"
 version = "1.13.5"
 dependencies = [
- "bs58",
+ "bs58 0.4.0",
  "crossbeam-channel",
  "json5",
  "libloading",
@@ -4899,7 +5267,7 @@ version = "1.13.5"
 dependencies = [
  "bincode",
  "bv",
- "clap",
+ "clap 2.34.0",
  "crossbeam-channel",
  "flate2",
  "indexmap",
@@ -4947,7 +5315,7 @@ dependencies = [
  "chrono-humanize",
  "crossbeam-channel",
  "fs_extra",
- "futures 0.3.21",
+ "futures 0.3.25",
  "itertools",
  "lazy_static",
  "libc",
@@ -4955,7 +5323,7 @@ dependencies = [
  "lru",
  "num_cpus",
  "num_enum",
- "prost 0.10.1",
+ "prost 0.10.4",
  "rand 0.7.3",
  "rand_chacha 0.2.2",
  "rayon",
@@ -4964,7 +5332,7 @@ dependencies = [
  "rustc_version 0.4.0",
  "serde",
  "serde_bytes",
- "sha2 0.10.2",
+ "sha2 0.10.6",
  "solana-account-decoder",
  "solana-bpf-loader-program",
  "solana-entry",
@@ -4992,9 +5360,7 @@ dependencies = [
 
 [[package]]
 name = "solana-logger"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b12cb6e6f1f9c9876d356c928b8c2ac532f6715e7cd2a1b4343d747bee3eca73"
+version = "1.13.5"
 dependencies = [
  "env_logger",
  "lazy_static",
@@ -5003,7 +5369,9 @@ dependencies = [
 
 [[package]]
 name = "solana-logger"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d255b73f0c0e1eaa34280094f1304a783ea9394dbf2f8b749a3661e948ca5551"
 dependencies = [
  "env_logger",
  "lazy_static",
@@ -5044,7 +5412,7 @@ name = "solana-net-utils"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "clap",
+ "clap 2.34.0",
  "crossbeam-channel",
  "log",
  "nix",
@@ -5056,7 +5424,7 @@ dependencies = [
  "solana-sdk 1.13.5",
  "solana-version",
  "tokio",
- "url 2.2.2",
+ "url 2.3.1",
 ]
 
 [[package]]
@@ -5103,23 +5471,21 @@ dependencies = [
 
 [[package]]
 name = "solana-program"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeecf504cee2821b006871f70e7a1f54db15f914cedf259eaf5976fe606470f0"
+version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bitflags",
  "blake3",
  "borsh",
  "borsh-derive",
- "bs58",
+ "bs58 0.4.0",
  "bv",
  "bytemuck",
  "console_error_panic_hook",
  "console_log",
  "curve25519-dalek",
- "getrandom 0.1.14",
+ "getrandom 0.1.16",
  "itertools",
  "js-sys",
  "lazy_static",
@@ -5127,67 +5493,76 @@ dependencies = [
  "log",
  "num-derive",
  "num-traits",
- "parking_lot 0.12.0",
+ "parking_lot 0.12.1",
  "rand 0.7.3",
  "rustc_version 0.4.0",
  "rustversion",
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.10.33",
- "solana-frozen-abi-macro 1.10.33",
- "solana-sdk-macro 1.10.33",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.13.5",
+ "solana-frozen-abi-macro 1.13.5",
+ "solana-sdk-macro 1.13.5",
  "thiserror",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "solana-program"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0eeda17e271e11864d48b35eeaefed9591305b4d47266caf3f8b11b9271833d"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bitflags",
  "blake3",
  "borsh",
  "borsh-derive",
- "bs58",
+ "bs58 0.4.0",
  "bv",
  "bytemuck",
+ "cc",
  "console_error_panic_hook",
  "console_log",
  "curve25519-dalek",
- "getrandom 0.1.14",
+ "getrandom 0.2.8",
  "itertools",
  "js-sys",
  "lazy_static",
+ "libc",
  "libsecp256k1",
  "log",
+ "memoffset",
  "num-derive",
  "num-traits",
- "parking_lot 0.12.0",
+ "parking_lot 0.12.1",
  "rand 0.7.3",
+ "rand_chacha 0.2.2",
  "rustc_version 0.4.0",
  "rustversion",
  "serde",
  "serde_bytes",
  "serde_derive",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.13.5",
- "solana-frozen-abi-macro 1.13.5",
- "solana-sdk-macro 1.13.5",
+ "serde_json",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.14.6",
+ "solana-frozen-abi-macro 1.14.6",
+ "solana-sdk-macro 1.14.6",
  "thiserror",
+ "tiny-bip39",
  "wasm-bindgen",
+ "zeroize",
 ]
 
 [[package]]
 name = "solana-program-runtime"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "enum-iterator",
  "itertools",
@@ -5210,7 +5585,7 @@ name = "solana-program-test"
 version = "1.13.5"
 dependencies = [
  "async-trait",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "chrono-humanize",
  "log",
@@ -5244,9 +5619,9 @@ dependencies = [
  "log",
  "num-derive",
  "num-traits",
- "parking_lot 0.12.0",
+ "parking_lot 0.12.1",
  "qstring",
- "semver 1.0.6",
+ "semver 1.0.14",
  "solana-sdk 1.13.5",
  "thiserror",
  "uriparse",
@@ -5259,22 +5634,22 @@ dependencies = [
  "crossbeam-channel",
  "futures-util",
  "log",
- "prost 0.10.1",
+ "prost 0.10.4",
  "solana-rpc",
  "solana-runtime",
  "solana-sdk 1.13.5",
  "tokio",
- "tonic 0.7.1",
- "tonic-build 0.7.0",
+ "tonic 0.7.2",
+ "tonic-build 0.7.2",
 ]
 
 [[package]]
 name = "solana-rpc"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
- "bs58",
+ "bs58 0.4.0",
  "crossbeam-channel",
  "dashmap",
  "itertools",
@@ -5315,7 +5690,7 @@ dependencies = [
  "stream-cancel",
  "thiserror",
  "tokio",
- "tokio-util 0.6.4",
+ "tokio-util 0.6.10",
 ]
 
 [[package]]
@@ -5377,24 +5752,23 @@ dependencies = [
 
 [[package]]
 name = "solana-sdk"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "636f6c615aca6f75e22b6baceaf0ffed9d74367f9320b07ed57cd9b5ce2e4ff9"
+version = "1.13.5"
 dependencies = [
+ "anchor-lang",
  "assert_matches",
- "base64 0.13.0",
+ "base64 0.13.1",
  "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.5",
+ "generic-array",
  "hmac 0.12.1",
  "itertools",
  "js-sys",
@@ -5414,36 +5788,39 @@ dependencies = [
  "serde_bytes",
  "serde_derive",
  "serde_json",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.10.33",
- "solana-frozen-abi-macro 1.10.33",
- "solana-logger 1.10.33",
- "solana-program 1.10.33",
- "solana-sdk-macro 1.10.33",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.13.5",
+ "solana-frozen-abi-macro 1.13.5",
+ "solana-logger 1.13.5",
+ "solana-program 1.13.5",
+ "solana-sdk-macro 1.13.5",
  "thiserror",
  "uriparse",
+ "uuid",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "solana-sdk"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57fe62f7fe938607437ed29b0e95b4cffc7db186bf04fea6a98d92319da941b1"
 dependencies = [
  "assert_matches",
- "base64 0.13.0",
+ "base64 0.13.1",
  "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.5",
+ "generic-array",
  "hmac 0.12.1",
  "itertools",
  "js-sys",
@@ -5453,7 +5830,7 @@ dependencies = [
  "memmap2",
  "num-derive",
  "num-traits",
- "pbkdf2 0.10.1",
+ "pbkdf2 0.11.0",
  "qstring",
  "rand 0.7.3",
  "rand_chacha 0.2.2",
@@ -5463,13 +5840,13 @@ dependencies = [
  "serde_bytes",
  "serde_derive",
  "serde_json",
- "sha2 0.10.2",
- "sha3 0.10.1",
- "solana-frozen-abi 1.13.5",
- "solana-frozen-abi-macro 1.13.5",
- "solana-logger 1.13.5",
- "solana-program 1.13.5",
- "solana-sdk-macro 1.13.5",
+ "sha2 0.10.6",
+ "sha3 0.10.6",
+ "solana-frozen-abi 1.14.6",
+ "solana-frozen-abi-macro 1.14.6",
+ "solana-logger 1.14.6",
+ "solana-program 1.14.6",
+ "solana-sdk-macro 1.14.6",
  "thiserror",
  "uriparse",
  "wasm-bindgen",
@@ -5477,26 +5854,26 @@ dependencies = [
 
 [[package]]
 name = "solana-sdk-macro"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b8bcac4394644f21dc013e932a7df9f536fcecef3e5df43fe362b4ec532ce30"
+version = "1.13.5"
 dependencies = [
- "bs58",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "bs58 0.4.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "solana-sdk-macro"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f9abf0bdba679d93c5624043ae3dcbf529ed2251c281cc615fa55c8dc2f0bd"
 dependencies = [
- "bs58",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "bs58 0.4.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -5506,6 +5883,7 @@ dependencies = [
  "crossbeam-channel",
  "log",
  "solana-client",
+ "solana-gossip",
  "solana-measure",
  "solana-metrics",
  "solana-runtime",
@@ -5543,14 +5921,14 @@ dependencies = [
  "bzip2",
  "enum-iterator",
  "flate2",
- "futures 0.3.21",
+ "futures 0.3.25",
  "goauth",
  "http",
  "hyper",
  "hyper-proxy",
  "log",
  "openssl",
- "prost 0.10.1",
+ "prost 0.10.4",
  "prost-types 0.10.1",
  "serde",
  "serde_derive",
@@ -5561,7 +5939,7 @@ dependencies = [
  "solana-transaction-status",
  "thiserror",
  "tokio",
- "tonic 0.7.1",
+ "tonic 0.7.2",
  "zstd",
 ]
 
@@ -5570,13 +5948,13 @@ name = "solana-storage-proto"
 version = "1.13.5"
 dependencies = [
  "bincode",
- "bs58",
- "prost 0.10.1",
+ "bs58 0.4.0",
+ "prost 0.10.4",
  "serde",
  "solana-account-decoder",
  "solana-sdk 1.13.5",
  "solana-transaction-status",
- "tonic-build 0.7.0",
+ "tonic-build 0.7.2",
 ]
 
 [[package]]
@@ -5597,7 +5975,7 @@ dependencies = [
  "quinn",
  "rand 0.7.3",
  "rcgen",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "solana-metrics",
  "solana-perf",
  "solana-sdk 1.13.5",
@@ -5610,7 +5988,7 @@ dependencies = [
 name = "solana-sys-tuner"
 version = "1.13.5"
 dependencies = [
- "clap",
+ "clap 2.34.0",
  "libc",
  "log",
  "nix",
@@ -5625,7 +6003,7 @@ dependencies = [
 name = "solana-test-validator"
 version = "1.13.5"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "log",
  "serde_derive",
  "serde_json",
@@ -5649,10 +6027,10 @@ name = "solana-transaction-status"
 version = "1.13.5"
 dependencies = [
  "Inflector",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "borsh",
- "bs58",
+ "bs58 0.4.0",
  "lazy_static",
  "log",
  "serde",
@@ -5675,7 +6053,7 @@ name = "solana-validator"
 version = "1.13.5"
 dependencies = [
  "chrono",
- "clap",
+ "clap 2.34.0",
  "console",
  "core_affinity",
  "crossbeam-channel",
@@ -5720,6 +6098,7 @@ dependencies = [
  "solana-vote-program",
  "symlink",
  "tikv-jemallocator",
+ "tonic 0.5.2",
 ]
 
 [[package]]
@@ -5728,7 +6107,7 @@ version = "1.13.5"
 dependencies = [
  "log",
  "rustc_version 0.4.0",
- "semver 1.0.6",
+ "semver 1.0.14",
  "serde",
  "serde_derive",
  "solana-frozen-abi 1.13.5",
@@ -5760,7 +6139,7 @@ name = "solana-zk-token-proof-program"
 version = "1.13.5"
 dependencies = [
  "bytemuck",
- "getrandom 0.1.14",
+ "getrandom 0.1.16",
  "num-derive",
  "num-traits",
  "solana-program-runtime",
@@ -5770,19 +6149,17 @@ dependencies = [
 
 [[package]]
 name = "solana-zk-token-sdk"
-version = "1.10.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "410ee53a26ac91098c289c983863535d4fbb6604b229ae1159503f48fa4fc90f"
+version = "1.13.5"
 dependencies = [
  "aes-gcm-siv",
  "arrayref",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bytemuck",
  "byteorder 1.4.3",
  "cipher 0.4.3",
  "curve25519-dalek",
- "getrandom 0.1.14",
+ "getrandom 0.1.16",
  "lazy_static",
  "merlin",
  "num-derive",
@@ -5791,8 +6168,8 @@ dependencies = [
  "serde",
  "serde_json",
  "sha3 0.9.1",
- "solana-program 1.10.33",
- "solana-sdk 1.10.33",
+ "solana-program 1.13.5",
+ "solana-sdk 1.13.5",
  "subtle",
  "thiserror",
  "zeroize",
@@ -5800,17 +6177,20 @@ dependencies = [
 
 [[package]]
 name = "solana-zk-token-sdk"
-version = "1.13.5"
+version = "1.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5b9d83227b8bdfe5c7b73693aab59942fb3a18a47633284affadd28d65f454"
 dependencies = [
  "aes-gcm-siv",
  "arrayref",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bincode",
  "bytemuck",
  "byteorder 1.4.3",
  "cipher 0.4.3",
  "curve25519-dalek",
- "getrandom 0.1.14",
+ "getrandom 0.1.16",
+ "itertools",
  "lazy_static",
  "merlin",
  "num-derive",
@@ -5819,8 +6199,8 @@ dependencies = [
  "serde",
  "serde_json",
  "sha3 0.9.1",
- "solana-program 1.13.5",
- "solana-sdk 1.13.5",
+ "solana-program 1.14.6",
+ "solana-sdk 1.14.6",
  "subtle",
  "thiserror",
  "zeroize",
@@ -5842,7 +6222,7 @@ dependencies = [
  "rustc-demangle",
  "scroll",
  "thiserror",
- "time 0.1.43",
+ "time 0.1.44",
 ]
 
 [[package]]
@@ -5853,9 +6233,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"
@@ -5877,7 +6257,7 @@ dependencies = [
  "borsh",
  "num-derive",
  "num-traits",
- "solana-program 1.10.33",
+ "solana-program 1.14.6",
  "spl-token",
  "spl-token-2022",
  "thiserror",
@@ -5889,7 +6269,7 @@ version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325"
 dependencies = [
- "solana-program 1.10.33",
+ "solana-program 1.14.6",
 ]
 
 [[package]]
@@ -5903,7 +6283,7 @@ dependencies = [
  "num-derive",
  "num-traits",
  "num_enum",
- "solana-program 1.10.33",
+ "solana-program 1.14.6",
  "thiserror",
 ]
 
@@ -5918,8 +6298,8 @@ dependencies = [
  "num-derive",
  "num-traits",
  "num_enum",
- "solana-program 1.10.33",
- "solana-zk-token-sdk 1.10.33",
+ "solana-program 1.14.6",
+ "solana-zk-token-sdk 1.14.6",
  "spl-memo",
  "spl-token",
  "thiserror",
@@ -5966,11 +6346,11 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "serde",
  "serde_derive",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -5980,13 +6360,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
 dependencies = [
  "base-x",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "serde",
  "serde_derive",
  "serde_json",
- "sha1",
- "syn 1.0.91",
+ "sha1 0.6.1",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6012,26 +6392,32 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
 [[package]]
 name = "strum"
-version = "0.24.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.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "rustversion",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6059,13 +6445,13 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.91"
+version = "1.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "unicode-xid 0.2.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -6076,14 +6462,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.36",
- "quote 1.0.6",
- "syn 1.0.91",
- "unicode-xid 0.2.0",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+ "unicode-xid 0.2.4",
 ]
 
 [[package]]
@@ -6098,9 +6484,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",
@@ -6128,18 +6514,18 @@ checksum = "b85d0a9369a919ba0db919b142a2b704cd207dfc676f7a43c2d105d0bc225487"
 dependencies = [
  "anyhow",
  "fnv",
- "futures 0.3.21",
+ "futures 0.3.25",
  "humantime",
  "opentelemetry",
  "pin-project",
- "rand 0.8.2",
+ "rand 0.8.5",
  "serde",
  "static_assertions",
  "tarpc-plugins",
  "thiserror",
  "tokio",
  "tokio-serde",
- "tokio-util 0.6.4",
+ "tokio-util 0.6.10",
  "tracing",
  "tracing-opentelemetry",
 ]
@@ -6150,9 +6536,9 @@ version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6164,25 +6550,25 @@ dependencies = [
  "cfg-if 1.0.0",
  "fastrand",
  "libc",
- "redox_syscall 0.2.10",
+ "redox_syscall",
  "remove_dir_all",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.0"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
 dependencies = [
  "winapi-util",
 ]
 
 [[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",
@@ -6197,24 +6583,30 @@ dependencies = [
  "unicode-width",
 ]
 
+[[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
 [[package]]
 name = "thiserror"
-version = "1.0.30"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6249,11 +6641,12 @@ 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",
 ]
 
@@ -6274,16 +6667,24 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.7"
+version = "0.3.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
+checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca"
 dependencies = [
  "itoa",
  "libc",
  "num_threads",
- "time-macros 0.2.3",
+ "serde",
+ "time-core",
+ "time-macros 0.2.5",
 ]
 
+[[package]]
+name = "time-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
 [[package]]
 name = "time-macros"
 version = "0.1.1"
@@ -6296,9 +6697,12 @@ dependencies = [
 
 [[package]]
 name = "time-macros"
-version = "0.2.3"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
+checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b"
+dependencies = [
+ "time-core",
+]
 
 [[package]]
 name = "time-macros-impl"
@@ -6307,10 +6711,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
 dependencies = [
  "proc-macro-hack",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
  "standback",
- "syn 1.0.91",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6334,9 +6738,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",
 ]
@@ -6347,6 +6751,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.13.5",
+]
+
+[[package]]
+name = "tip-payment"
+version = "0.1.0"
+dependencies = [
+ "anchor-lang",
+]
+
 [[package]]
 name = "tokio"
 version = "1.14.1"
@@ -6379,13 +6798,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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
@@ -6411,11 +6830,11 @@ 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.4",
+ "rustls 0.20.7",
  "tokio",
  "webpki 0.22.0",
 ]
@@ -6438,9 +6857,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.8"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
+checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -6449,25 +6868,25 @@ dependencies = [
 
 [[package]]
 name = "tokio-tungstenite"
-version = "0.17.1"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
+checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
 dependencies = [
  "futures-util",
  "log",
- "rustls 0.20.4",
+ "rustls 0.20.7",
  "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.4"
+version = "0.6.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec31e5cc6b46e653cf57762f36f71d5e6386391d88a72fd6db4508f8f676fb29"
+checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
 dependencies = [
  "bytes",
  "futures-core",
@@ -6481,9 +6900,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",
@@ -6495,13 +6914,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.1",
+ "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"
@@ -6510,7 +6963,7 @@ checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a"
 dependencies = [
  "async-stream",
  "async-trait",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
  "futures-core",
  "futures-util",
@@ -6519,14 +6972,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.4",
+ "tokio-util 0.6.10",
  "tower",
  "tower-layer",
  "tower-service",
@@ -6536,14 +6989,14 @@ dependencies = [
 
 [[package]]
 name = "tonic"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30fb54bf1e446f44d870d260d99957e7d11fb9d0a0f5bd1a662ad1411cc103f9"
+checksum = "5be9d60db39854b30b835107500cf0aca0b0d14d6e1c3de124217c23a29c2ddb"
 dependencies = [
  "async-stream",
  "async-trait",
  "axum",
- "base64 0.13.0",
+ "base64 0.13.1",
  "bytes",
  "futures-core",
  "futures-util",
@@ -6552,15 +7005,15 @@ dependencies = [
  "http-body",
  "hyper",
  "hyper-timeout",
- "percent-encoding 2.1.0",
+ "percent-encoding 2.2.0",
  "pin-project",
- "prost 0.10.1",
+ "prost 0.10.4",
  "prost-derive 0.10.1",
- "rustls-pemfile 0.3.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",
@@ -6568,46 +7021,58 @@ 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.47",
+ "prost-build 0.8.0",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
 [[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.36",
+ "proc-macro2 1.0.47",
  "prost-build 0.9.0",
- "quote 1.0.6",
- "syn 1.0.91",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[package]]
 name = "tonic-build"
-version = "0.7.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d17087af5c80e5d5fc8ba9878e60258065a0a757e35efe7a05b7904bece1943"
+checksum = "d9263bf4c9bfaae7317c1c2faf7f18491d2fe476f70c414b73bf5d445b00ffa1"
 dependencies = [
  "prettyplease",
- "proc-macro2 1.0.36",
- "prost-build 0.10.1",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "prost-build 0.10.4",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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",
  "indexmap",
  "pin-project",
  "pin-project-lite",
- "rand 0.8.2",
+ "rand 0.8.5",
  "slab",
  "tokio",
- "tokio-util 0.7.1",
+ "tokio-util 0.7.2",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -6615,9 +7080,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",
@@ -6634,21 +7099,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",
@@ -6659,22 +7124,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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
 ]
 
 [[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]]
@@ -6701,9 +7167,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.2.18"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa5553bf0883ba7c9cbe493b085c29926bd41b66afc31ff72cf17ff4fb60dcd5"
+checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
 dependencies = [
  "sharded-slab",
  "thread_local",
@@ -6718,30 +7184,30 @@ 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"
-version = "0.17.2"
+version = "0.17.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
+checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
 dependencies = [
- "base64 0.13.0",
+ "base64 0.13.1",
  "byteorder 1.4.3",
  "bytes",
  "http",
  "httparse",
  "log",
- "rand 0.8.2",
- "rustls 0.20.4",
+ "rand 0.8.5",
+ "rustls 0.20.7",
  "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]]
@@ -6752,9 +7218,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"
@@ -6767,33 +7233,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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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"
@@ -6803,9 +7272,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.0"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
 
 [[package]]
 name = "universal-hash"
@@ -6813,7 +7282,7 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
 dependencies = [
- "generic-array 0.14.5",
+ "generic-array",
  "subtle",
 ]
 
@@ -6843,9 +7312,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
 [[package]]
 name = "uriparse"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e515b1ada404168e145ac55afba3c42f04cf972201a8552d42e2abb17c1b7221"
+checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff"
 dependencies = [
  "fnv",
  "lazy_static",
@@ -6864,14 +7333,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]]
@@ -6886,9 +7354,25 @@ 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 = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
+dependencies = [
+ "getrandom 0.2.8",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
 name = "vcpkg"
@@ -6904,9 +7388,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"
@@ -6916,9 +7400,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",
@@ -6943,15 +7427,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 = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
 dependencies = [
  "cfg-if 1.0.0",
  "wasm-bindgen-macro",
@@ -6959,24 +7449,24 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
 dependencies = [
  "bumpalo",
- "lazy_static",
  "log",
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "once_cell",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "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",
@@ -6986,38 +7476,38 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
 dependencies = [
- "quote 1.0.6",
+ "quote 1.0.21",
  "wasm-bindgen-macro-support",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
 dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.78"
+version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
+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",
@@ -7045,22 +7535,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.4"
+version = "4.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2"
+checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
 dependencies = [
  "either",
- "lazy_static",
  "libc",
+ "once_cell",
 ]
 
 [[package]]
@@ -7108,89 +7607,103 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows-sys"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 dependencies = [
- "windows_aarch64_msvc 0.30.0",
- "windows_i686_gnu 0.30.0",
- "windows_i686_msvc 0.30.0",
- "windows_x86_64_gnu 0.30.0",
- "windows_x86_64_msvc 0.30.0",
+ "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",
 ]
 
 [[package]]
 name = "windows-sys"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
 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",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
 ]
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.32.0"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.30.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.32.0"
+version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
 
 [[package]]
 name = "winreg"
@@ -7208,7 +7721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8"
 dependencies = [
  "asn1-rs",
- "base64 0.13.0",
+ "base64 0.13.1",
  "data-encoding",
  "der-parser",
  "lazy_static",
@@ -7216,14 +7729,14 @@ dependencies = [
  "oid-registry",
  "rusticata-macros",
  "thiserror",
- "time 0.3.7",
+ "time 0.3.16",
 ]
 
 [[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",
 ]
@@ -7237,13 +7750,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.7",
+ "time 0.3.16",
 ]
 
 [[package]]
@@ -7257,30 +7776,30 @@ 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.36",
- "quote 1.0.6",
- "syn 1.0.91",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
  "synstructure",
 ]
 
 [[package]]
 name = "zstd"
-version = "0.11.1+zstd.1.5.2"
+version = "0.11.2+zstd.1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a16b8414fde0414e90c612eba70985577451c4c504b99885ebed24762cb81a"
+checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
 dependencies = [
  "zstd-safe",
 ]
 
 [[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 7e59d0271f..592bb44a1c 100644
--- a/programs/bpf/tests/programs.rs
+++ b/programs/bpf/tests/programs.rs
@@ -348,7 +348,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, ..
@@ -366,7 +366,7 @@ fn execute_transactions(
         true,
         &mut timings,
     );
-    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/replica-node/src/replica_node.rs b/replica-node/src/replica_node.rs
index 29ba56cf81..1b6b772483 100644
--- a/replica-node/src/replica_node.rs
+++ b/replica-node/src/replica_node.rs
@@ -105,6 +105,7 @@ fn initialize_from_snapshot(
 
     let archive_info = snapshot_utils::get_highest_full_snapshot_archive_info(
         &replica_config.snapshot_archives_dir,
+        None,
     )
     .unwrap();
 
diff --git a/replica-node/src/replica_util.rs b/replica-node/src/replica_util.rs
index 5c5dc461cc..b697256438 100644
--- a/replica-node/src/replica_util.rs
+++ b/replica-node/src/replica_util.rs
@@ -113,11 +113,10 @@ fn get_rpc_peer_node(
         );
 
         let mut highest_snapshot_info: Option<(Slot, Hash)> =
-            snapshot_utils::get_highest_full_snapshot_archive_info(snapshot_archives_dir).map(
-                |snapshot_archive_info| {
+            snapshot_utils::get_highest_full_snapshot_archive_info(snapshot_archives_dir, None)
+                .map(|snapshot_archive_info| {
                     (snapshot_archive_info.slot(), *snapshot_archive_info.hash())
-                },
-            );
+                });
         let eligible_rpc_peers = {
             let mut eligible_rpc_peers = vec![];
 
diff --git a/replica-node/tests/local_replica.rs b/replica-node/tests/local_replica.rs
index 372de06c9d..1932b89448 100644
--- a/replica-node/tests/local_replica.rs
+++ b/replica-node/tests/local_replica.rs
@@ -62,7 +62,7 @@ fn wait_for_next_snapshot(
     );
     loop {
         if let Some(full_snapshot_archive_info) =
-            snapshot_utils::get_highest_full_snapshot_archive_info(snapshot_archives_dir)
+            snapshot_utils::get_highest_full_snapshot_archive_info(snapshot_archives_dir, None)
         {
             trace!(
                 "full snapshot for slot {} exists",
diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs
index 312737670b..128dbc404a 100644
--- a/rpc/src/rpc.rs
+++ b/rpc/src/rpc.rs
@@ -64,7 +64,7 @@ use {
         feature_set::{self, nonce_must_be_writable},
         fee_calculator::FeeCalculator,
         hash::Hash,
-        message::SanitizedMessage,
+        message::{SanitizedMessage, SanitizedVersionedMessage, VersionedMessage},
         pubkey::{Pubkey, PUBKEY_BYTES},
         signature::{Keypair, Signature, Signer},
         stake::state::{StakeActivationStatus, StakeState},
@@ -231,6 +231,13 @@ impl JsonRpcRequestProcessor {
         Ok(bank)
     }
 
+    fn bank_from_slot(&self, slot: Slot) -> Option<Arc<Bank>> {
+        debug!("Slot: {:?}", slot);
+
+        let r_bank_forks = self.bank_forks.read().unwrap();
+        r_bank_forks.get(slot)
+    }
+
     #[allow(deprecated)]
     fn bank(&self, commitment: Option<CommitmentConfig>) -> Arc<Bank> {
         debug!("RPC commitment_config: {:?}", commitment);
@@ -358,10 +365,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::<NullTpuInfo>(
-            tpu_address,
+            cluster_info.clone(),
             &bank_forks,
             None,
             receiver,
@@ -2645,13 +2651,16 @@ pub mod rpc_minimal {
                 .map(|snapshot_config| snapshot_config.snapshot_archives_dir)
                 .unwrap();
 
-            let full_snapshot_slot =
-                snapshot_utils::get_highest_full_snapshot_archive_slot(&snapshot_archives_dir)
-                    .ok_or(RpcCustomError::NoSnapshot)?;
+            let full_snapshot_slot = snapshot_utils::get_highest_full_snapshot_archive_slot(
+                &snapshot_archives_dir,
+                None,
+            )
+            .ok_or(RpcCustomError::NoSnapshot)?;
             let incremental_snapshot_slot =
                 snapshot_utils::get_highest_incremental_snapshot_archive_slot(
                     &snapshot_archives_dir,
                     full_snapshot_slot,
+                    None,
                 );
 
             Ok(RpcSnapshotSlotInfo {
@@ -3242,13 +3251,164 @@ 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_client::{
+            self,
+            rpc_config::{RpcSimulateBundleConfig, RpcSimulateTransactionAccountsConfig},
+            rpc_response::{
+                RpcBundleSimulationSummary, RpcSimulateBundleResult,
+                RpcSimulateBundleTransactionResult,
+            },
+        },
+        solana_runtime::bank::{Bank, BundleSimulationResult, BundleSimulationSummary},
+        solana_sdk::{
+            pubkey::Pubkey,
+            transaction::{SanitizedTransaction, VersionedTransaction},
+            transaction_context::TransactionAccount,
+        },
+    };
+
+    pub type BundleSimulationParams = (
+        Vec<SanitizedTransaction>,
+        Vec<Option<Vec<Pubkey>>>,
+        Vec<Option<Vec<Pubkey>>>,
+    );
+
+    fn try_build_pubkeys_from_config(
+        maybe_config: &Option<RpcSimulateTransactionAccountsConfig>,
+        sanitized_tx: &SanitizedTransaction,
+    ) -> Result<Option<Vec<Pubkey>>, 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::<Result<Vec<Pubkey>, Error>>()?,
+            ))
+        } else {
+            Ok(None)
+        }
+    }
+
+    pub fn build_simulate_bundle_params(
+        txs_and_configs: Vec<(
+            VersionedTransaction,
+            Option<RpcSimulateTransactionAccountsConfig>,
+            Option<RpcSimulateTransactionAccountsConfig>,
+        )>,
+        bank: &Bank,
+    ) -> Result<BundleSimulationParams, Error> {
+        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<Vec<TransactionAccount>>,
+        encoding: Option<UiAccountEncoding>,
+    ) -> Result<Option<Vec<UiAccount>>, Error> {
+        if let Some(accounts) = accounts {
+            let encoding = encoding.unwrap_or(UiAccountEncoding::Base64);
+            Ok(Some(
+                accounts
+                    .iter()
+                    .map(|a| encode_account(&a.1, &a.0, encoding, None))
+                    .collect::<Result<Vec<UiAccount>, 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<RpcSimulateBundleResult, Error> {
+        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),
+            });
+        }
+
+        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::*,
-        solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage},
+        crate::rpc::utils::{build_simulate_bundle_params, rpc_bundle_result_from_bank_result},
+        itertools::izip,
     };
+
     #[rpc]
     pub trait Full {
         type Metadata;
@@ -3310,6 +3470,14 @@ pub mod rpc_full {
             config: Option<RpcSimulateTransactionConfig>,
         ) -> Result<RpcResponse<RpcSimulateTransactionResult>>;
 
+        #[rpc(meta, name = "simulateBundle")]
+        fn simulate_bundle(
+            &self,
+            meta: Self::Metadata,
+            rpc_bundle_request: RpcBundleRequest,
+            config: Option<RpcSimulateBundleConfig>,
+        ) -> Result<RpcResponse<RpcSimulateBundleResult>>;
+
         #[rpc(meta, name = "minimumLedgerSlot")]
         fn minimum_ledger_slot(&self, meta: Self::Metadata) -> Result<Slot>;
 
@@ -3595,7 +3763,6 @@ pub mod rpc_full {
                 commitment: preflight_commitment,
                 min_context_slot,
             })?;
-
             let transaction = sanitize_transaction(unsanitized_tx, preflight_bank)?;
             let signature = *transaction.signature();
 
@@ -3786,6 +3953,108 @@ pub mod rpc_full {
             ))
         }
 
+        fn simulate_bundle(
+            &self,
+            meta: Self::Metadata,
+            rpc_bundle_request: RpcBundleRequest,
+            config: Option<RpcSimulateBundleConfig>,
+        ) -> Result<RpcResponse<RpcSimulateBundleResult>> {
+            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::<VersionedTransaction>(encoded_tx, binary_encoding)
+                        .map(|de| de.1)
+                })
+                .collect::<Result<Vec<VersionedTransaction>>>()?;
+
+            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)?;
+                }
+            }
+
+            // TODO (LB): fix simulate_bundle
+            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<Slot> {
             debug!("minimum_ledger_slot rpc request received");
             meta.minimum_ledger_slot()
@@ -4057,6 +4326,7 @@ pub mod rpc_deprecated_v1_9 {
                 .and_then(|snapshot_config| {
                     snapshot_utils::get_highest_full_snapshot_archive_slot(
                         &snapshot_config.snapshot_archives_dir,
+                        None,
                     )
                 })
                 .ok_or_else(|| RpcCustomError::NoSnapshot.into())
@@ -5680,6 +5950,143 @@ 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"],
+                            "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"],
+                            "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::<Response>(expected_response)
+            .expect("expected_response deserialization");
+        let actual_response = serde_json::from_str::<Response>(&actual_response)
+            .expect("actual_response deserialization");
+
+        assert_eq!(expected_response, actual_response);
+    }
+
     #[test]
     fn test_rpc_simulate_transaction() {
         let rpc = RpcHandler::start();
@@ -6073,7 +6480,7 @@ pub mod tests {
                 "value":{
                     "blockhash": recent_blockhash.to_string(),
                     "feeCalculator": {
-                        "lamportsPerSignature": TEST_SIGNATURE_FEE,
+                        "lamportsPerSignature": 5000,
                     }
                 },
             },
@@ -6259,7 +6666,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,
@@ -6268,7 +6674,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),
@@ -6279,7 +6685,7 @@ pub mod tests {
         );
         let connection_cache = Arc::new(ConnectionCache::default());
         SendTransactionService::new::<NullTpuInfo>(
-            tpu_address,
+            cluster_info,
             &bank_forks,
             None,
             receiver,
@@ -6527,7 +6933,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,
@@ -6536,7 +6941,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),
@@ -6547,7 +6952,7 @@ pub mod tests {
         );
         let connection_cache = Arc::new(ConnectionCache::default());
         SendTransactionService::new::<NullTpuInfo>(
-            tpu_address,
+            cluster_info,
             &bank_forks,
             None,
             receiver,
diff --git a/rpc/src/rpc_service.rs b/rpc/src/rpc_service.rs
index 7e32bf81ea..f3be4ac1e8 100644
--- a/rpc/src/rpc_service.rs
+++ b/rpc/src/rpc_service.rs
@@ -238,6 +238,7 @@ impl RequestMiddleware for RpcRequestMiddleware {
                 let full_snapshot_archive_info =
                     snapshot_utils::get_highest_full_snapshot_archive_info(
                         &snapshot_config.snapshot_archives_dir,
+                        None,
                     );
                 let snapshot_archive_info =
                     if let Some(full_snapshot_archive_info) = full_snapshot_archive_info {
@@ -247,6 +248,7 @@ impl RequestMiddleware for RpcRequestMiddleware {
                             snapshot_utils::get_highest_incremental_snapshot_archive_info(
                                 &snapshot_config.snapshot_archives_dir,
                                 full_snapshot_archive_info.slot(),
+                                None,
                             )
                             .map(|incremental_snapshot_archive_info| {
                                 incremental_snapshot_archive_info
@@ -356,8 +358,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.
@@ -448,7 +448,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/Cargo.toml b/runtime/Cargo.toml
index 516d195958..baa6ce86bf 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -27,6 +27,7 @@ index_list = "0.2.7"
 itertools = "0.10.3"
 lazy_static = "1.4.0"
 log = "0.4.14"
+lz4 = "1.24.0"
 memmap2 = "0.5.3"
 num-derive = { version = "0.3" }
 num-traits = { version = "0.2" }
@@ -52,14 +53,13 @@ solana-stake-program = { path = "../programs/stake", version = "=1.13.5" }
 solana-vote-program = { path = "../programs/vote", version = "=1.13.5" }
 solana-zk-token-proof-program = { path = "../programs/zk-token-proof", version = "=1.13.5" }
 solana-zk-token-sdk = { path = "../zk-token-sdk", version = "=1.13.5" }
+strum = { version = "0.24", features = ["derive"] }
+strum_macros = "0.24"
 symlink = "0.1.0"
 tar = "0.4.38"
 tempfile = "3.3.0"
 thiserror = "1.0"
 zstd = "0.11.1"
-lz4 = "1.24.0"
-strum_macros = "0.24"
-strum = { version = "0.24", features = ["derive"] }
 
 [lib]
 crate-type = ["lib"]
diff --git a/runtime/src/account_overrides.rs b/runtime/src/account_overrides.rs
index 62ee494ff2..ee8e7ec9e2 100644
--- a/runtime/src/account_overrides.rs
+++ b/runtime/src/account_overrides.rs
@@ -1,25 +1,31 @@
-use solana_sdk::{account::AccountSharedData, pubkey::Pubkey, sysvar};
+use {
+    solana_sdk::{account::AccountSharedData, pubkey::Pubkey, sysvar},
+    std::collections::HashMap,
+};
 
 /// Encapsulates overridden accounts, typically used for transaction simulations
 #[derive(Default)]
 pub struct AccountOverrides {
-    pub slot_history: Option<AccountSharedData>,
+    accounts: HashMap<Pubkey, AccountSharedData>,
 }
 
 impl AccountOverrides {
+    pub fn set_account(&mut self, pubkey: &Pubkey, account: Option<AccountSharedData>) {
+        match account {
+            Some(account) => self.accounts.insert(*pubkey, account),
+            None => self.accounts.remove(pubkey),
+        };
+    }
+
     /// Sets in the slot history
     ///
     /// Note: no checks are performed on the correctness of the contained data
     pub fn set_slot_history(&mut self, slot_history: Option<AccountSharedData>) {
-        self.slot_history = slot_history;
+        self.set_account(&sysvar::slot_history::id(), slot_history);
     }
 
     /// Gets the account if it's found in the list of overrides
     pub fn get(&self, pubkey: &Pubkey) -> Option<&AccountSharedData> {
-        if pubkey == &sysvar::slot_history::id() {
-            self.slot_history.as_ref()
-        } else {
-            None
-        }
+        self.accounts.get(pubkey)
     }
 }
diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs
index e8859b8522..90e022e750 100644
--- a/runtime/src/accounts.rs
+++ b/runtime/src/accounts.rs
@@ -1049,15 +1049,21 @@ impl Accounts {
         account_locks: &mut AccountLocks,
         writable_keys: Vec<&Pubkey>,
         readonly_keys: Vec<&Pubkey>,
+        additional_read_locks: &HashSet<Pubkey>,
+        additional_write_locks: &HashSet<Pubkey>,
     ) -> 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);
             }
@@ -1117,7 +1123,23 @@ impl Accounts {
         let tx_account_locks_results: Vec<Result<_>> = 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<Item = &'a SanitizedTransaction>,
+        tx_account_lock_limit: usize,
+        account_locks_override: Option<Mutex<AccountLocks>>,
+    ) -> Vec<Result<()>> {
+        let tx_account_locks_results: Vec<Result<_>> = 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]
@@ -1127,6 +1149,8 @@ impl Accounts {
         txs: impl Iterator<Item = &'a SanitizedTransaction>,
         results: impl Iterator<Item = &'a Result<()>>,
         tx_account_lock_limit: usize,
+        additional_read_locks: &HashSet<Pubkey>,
+        additional_write_locks: &HashSet<Pubkey>,
     ) -> Vec<Result<()>> {
         let tx_account_locks_results: Vec<Result<_>> = txs
             .zip(results)
@@ -1135,13 +1159,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<Result<TransactionAccountLocks>>,
+        additional_read_locks: &HashSet<Pubkey>,
+        additional_write_locks: &HashSet<Pubkey>,
     ) -> Vec<Result<()>> {
         let account_locks = &mut self.account_locks.lock().unwrap();
         tx_account_locks_results
@@ -1151,12 +1181,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<Result<TransactionAccountLocks>>,
+        account_locks_override: Option<Mutex<AccountLocks>>,
+    ) -> Vec<Result<()>> {
+        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>(
@@ -1169,6 +1238,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)
@@ -1201,7 +1271,7 @@ impl Accounts {
         leave_nonce_on_success: bool,
         preserve_rent_epoch_for_rent_exempt_accounts: bool,
     ) {
-        let accounts_to_store = self.collect_accounts_to_store(
+        let accounts_to_store = Self::collect_accounts_to_store(
             txs,
             res,
             loaded,
@@ -1227,8 +1297,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],
@@ -1431,6 +1500,7 @@ mod tests {
             sync::atomic::{AtomicBool, AtomicU64, Ordering},
             thread, time,
         },
+        Accounts,
     };
 
     fn new_sanitized_tx<T: Signers>(
@@ -2893,6 +2963,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
@@ -3015,7 +3087,7 @@ mod tests {
         }
         let txs = vec![tx0, tx1];
         let execution_results = vec![new_execution_result(Ok(()), None); 2];
-        let collected_accounts = accounts.collect_accounts_to_store(
+        let collected_accounts = Accounts::collect_accounts_to_store(
             &txs,
             &execution_results,
             loaded.as_mut_slice(),
@@ -3500,7 +3572,7 @@ mod tests {
 
         let durable_nonce =
             DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
-        let accounts = Accounts::new_with_config_for_tests(
+        let _accounts = Accounts::new_with_config_for_tests(
             Vec::new(),
             &ClusterType::Development,
             AccountSecondaryIndexes::default(),
@@ -3515,7 +3587,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(),
@@ -3629,7 +3701,7 @@ mod tests {
 
         let durable_nonce =
             DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
-        let accounts = Accounts::new_with_config_for_tests(
+        let _accounts = Accounts::new_with_config_for_tests(
             Vec::new(),
             &ClusterType::Development,
             AccountSecondaryIndexes::default(),
@@ -3644,7 +3716,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 d8af3939d2..ab78d50c9c 100644
--- a/runtime/src/bank.rs
+++ b/runtime/src/bank.rs
@@ -39,7 +39,9 @@ pub use solana_sdk::reward_type::RewardType;
 use {
     crate::{
         account_overrides::AccountOverrides,
-        accounts::{AccountAddressFilter, Accounts, LoadedTransaction, TransactionLoadResult},
+        accounts::{
+            AccountAddressFilter, AccountLocks, Accounts, LoadedTransaction, TransactionLoadResult,
+        },
         accounts_db::{
             AccountShrinkThreshold, AccountsDbConfig, SnapshotStorages,
             ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
@@ -94,6 +96,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_RECENT_BLOCKHASHES,
@@ -145,19 +148,22 @@ use {
     std::{
         borrow::Cow,
         cell::RefCell,
+        cmp::min,
         collections::{HashMap, HashSet},
         convert::{TryFrom, TryInto},
+        error::Error,
         fmt, mem,
         ops::{Div, RangeInclusive},
         path::PathBuf,
         ptr,
         rc::Rc,
+        result,
         sync::{
             atomic::{
                 AtomicBool, AtomicI64, AtomicU64,
                 Ordering::{AcqRel, Acquire, Relaxed},
             },
-            Arc, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard,
+            Arc, LockResult, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
         },
         time::{Duration, Instant},
     },
@@ -230,7 +236,7 @@ impl RentDebits {
 }
 
 type BankStatusCache = StatusCache<Result<()>>;
-#[frozen_abi(digest = "2YZk2K45HmmAafmxPJnYVXyQ7uA7WuBrRkpwrCawdK31")]
+#[frozen_abi(digest = "82WhodJTtPtYR1R3XnVAn8iMJAMJvcWAGuoorbjSSRUb")]
 pub type BankSlotDelta = SlotDelta<Result<()>>;
 
 // Eager rent collection repeats in cyclic manner.
@@ -660,8 +666,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,
+                    executors: _,
+                } => {
+                    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<TransactionLoadResult>,
     // Vector of results indicating whether a transaction was executed or could not
@@ -701,6 +739,32 @@ impl DurableNonceFee {
     }
 }
 
+#[derive(Debug, Clone, PartialEq)]
+pub enum BundleSimulationSummary {
+    // error and transaction signature responsible
+    Failed {
+        error: BundleExecutionError,
+        tx_signature: Signature,
+    },
+    Succeeded,
+}
+
+#[derive(Clone)]
+pub struct BundleSimulationResult {
+    /// Gives high level summary of bundle.
+    pub summary: BundleSimulationSummary,
+    pub transaction_results: Vec<BundleTransactionSimulationResult>,
+}
+
+#[derive(Clone)]
+pub struct BundleTransactionSimulationResult {
+    pub result: Result<()>,
+    pub logs: TransactionLogMessages,
+    pub pre_execution_accounts: Option<Vec<TransactionAccount>>,
+    pub post_execution_accounts: Option<Vec<TransactionAccount>>,
+    pub units_consumed: u64,
+}
+
 pub struct TransactionSimulationResult {
     pub result: Result<()>,
     pub logs: TransactionLogMessages,
@@ -1185,7 +1249,7 @@ pub struct Bank {
     inflation: Arc<RwLock<Inflation>>,
 
     /// 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
@@ -3576,12 +3640,33 @@ impl Bank {
         &'a self,
         transactions: &'b [SanitizedTransaction],
         transaction_results: impl Iterator<Item = &'b Result<()>>,
+        additional_read_locks: &HashSet<Pubkey>,
+        additional_write_locks: &HashSet<Pubkey>,
     ) -> TransactionBatch<'a, 'b> {
-        // this lock_results could be: Ok, AccountInUse, WouldExceedBlockMaxLimit or WouldExceedAccountMaxLimit
         let lock_results = self.rc.accounts.lock_accounts_with_results(
             transactions.iter(),
             transaction_results,
             self.get_transaction_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<Mutex<AccountLocks>>,
+    ) -> 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))
     }
@@ -3600,6 +3685,243 @@ 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<SanitizedTransaction>,
+        pre_execution_accounts_requested: Vec<Option<Vec<Pubkey>>>,
+        post_execution_accounts_requested: Vec<Option<Vec<Pubkey>>>,
+    ) -> result::Result<BundleSimulationResult, Box<dyn Error>> {
+        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<SanitizedTransaction>,
+        pre_execution_accounts_requested: Vec<Option<Vec<Pubkey>>>,
+        post_execution_accounts_requested: Vec<Option<Vec<Pubkey>>>,
+    ) -> result::Result<BundleSimulationResult, Box<dyn Error>> {
+        // 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,
+                        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((*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,
+                &mut timings,
+                Some(&account_overrides),
+            );
+
+            // 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::<HashMap<&Pubkey, &AccountSharedData>>();
+
+            // 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((*pubkey, 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(),
+                    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,
+                        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,
@@ -3617,14 +3939,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,
@@ -3638,41 +3959,41 @@ impl Bank {
             Some(&account_overrides),
         );
 
-        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::<Vec<_>>()
-            })
-            .unwrap_or_default();
+        Self::build_transaction_simulation_result(&loaded_transactions[0], &execution_results[0])
+    }
 
-        let units_consumed = timings
-            .details
-            .per_program_timings
-            .iter()
-            .fold(0, |acc: u64, (_, program_timing)| {
-                acc.saturating_add(program_timing.accumulated_units)
-            });
+    fn build_transaction_simulation_result(
+        loaded_transaction_result: &TransactionLoadResult,
+        execution_result: &TransactionExecutionResult,
+    ) -> TransactionSimulationResult {
+        let (logs, units_consumed, result) = match execution_result {
+            TransactionExecutionResult::Executed { details, .. } => {
+                let log_messages = if let Some(ref log_messages) = details.log_messages {
+                    log_messages.clone()
+                } else {
+                    vec![]
+                };
 
-        debug!("simulate_transaction: {:?}", timings);
+                (
+                    log_messages,
+                    details.executed_units,
+                    execution_result.flattened_result(),
+                )
+            }
+            TransactionExecutionResult::NotExecuted(_) => {
+                (vec![], 0, execution_result.flattened_result())
+            }
+        };
 
-        let execution_result = execution_results.pop().unwrap();
-        let flattened_result = execution_result.flattened_result();
-        let logs = match execution_result {
-            TransactionExecutionResult::Executed { details, .. } => details.log_messages,
-            TransactionExecutionResult::NotExecuted(_) => None,
-        }
-        .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,
@@ -3874,6 +4195,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<u64> = 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_executors(&self, accounts: &[TransactionAccount]) -> Rc<RefCell<Executors>> {
         let executable_keys: Vec<_> = accounts
@@ -4676,6 +5020,30 @@ 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 durable_nonce = {
+            let separate_nonce_from_blockhash = self.separate_nonce_from_blockhash();
+            let durable_nonce =
+                DurableNonce::from_blockhash(&self.last_blockhash(), separate_nonce_from_blockhash);
+            (durable_nonce, separate_nonce_from_blockhash)
+        };
+        Accounts::collect_accounts_to_store(
+            txs,
+            res,
+            loaded,
+            &self.rent_collector,
+            &durable_nonce,
+            self.get_lamports_per_signature(),
+            self.leave_nonce_on_success(),
+            self.preserve_rent_epoch_for_rent_exempt_accounts(),
+        )
+    }
+
     // Distribute collected rent fees for this slot to staked validators (excluding stakers)
     // according to stake.
     //
@@ -7071,6 +7439,127 @@ pub(crate) mod tests {
         }
     }
 
+    fn tx_factory(
+        readonly_accounts: Vec<Pubkey>,
+        mut writeable_accounts: Vec<Pubkey>,
+        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<usize>,
+        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);
+    }
+
     #[test]
     fn test_nonce_info() {
         let lamports_per_signature = 42;
@@ -7649,6 +8138,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<Pubkey>,
+        lamports: u64,
+        bank: &Bank,
+    ) -> Vec<AccountSharedData> {
+        pubkeys
+            .iter()
+            .map(|pk| {
+                let data = AccountSharedData::new(lamports, 0, &system_program::id());
+                bank.store_account(pk, &data);
+                data
+            })
+            .collect::<Vec<_>>()
+    }
+
+    fn assert_transaction_results(
+        actual_results: Vec<Result<()>>,
+        expected_results: Vec<Result<()>>,
+    ) {
+        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<Option<Vec<TransactionAccount>>>,
+        expected_lamports: Vec<Option<HashMap<Pubkey, u64>>>,
+    ) {
+        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.0 == pk).unwrap();
+                    assert_eq!(account.1.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::<Vec<Result<()>>>();
+        assert_transaction_results(actual_results, expected_reults);
+
+        let actual_pre_lamports = result
+            .transaction_results
+            .clone()
+            .into_iter()
+            .map(|res| res.pre_execution_accounts)
+            .collect::<Vec<Option<Vec<TransactionAccount>>>>();
+        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::<Vec<Option<Vec<TransactionAccount>>>>();
+        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::<Vec<Result<()>>>();
+        assert_transaction_results(actual_results, expected_reults);
+
+        let actual_pre_lamports = result
+            .transaction_results
+            .clone()
+            .into_iter()
+            .map(|res| res.pre_execution_accounts)
+            .collect::<Vec<Option<Vec<TransactionAccount>>>>();
+        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::<Vec<Option<Vec<TransactionAccount>>>>();
+        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::<Vec<Result<()>>>();
+        assert_transaction_results(actual_results, expected_reults);
+
+        let actual_pre_lamports = result
+            .transaction_results
+            .clone()
+            .into_iter()
+            .map(|res| res.pre_execution_accounts)
+            .collect::<Vec<Option<Vec<TransactionAccount>>>>();
+        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::<Vec<Option<Vec<TransactionAccount>>>>();
+        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 9071bc78e6..bb3e59427d 100644
--- a/runtime/src/builtins.rs
+++ b/runtime/src/builtins.rs
@@ -198,7 +198,7 @@ fn builtin_feature_transitions() -> Vec<BuiltinFeatureTransition> {
     ]
 }
 
-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 4a3e74fdfd..fbd4a8f031 100644
--- a/runtime/src/snapshot_utils.rs
+++ b/runtime/src/snapshot_utils.rs
@@ -884,17 +884,20 @@ pub fn bank_from_latest_snapshot_archives(
     verify_index: bool,
     accounts_db_config: Option<AccountsDbConfig>,
     accounts_update_notifier: Option<AccountsUpdateNotifier>,
+    halt_at_slot: Option<Slot>,
 ) -> Result<(
     Bank,
     FullSnapshotArchiveInfo,
     Option<IncrementalSnapshotArchiveInfo>,
 )> {
-    let full_snapshot_archive_info = get_highest_full_snapshot_archive_info(&snapshot_archives_dir)
-        .ok_or(SnapshotError::NoSnapshotArchives)?;
+    let full_snapshot_archive_info =
+        get_highest_full_snapshot_archive_info(&snapshot_archives_dir, halt_at_slot)
+            .ok_or(SnapshotError::NoSnapshotArchives)?;
 
     let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info(
         &snapshot_archives_dir,
         full_snapshot_archive_info.slot(),
+        halt_at_slot,
     );
 
     info!(
@@ -1253,11 +1256,14 @@ where
 }
 
 /// Get the highest slot of the full snapshot archives in a directory
-pub fn get_highest_full_snapshot_archive_slot<P>(snapshot_archives_dir: P) -> Option<Slot>
+pub fn get_highest_full_snapshot_archive_slot<P>(
+    snapshot_archives_dir: P,
+    halt_at_slot: Option<Slot>,
+) -> Option<Slot>
 where
     P: AsRef<Path>,
 {
-    get_highest_full_snapshot_archive_info(snapshot_archives_dir)
+    get_highest_full_snapshot_archive_info(snapshot_archives_dir, halt_at_slot)
         .map(|full_snapshot_archive_info| full_snapshot_archive_info.slot())
 }
 
@@ -1266,19 +1272,35 @@ where
 pub fn get_highest_incremental_snapshot_archive_slot<P: AsRef<Path>>(
     snapshot_archives_dir: P,
     full_snapshot_slot: Slot,
+    halt_at_slot: Option<Slot>,
 ) -> Option<Slot> {
-    get_highest_incremental_snapshot_archive_info(snapshot_archives_dir, full_snapshot_slot)
-        .map(|incremental_snapshot_archive_info| incremental_snapshot_archive_info.slot())
+    get_highest_incremental_snapshot_archive_info(
+        snapshot_archives_dir,
+        full_snapshot_slot,
+        halt_at_slot,
+    )
+    .map(|incremental_snapshot_archive_info| incremental_snapshot_archive_info.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<P>(
     snapshot_archives_dir: P,
+    halt_at_slot: Option<Slot>,
 ) -> Option<FullSnapshotArchiveInfo>
 where
     P: AsRef<Path>,
 {
     let mut full_snapshot_archives = get_full_snapshot_archives(snapshot_archives_dir);
+    info!(
+        "halt_at_slot: {:?} full_snapshot_archives: {:?}",
+        halt_at_slot, full_snapshot_archives
+    );
+    if let Some(halt_at_slot) = halt_at_slot {
+        info!("retaining slots for {}", halt_at_slot);
+        full_snapshot_archives
+            .retain(|archive| archive.snapshot_archive_info().slot <= halt_at_slot);
+    }
+    info!("full_snapshot_archives: {:?}", full_snapshot_archives);
     full_snapshot_archives.sort_unstable();
     full_snapshot_archives.into_iter().rev().next()
 }
@@ -1288,6 +1310,7 @@ where
 pub fn get_highest_incremental_snapshot_archive_info<P>(
     snapshot_archives_dir: P,
     full_snapshot_slot: Slot,
+    halt_at_slot: Option<Slot>,
 ) -> Option<IncrementalSnapshotArchiveInfo>
 where
     P: AsRef<Path>,
@@ -1302,6 +1325,9 @@ where
                 incremental_snapshot_archive_info.base_slot() == full_snapshot_slot
             })
             .collect::<Vec<_>>();
+    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()
 }
@@ -1353,7 +1379,8 @@ pub fn purge_old_snapshot_archives<P>(
     // `maximum_incremental_snapshot_archives_to_retain`.
     //
     // Purge all the rest.
-    let highest_full_snapshot_slot = get_highest_full_snapshot_archive_slot(&snapshot_archives_dir);
+    let highest_full_snapshot_slot =
+        get_highest_full_snapshot_archive_slot(&snapshot_archives_dir, None);
     let mut incremental_snapshot_archives_with_same_base_slot = vec![];
     let mut incremental_snapshot_archives_with_different_base_slot = vec![];
     get_incremental_snapshot_archives(&snapshot_archives_dir)
@@ -2607,7 +2634,7 @@ mod tests {
         );
 
         assert_eq!(
-            get_highest_full_snapshot_archive_slot(temp_snapshot_archives_dir.path()),
+            get_highest_full_snapshot_archive_slot(temp_snapshot_archives_dir.path(), None),
             Some(max_slot - 1)
         );
     }
@@ -2632,7 +2659,8 @@ mod tests {
             assert_eq!(
                 get_highest_incremental_snapshot_archive_slot(
                     temp_snapshot_archives_dir.path(),
-                    full_snapshot_slot
+                    full_snapshot_slot,
+                    None
                 ),
                 Some(max_incremental_snapshot_slot - 1)
             );
@@ -2641,7 +2669,8 @@ mod tests {
         assert_eq!(
             get_highest_incremental_snapshot_archive_slot(
                 temp_snapshot_archives_dir.path(),
-                max_full_snapshot_slot
+                max_full_snapshot_slot,
+                None,
             ),
             None
         );
@@ -3243,6 +3272,7 @@ mod tests {
             false,
             Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
             None,
+            None,
         )
         .unwrap();
 
diff --git a/runtime/src/stakes.rs b/runtime/src/stakes.rs
index 34b993a7d3..ac832bdb55 100644
--- a/runtime/src/stakes.rs
+++ b/runtime/src/stakes.rs
@@ -149,7 +149,7 @@ pub struct Stakes {
     vote_accounts: VoteAccounts,
 
     /// stake_delegations
-    stake_delegations: ImHashMap<Pubkey, Delegation>,
+    pub stake_delegations: ImHashMap<Pubkey, Delegation>,
 
     /// unused
     unused: u64,
@@ -318,7 +318,7 @@ impl Stakes {
         &self.vote_accounts
     }
 
-    pub(crate) fn stake_delegations(&self) -> &ImHashMap<Pubkey, Delegation> {
+    pub fn stake_delegations(&self) -> &ImHashMap<Pubkey, Delegation> {
         &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..d6e0fd31b3
--- /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 docker-output "$SCRIPT_DIR" "$HOST":~/
diff --git a/scripts/increment-cargo-version.sh b/scripts/increment-cargo-version.sh
index faf91f36d6..ce2fcc86ad 100755
--- a/scripts/increment-cargo-version.sh
+++ b/scripts/increment-cargo-version.sh
@@ -25,6 +25,9 @@ ignores=(
   web3.js/examples
   web3.js/test
   node_modules
+  jito-programs
+  anchor
+  solana-accountsdb-connector
 )
 
 not_paths=()
diff --git a/scripts/run.sh b/scripts/run.sh
index 86407d339d..48c9af6418 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 bed05ae33e..c736c7a21f 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.13.5" }
 solana-sdk-macro = { path = "macro", version = "=1.13.5" }
 thiserror = "1.0"
 uriparse = "0.6.3"
+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<anchor_lang::error::Error> 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<VersionedTransaction>,
+}
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<SanitizedTransaction>,
+}
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<T> = Result<T, BundleExecutionError>;
diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs
index 08da2ce824..3e4230a33d 100644
--- a/sdk/src/lib.rs
+++ b/sdk/src/lib.rs
@@ -12,6 +12,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 3f25fa5a62..0d9fa6944c 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}) without 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<SanitizeError> for TransactionError {
diff --git a/send-transaction-service/Cargo.toml b/send-transaction-service/Cargo.toml
index d03ed70921..846502ee0e 100644
--- a/send-transaction-service/Cargo.toml
+++ b/send-transaction-service/Cargo.toml
@@ -13,14 +13,15 @@ edition = "2021"
 crossbeam-channel = "0.5"
 log = "0.4.14"
 solana-client = { path = "../client", version = "=1.13.5" }
+solana-gossip = { path = "../gossip", version = "=1.13.5" }
 solana-measure = { path = "../measure", version = "=1.13.5" }
 solana-metrics = { path = "../metrics", version = "=1.13.5" }
 solana-runtime = { path = "../runtime", version = "=1.13.5" }
 solana-sdk = { path = "../sdk", version = "=1.13.5" }
 
-
 [dev-dependencies]
 solana-logger = { path = "../logger", version = "=1.13.5" }
+solana-streamer = { path = "../streamer", version = "=1.13.5" }
 
 [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 e440968b9d..f0b68e2dc9 100644
--- a/send-transaction-service/src/send_transaction_service.rs
+++ b/send-transaction-service/src/send_transaction_service.rs
@@ -3,6 +3,7 @@ use {
     crossbeam_channel::{Receiver, RecvTimeoutError},
     log::*,
     solana_client::{connection_cache::ConnectionCache, tpu_connection::TpuConnection},
+    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<T: TpuInfo + std::marker::Send + 'static>(
-        tpu_address: SocketAddr,
+        cluster_info: Arc<ClusterInfo>,
         bank_forks: &Arc<RwLock<BankForks>>,
         leader_info: Option<T>,
         receiver: Receiver<TransactionInfo>,
@@ -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<T: TpuInfo + std::marker::Send + 'static>(
-        tpu_address: SocketAddr,
+        cluster_info: Arc<ClusterInfo>,
         bank_forks: &Arc<RwLock<BankForks>>,
         leader_info: Option<T>,
         receiver: Receiver<TransactionInfo>,
@@ -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<T: TpuInfo + std::marker::Send + 'static>(
-        tpu_address: SocketAddr,
+        cluster_info: Arc<ClusterInfo>,
         receiver: Receiver<TransactionInfo>,
         leader_info_provider: Arc<Mutex<CurrentLeaderInfo<T>>>,
         connection_cache: Arc<ConnectionCache>,
@@ -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;
                     let _result = Self::send_transactions_in_batch(
                         &tpu_address,
                         &mut transactions,
@@ -500,7 +502,7 @@ impl SendTransactionService {
 
     /// Thread responsible for retrying transactions
     fn retry_thread<T: TpuInfo + std::marker::Send + 'static>(
-        tpu_address: SocketAddr,
+        cluster_info: Arc<ClusterInfo>,
         bank_forks: Arc<RwLock<BankForks>>,
         leader_info_provider: Arc<Mutex<CurrentLeaderInfo<T>>>,
         connection_cache: Arc<ConnectionCache>,
@@ -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,
@@ -780,27 +782,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<ClusterInfo> = Arc::new(ClusterInfo::new(
+            contact_info,
+            Arc::new(Keypair::new()),
+            SocketAddrSpace::new(false),
+        ));
         let send_tranaction_service = SendTransactionService::new::<NullTpuInfo>(
-            tpu_address,
+            cluster_info,
             &bank_forks,
             None,
             receiver,
diff --git a/solana-accountsdb-connector b/solana-accountsdb-connector
new file mode 160000
index 0000000000..45cab4114e
--- /dev/null
+++ b/solana-accountsdb-connector
@@ -0,0 +1 @@
+Subproject commit 45cab4114e32ba408a7b9ad1174fcfa20a3e9748
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-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs
index d8440b3c95..5d410c8889 100644
--- a/storage-bigtable/src/bigtable.rs
+++ b/storage-bigtable/src/bigtable.rs
@@ -223,7 +223,7 @@ impl BigTableConnection {
             self.channel.clone(),
             move |mut req: Request<()>| {
                 if let Some(access_token) = &access_token {
-                    match MetadataValue::from_str(&access_token.get()) {
+                    match MetadataValue::try_from(&access_token.get()) {
                         Ok(authorization_header) => {
                             req.metadata_mut()
                                 .insert("authorization", authorization_header);
diff --git a/storage-proto/proto/transaction_by_addr.proto b/storage-proto/proto/transaction_by_addr.proto
index 2d582ba524..aa619550f8 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 cb6eaf06b7..2bbad7d394 100644
--- a/storage-proto/src/convert.rs
+++ b/storage-proto/src/convert.rs
@@ -730,6 +730,8 @@ impl TryFrom<tx_by_addr::TransactionError> for TransactionError {
             27 => TransactionError::InvalidRentPayingAccount,
             28 => TransactionError::WouldExceedMaxVoteCostLimit,
             29 => TransactionError::WouldExceedAccountDataTotalLimit,
+            32 => TransactionError::BundleNotContinuous,
+            33 => TransactionError::SkippedExecution,
             _ => return Err("Invalid TransactionError"),
         })
     }
@@ -833,6 +835,12 @@ impl From<TransactionError> 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..21c3399667
--- /dev/null
+++ b/tip-distributor/Cargo.toml
@@ -0,0 +1,48 @@
+[package]
+name = "solana-tip-distributor"
+version = "1.13.5"
+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.13.5" }
+solana-genesis-utils = { path = "../genesis-utils", version = "=1.13.5" }
+solana-ledger = { path = "../ledger", version = "=1.13.5" }
+solana-merkle-tree = { path = "../merkle-tree", version = "=1.13.5" }
+solana-program = { path = "../sdk/program", version = "=1.13.5" }
+solana-rpc = { path = "../rpc", version = "=1.13.5" }
+solana-runtime = { path = "../runtime", version = "=1.13.5" }
+solana-sdk = { path = "../sdk", version = "=1.13.5" }
+solana-stake-program = { path = "../programs/stake", version = "=1.13.5" }
+thiserror = "1.0.31"
+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.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"
+
+[[bin]]
+name = "solana-merkle-root-uploader"
+path = "src/bin/merkle-root-uploader.rs"
+
+[[bin]]
+name = "solana-claim-mev-tips"
+path = "src/bin/claim-mev-tips.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/claim-mev-tips.rs b/tip-distributor/src/bin/claim-mev-tips.rs
new file mode 100644
index 0000000000..45b515dbd1
--- /dev/null
+++ b/tip-distributor/src/bin/claim-mev-tips.rs
@@ -0,0 +1,52 @@
+//! This binary claims MEV tips.
+
+use {
+    clap::Parser,
+    log::*,
+    solana_sdk::pubkey::Pubkey,
+    solana_tip_distributor::claim_mev_workflow::claim_mev_tips,
+    std::{path::PathBuf, str::FromStr},
+};
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct Args {
+    /// Path to JSON file containing the [GeneratedMerkleTreeCollection] object.
+    #[clap(long, env)]
+    merkle_trees_path: PathBuf,
+
+    /// RPC to send transactions through
+    #[clap(long, env)]
+    rpc_url: String,
+
+    /// Tip distribution program ID
+    #[clap(long, env)]
+    tip_distribution_program_id: String,
+
+    /// Path to keypair
+    #[clap(long, env)]
+    keypair_path: PathBuf,
+}
+
+fn main() {
+    env_logger::init();
+    info!("Starting to claim mev tips...");
+
+    let args: Args = Args::parse();
+
+    let tip_distribution_program_id = Pubkey::from_str(&args.tip_distribution_program_id)
+        .expect("valid tip_distribution_program_id");
+
+    if let Err(e) = claim_mev_tips(
+        &args.merkle_trees_path,
+        &args.rpc_url,
+        &tip_distribution_program_id,
+        &args.keypair_path,
+    ) {
+        panic!("error claiming mev tips: {:?}", e);
+    }
+    info!(
+        "done claiming mev tips from file {:?}",
+        args.merkle_trees_path
+    );
+}
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..f9e9072bed
--- /dev/null
+++ b/tip-distributor/src/bin/merkle-root-generator.rs
@@ -0,0 +1,29 @@
+//! This binary generates a merkle tree for each [TipDistributionAccount]; they are derived
+//! using a user provided [StakeMetaCollection] JSON file.
+
+use {
+    clap::Parser, log::*,
+    solana_tip_distributor::merkle_root_generator_workflow::generate_merkle_root,
+    std::path::PathBuf,
+};
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct Args {
+    /// Path to JSON file containing the [StakeMetaCollection] object.
+    #[clap(long, env)]
+    stake_meta_coll_path: PathBuf,
+
+    /// Path to JSON file to get populated with tree node data.
+    #[clap(long, env)]
+    out_path: PathBuf,
+}
+
+fn main() {
+    env_logger::init();
+    info!("Starting merkle-root-generator workflow...");
+
+    let args: Args = Args::parse();
+    generate_merkle_root(&args.stake_meta_coll_path, &args.out_path).expect("merkle tree produced");
+    info!("saved merkle roots to {:?}", args.stake_meta_coll_path);
+}
diff --git a/tip-distributor/src/bin/merkle-root-uploader.rs b/tip-distributor/src/bin/merkle-root-uploader.rs
new file mode 100644
index 0000000000..f23e8f74b2
--- /dev/null
+++ b/tip-distributor/src/bin/merkle-root-uploader.rs
@@ -0,0 +1,50 @@
+use {
+    clap::Parser,
+    log::info,
+    solana_sdk::pubkey::Pubkey,
+    solana_tip_distributor::merkle_root_upload_workflow::upload_merkle_root,
+    std::{path::PathBuf, str::FromStr},
+};
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct Args {
+    /// Path to JSON file containing the [StakeMetaCollection] object.
+    #[clap(long, env)]
+    merkle_root_path: PathBuf,
+
+    /// The path to the keypair used to sign and pay for the `upload_merkle_root` transactions.
+    #[clap(long, env)]
+    keypair_path: PathBuf,
+
+    /// The RPC to send transactions to.
+    #[clap(long, env)]
+    rpc_url: String,
+
+    /// Tip distribution program ID
+    #[clap(long, env)]
+    tip_distribution_program_id: String,
+}
+
+fn main() {
+    env_logger::init();
+
+    let args: Args = Args::parse();
+
+    let tip_distribution_program_id = Pubkey::from_str(&args.tip_distribution_program_id)
+        .expect("valid tip_distribution_program_id");
+
+    info!("starting merkle root uploader...");
+    if let Err(e) = upload_merkle_root(
+        &args.merkle_root_path,
+        &args.keypair_path,
+        &args.rpc_url,
+        &tip_distribution_program_id,
+    ) {
+        panic!("failed to upload merkle roots: {:?}", e);
+    }
+    info!(
+        "uploaded merkle roots from file {:?}",
+        args.merkle_root_path
+    );
+}
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..365f82290e
--- /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_sdk::{clock::Slot, pubkey::Pubkey},
+    solana_tip_distributor::{self, stake_meta_generator_workflow::generate_stake_meta},
+    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,
+
+    /// The tip-payment program id.
+    #[clap(long, env)]
+    tip_payment_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,
+}
+
+impl Args {
+    fn ledger_path_parser(ledger_path: &str) -> Result<PathBuf, &'static str> {
+        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();
+
+    if let Err(e) = generate_stake_meta(
+        &args.ledger_path,
+        &args.snapshot_slot,
+        &args.tip_distribution_program_id,
+        &args.out_path,
+        &args.tip_payment_program_id,
+    ) {
+        error!("error producing stake-meta: {:?}", e);
+    } else {
+        info!("produced stake meta");
+    }
+}
diff --git a/tip-distributor/src/claim_mev_workflow.rs b/tip-distributor/src/claim_mev_workflow.rs
new file mode 100644
index 0000000000..3a2fd19bd8
--- /dev/null
+++ b/tip-distributor/src/claim_mev_workflow.rs
@@ -0,0 +1,143 @@
+use {
+    crate::{read_json_from_file, send_transactions_with_retry, GeneratedMerkleTreeCollection},
+    anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas},
+    log::{debug, info},
+    solana_client::{
+        client_error::ClientErrorKind, nonblocking::rpc_client::RpcClient, rpc_request::RpcError,
+    },
+    solana_program::system_program,
+    solana_sdk::{
+        commitment_config::CommitmentConfig,
+        instruction::Instruction,
+        pubkey::Pubkey,
+        signature::{read_keypair_file, Signer},
+        transaction::Transaction,
+    },
+    std::{path::PathBuf, time::Duration},
+    thiserror::Error,
+    tip_distribution::state::*,
+    tokio::runtime::Builder,
+};
+
+#[derive(Error, Debug)]
+pub enum ClaimMevError {
+    #[error(transparent)]
+    IoError(#[from] std::io::Error),
+
+    #[error(transparent)]
+    JsonError(#[from] serde_json::Error),
+}
+
+pub fn claim_mev_tips(
+    merkle_root_path: &PathBuf,
+    rpc_url: &str,
+    tip_distribution_program_id: &Pubkey,
+    keypair_path: &PathBuf,
+) -> Result<(), ClaimMevError> {
+    // roughly how long before blockhash expires
+    const MAX_RETRY_DURATION: Duration = Duration::from_secs(60);
+
+    let merkle_trees: GeneratedMerkleTreeCollection =
+        read_json_from_file(merkle_root_path).expect("read GeneratedMerkleTreeCollection");
+    let keypair = read_keypair_file(keypair_path).expect("read keypair file");
+
+    let tip_distribution_config =
+        Pubkey::find_program_address(&[Config::SEED], tip_distribution_program_id).0;
+
+    let rpc_client =
+        RpcClient::new_with_commitment(rpc_url.to_string(), CommitmentConfig::confirmed());
+
+    let runtime = Builder::new_multi_thread()
+        .worker_threads(16)
+        .enable_all()
+        .build()
+        .unwrap();
+
+    let mut transactions = Vec::new();
+
+    runtime.block_on(async move {
+        let blockhash = rpc_client
+            .get_latest_blockhash()
+            .await
+            .expect("read blockhash");
+
+        for tree in merkle_trees.generated_merkle_trees {
+            for node in tree.tree_nodes {
+                let (claim_status_pubkey, claim_status_bump) = Pubkey::find_program_address(
+                    &[
+                        ClaimStatus::SEED,
+                        node.claimant.as_ref(), // ordering matters here
+                        tree.tip_distribution_account.as_ref(),
+                    ],
+                    tip_distribution_program_id,
+                );
+
+                // only claim for ones that have merkle root on-chain
+                let account = rpc_client
+                    .get_account(&tree.tip_distribution_account)
+                    .await
+                    .expect("expected to fetch 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");
+
+                if fetched_tip_distribution_account.merkle_root.is_some() {
+                    match rpc_client.get_account(&claim_status_pubkey).await {
+                        Ok(_) => {
+                            debug!(
+                                "claim status account already exists: {:?}",
+                                claim_status_pubkey
+                            );
+                        }
+                        Err(e) => {
+                            if matches!(e.kind(), ClientErrorKind::RpcError(RpcError::ForUser(_))) {
+                                info!("claiming for public key: {:?}", node.claimant);
+
+                                let ix = Instruction {
+                                    program_id: *tip_distribution_program_id,
+                                    data: tip_distribution::instruction::Claim {
+                                        proof: node.proof.unwrap(),
+                                        amount: node.amount,
+                                        bump: claim_status_bump,
+                                    }
+                                    .data(),
+                                    accounts: tip_distribution::accounts::Claim {
+                                        config: tip_distribution_config,
+                                        tip_distribution_account: tree.tip_distribution_account,
+                                        claimant: node.claimant,
+                                        claim_status: claim_status_pubkey,
+                                        payer: keypair.pubkey(),
+                                        system_program: system_program::id(),
+                                    }
+                                    .to_account_metas(None),
+                                };
+                                let transaction = Transaction::new_signed_with_payer(
+                                    &[ix],
+                                    Some(&keypair.pubkey()),
+                                    &[&keypair],
+                                    blockhash,
+                                );
+                                info!("tx: {:?}", transaction);
+                                transactions.push(transaction);
+                            } else {
+                                panic!("unexpected rpc error: {:?}", e);
+                            }
+                        }
+                    }
+                } else {
+                    info!(
+                        "not claiming because merkle root isn't uploaded yet claimant: {:?} tda: {:?}",
+                        node.claimant,
+                        tree.tip_distribution_account
+                    );
+                }
+            }
+        }
+
+        send_transactions_with_retry(&rpc_client, &transactions, MAX_RETRY_DURATION).await;
+    });
+
+    Ok(())
+}
diff --git a/tip-distributor/src/lib.rs b/tip-distributor/src/lib.rs
new file mode 100644
index 0000000000..4ccde97ff0
--- /dev/null
+++ b/tip-distributor/src/lib.rs
@@ -0,0 +1,779 @@
+pub mod claim_mev_workflow;
+pub mod merkle_root_generator_workflow;
+pub mod merkle_root_upload_workflow;
+pub mod stake_meta_generator_workflow;
+
+use {
+    crate::{
+        merkle_root_generator_workflow::MerkleRootGeneratorError,
+        stake_meta_generator_workflow::StakeMetaGeneratorError::CheckedMathError,
+    },
+    bigdecimal::{num_bigint::BigUint, BigDecimal},
+    log::{error, info},
+    num_traits::{CheckedDiv, CheckedMul, ToPrimitive},
+    serde::{de::DeserializeOwned, Deserialize, Serialize},
+    solana_client::nonblocking::rpc_client::RpcClient,
+    solana_merkle_tree::MerkleTree,
+    solana_sdk::{
+        account::{AccountSharedData, ReadableAccount},
+        clock::Slot,
+        hash::{Hash, Hasher},
+        pubkey::Pubkey,
+        signature::Signature,
+        stake_history::Epoch,
+        transaction::Transaction,
+    },
+    std::{
+        collections::HashMap,
+        fs::File,
+        io::BufReader,
+        ops::{Div, Mul},
+        path::PathBuf,
+        time::{Duration, Instant},
+    },
+    tip_distribution::state::TipDistributionAccount,
+    tip_payment::{
+        Config, 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,
+    },
+    tokio::time::sleep,
+};
+
+#[derive(Deserialize, Serialize, Debug)]
+pub struct GeneratedMerkleTreeCollection {
+    pub generated_merkle_trees: Vec<GeneratedMerkleTree>,
+    pub bank_hash: String,
+    pub epoch: Epoch,
+    pub slot: Slot,
+}
+
+#[derive(Eq, Debug, Hash, PartialEq, Deserialize, Serialize)]
+pub struct GeneratedMerkleTree {
+    #[serde(with = "pubkey_string_conversion")]
+    pub tip_distribution_account: Pubkey,
+    #[serde(with = "pubkey_string_conversion")]
+    pub merkle_root_upload_authority: Pubkey,
+    pub merkle_root: Hash,
+    pub tree_nodes: Vec<TreeNode>,
+    pub max_total_claim: u64,
+    pub max_num_nodes: u64,
+}
+
+pub struct TipPaymentPubkeys {
+    config_pda: Pubkey,
+    tip_pdas: Vec<Pubkey>,
+}
+
+impl GeneratedMerkleTreeCollection {
+    pub fn new_from_stake_meta_collection(
+        stake_meta_coll: StakeMetaCollection,
+    ) -> Result<GeneratedMerkleTreeCollection, MerkleRootGeneratorError> {
+        let generated_merkle_trees = stake_meta_coll
+            .stake_metas
+            .into_iter()
+            .filter(|stake_meta| stake_meta.maybe_tip_distribution_meta.is_some())
+            .filter_map(|stake_meta| {
+                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,
+                }?;
+
+                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 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: tip_distribution_meta.tip_distribution_pubkey,
+                    merkle_root_upload_authority: tip_distribution_meta
+                        .merkle_root_upload_authority,
+                    merkle_root: *merkle_tree.get_root().unwrap(),
+                    tree_nodes,
+                    max_total_claim: tip_distribution_meta.total_tips,
+                }))
+            })
+            .collect::<Result<Vec<GeneratedMerkleTree>, MerkleRootGeneratorError>>()?;
+
+        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
+}
+
+fn derive_tip_payment_pubkeys(program_id: &Pubkey) -> TipPaymentPubkeys {
+    let config_pda = Pubkey::find_program_address(&[CONFIG_ACCOUNT_SEED], program_id).0;
+    let tip_pda_0 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_0], program_id).0;
+    let tip_pda_1 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_1], program_id).0;
+    let tip_pda_2 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_2], program_id).0;
+    let tip_pda_3 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_3], program_id).0;
+    let tip_pda_4 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_4], program_id).0;
+    let tip_pda_5 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_5], program_id).0;
+    let tip_pda_6 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_6], program_id).0;
+    let tip_pda_7 = Pubkey::find_program_address(&[TIP_ACCOUNT_SEED_7], program_id).0;
+
+    TipPaymentPubkeys {
+        config_pda,
+        tip_pdas: vec![
+            tip_pda_0, tip_pda_1, tip_pda_2, tip_pda_3, tip_pda_4, tip_pda_5, tip_pda_6, tip_pda_7,
+        ],
+    }
+}
+
+#[derive(Clone, Eq, Debug, Hash, PartialEq, Deserialize, Serialize)]
+pub struct TreeNode {
+    /// The stake account entitled to redeem.
+    #[serde(with = "pubkey_string_conversion")]
+    pub claimant: Pubkey,
+
+    #[serde(with = "pubkey_string_conversion")]
+    pub staker_pubkey: Pubkey,
+
+    #[serde(with = "pubkey_string_conversion")]
+    pub withdrawer_pubkey: Pubkey,
+
+    /// The amount this account is entitled to.
+    pub amount: u64,
+
+    /// The proof associated with this TreeNode
+    pub proof: Option<Vec<[u8; 32]>>,
+}
+
+impl TreeNode {
+    fn vec_from_stake_meta(
+        stake_meta: &StakeMeta,
+    ) -> Result<Option<Vec<TreeNode>>, MerkleRootGeneratorError> {
+        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: stake_meta.validator_vote_account,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                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.lamports_delegated as f64)
+                        .expect(&*format!(
+                            "failed to convert amount_delegated to BigDecimal [stake_account={}, amount_delegated={}]",
+                            delegation.stake_account_pubkey,
+                            delegation.lamports_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_pubkey, 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_pubkey,
+                        staker_pubkey: delegation.staker_pubkey,
+                        withdrawer_pubkey: delegation.withdrawer_pubkey,
+                        amount: amount.to_u64().unwrap(),
+                        proof: None
+                    })
+                })
+                .collect::<Result<Vec<TreeNode>, MerkleRootGeneratorError>>()?);
+
+            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<StakeMeta>,
+
+    /// base58 encoded tip-distribution program id.
+    #[serde(with = "pubkey_string_conversion")]
+    pub tip_distribution_program_id: Pubkey,
+
+    /// 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 {
+    #[serde(with = "pubkey_string_conversion")]
+    pub validator_vote_account: Pubkey,
+
+    /// The validator's tip-distribution meta if it exists.
+    pub maybe_tip_distribution_meta: Option<TipDistributionMeta>,
+
+    /// Delegations to this validator.
+    pub delegations: Vec<Delegation>,
+
+    /// 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 {
+    #[serde(with = "pubkey_string_conversion")]
+    pub merkle_root_upload_authority: Pubkey,
+
+    #[serde(with = "pubkey_string_conversion")]
+    pub tip_distribution_pubkey: Pubkey,
+
+    /// 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<Self, stake_meta_generator_workflow::StakeMetaGeneratorError> {
+        Ok(TipDistributionMeta {
+            tip_distribution_pubkey: tda_wrapper.tip_distribution_pubkey,
+            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: tda_wrapper
+                .tip_distribution_account
+                .merkle_root_upload_authority,
+        })
+    }
+}
+
+#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
+pub struct Delegation {
+    #[serde(with = "pubkey_string_conversion")]
+    pub stake_account_pubkey: Pubkey,
+
+    #[serde(with = "pubkey_string_conversion")]
+    pub staker_pubkey: Pubkey,
+
+    #[serde(with = "pubkey_string_conversion")]
+    pub withdrawer_pubkey: Pubkey,
+
+    /// Lamports delegated by the stake account
+    pub lamports_delegated: u64,
+}
+
+/// Convenience wrapper around [TipDistributionAccount]
+pub struct TipDistributionAccountWrapper {
+    pub tip_distribution_account: TipDistributionAccount,
+    pub account_data: AccountSharedData,
+    pub tip_distribution_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,
+    )
+}
+
+/// 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()
+}
+
+pub async fn send_transactions_with_retry(
+    rpc_client: &RpcClient,
+    transactions: &[Transaction],
+    max_send_duration: Duration,
+) {
+    let mut transactions_to_send: HashMap<Signature, Transaction> = transactions
+        .iter()
+        .map(|tx| (tx.signatures[0], tx.clone()))
+        .collect();
+
+    let start = Instant::now();
+    while !transactions_to_send.is_empty() && start.elapsed() < max_send_duration {
+        info!("sending {} transactions", transactions_to_send.len());
+
+        for (signature, tx) in &transactions_to_send {
+            match rpc_client.send_transaction(tx).await {
+                Ok(send_tx_sig) => {
+                    info!("send transaction: {:?}", send_tx_sig);
+                }
+                Err(e) => {
+                    error!("error sending transaction {:?} error: {:?}", signature, e);
+                }
+            }
+        }
+
+        sleep(Duration::from_secs(10)).await;
+
+        let mut signatures_confirmed: Vec<Signature> = Vec::new();
+        for signature in transactions_to_send.keys() {
+            match rpc_client.confirm_transaction(signature).await {
+                Ok(true) => {
+                    info!("confirmed signature: {:?}", signature);
+                    signatures_confirmed.push(*signature);
+                }
+                Ok(false) => {
+                    info!("didn't confirmed signature: {:?}", signature);
+                }
+                Err(e) => {
+                    error!(
+                        "error confirming signature: {:?}, signature: {:?}",
+                        signature, e
+                    );
+                }
+            }
+        }
+
+        info!("confirmed #{} signatures", signatures_confirmed.len());
+        for sig in signatures_confirmed {
+            transactions_to_send.remove(&sig);
+        }
+    }
+
+    assert!(
+        transactions_to_send.is_empty(),
+        "all transactions failed to send"
+    );
+}
+
+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(),
+        )
+    }
+}
+
+mod pubkey_string_conversion {
+    use {
+        serde::{self, Deserialize, Deserializer, Serializer},
+        solana_sdk::pubkey::Pubkey,
+        std::str::FromStr,
+    };
+
+    pub(crate) fn serialize<S>(pubkey: &Pubkey, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&pubkey.to_string())
+    }
+
+    pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Pubkey, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let s = String::deserialize(deserializer)?;
+        Pubkey::from_str(&s).map_err(serde::de::Error::custom)
+    }
+}
+
+pub(crate) fn read_json_from_file<T>(path: &PathBuf) -> serde_json::Result<T>
+where
+    T: DeserializeOwned,
+{
+    let file = File::open(path).unwrap();
+    let reader = BufReader::new(file);
+    serde_json::from_reader(reader)
+}
+
+#[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(),
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 151_507,
+                proof: None,
+            },
+            TreeNode {
+                claimant: acct_1.parse().unwrap(),
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                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 merkle_root_upload_authority = Pubkey::new_unique();
+
+        let (tda_0, tda_1) = (Pubkey::new_unique(), Pubkey::new_unique());
+
+        let stake_account_0 = Pubkey::new_unique();
+        let stake_account_1 = Pubkey::new_unique();
+        let stake_account_2 = Pubkey::new_unique();
+        let stake_account_3 = Pubkey::new_unique();
+
+        let staker_account_0 = Pubkey::new_unique();
+        let staker_account_1 = Pubkey::new_unique();
+        let staker_account_2 = Pubkey::new_unique();
+        let staker_account_3 = Pubkey::new_unique();
+
+        let validator_vote_account_0 = Pubkey::new_unique();
+        let validator_vote_account_1 = Pubkey::new_unique();
+
+        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,
+                    maybe_tip_distribution_meta: Some(TipDistributionMeta {
+                        merkle_root_upload_authority,
+                        tip_distribution_pubkey: tda_0,
+                        total_tips: 1_900_122_111_000,
+                        validator_fee_bps: 100,
+                    }),
+                    delegations: vec![
+                        Delegation {
+                            stake_account_pubkey: stake_account_0,
+                            staker_pubkey: staker_account_0,
+                            withdrawer_pubkey: staker_account_0,
+                            lamports_delegated: 123_999_123_555,
+                        },
+                        Delegation {
+                            stake_account_pubkey: stake_account_1,
+                            staker_pubkey: staker_account_1,
+                            withdrawer_pubkey: staker_account_1,
+                            lamports_delegated: 144_555_444_556,
+                        },
+                    ],
+                    total_delegated: 1_555_123_000_333_454_000,
+                    commission: 100,
+                },
+                StakeMeta {
+                    validator_vote_account: validator_vote_account_1,
+                    maybe_tip_distribution_meta: Some(TipDistributionMeta {
+                        merkle_root_upload_authority,
+                        tip_distribution_pubkey: tda_1,
+                        total_tips: 1_900_122_111_333,
+                        validator_fee_bps: 200,
+                    }),
+                    delegations: vec![
+                        Delegation {
+                            stake_account_pubkey: stake_account_2,
+                            staker_pubkey: staker_account_2,
+                            withdrawer_pubkey: staker_account_2,
+                            lamports_delegated: 224_555_444,
+                        },
+                        Delegation {
+                            stake_account_pubkey: stake_account_3,
+                            staker_pubkey: staker_account_3,
+                            withdrawer_pubkey: staker_account_3,
+                            lamports_delegated: 700_888_944_555,
+                        },
+                    ],
+                    total_delegated: 2_565_318_909_444_123,
+                    commission: 10,
+                },
+            ],
+            tip_distribution_program_id: Pubkey::new_unique(),
+            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(),
+        )
+        .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,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 19_001_221_110,
+                proof: None,
+            },
+            TreeNode {
+                claimant: stake_account_0,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 149_992,
+                proof: None,
+            },
+            TreeNode {
+                claimant: stake_account_1,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 174_858,
+                proof: None,
+            },
+        ];
+        let hashed_nodes: Vec<[u8; 32]> = tree_nodes.iter().map(|n| n.hash().to_bytes()).collect();
+        let merkle_tree = MerkleTree::new(&hashed_nodes[..], true);
+        let gmt_0 = GeneratedMerkleTree {
+            tip_distribution_account: tda_0,
+            merkle_root_upload_authority,
+            merkle_root: *merkle_tree.get_root().unwrap(),
+            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,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 38_002_442_227,
+                proof: None,
+            },
+            TreeNode {
+                claimant: stake_account_2,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 163_000,
+                proof: None,
+            },
+            TreeNode {
+                claimant: stake_account_3,
+                staker_pubkey: Pubkey::default(),
+                withdrawer_pubkey: Pubkey::default(),
+                amount: 508_762_900,
+                proof: None,
+            },
+        ];
+        let hashed_nodes: Vec<[u8; 32]> = tree_nodes.iter().map(|n| n.hash().to_bytes()).collect();
+        let merkle_tree = MerkleTree::new(&hashed_nodes[..], true);
+        let gmt_1 = GeneratedMerkleTree {
+            tip_distribution_account: tda_1,
+            merkle_root_upload_authority,
+            merkle_root: *merkle_tree.get_root().unwrap(),
+            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_root, actual_gmt.merkle_root);
+            });
+    }
+}
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..0da8c1fdb0
--- /dev/null
+++ b/tip-distributor/src/merkle_root_generator_workflow.rs
@@ -0,0 +1,49 @@
+use {
+    crate::{read_json_from_file, GeneratedMerkleTreeCollection, StakeMetaCollection},
+    log::*,
+    std::{
+        fmt::Debug,
+        fs::File,
+        io::{BufWriter, Write},
+        path::PathBuf,
+    },
+    thiserror::Error,
+};
+
+#[derive(Error, Debug)]
+pub enum MerkleRootGeneratorError {
+    #[error(transparent)]
+    IoError(#[from] std::io::Error),
+
+    #[error(transparent)]
+    RpcError(#[from] Box<solana_client::client_error::ClientError>),
+
+    #[error(transparent)]
+    SerdeJsonError(#[from] serde_json::Error),
+}
+
+pub fn generate_merkle_root(
+    stake_meta_coll_path: &PathBuf,
+    out_path: &PathBuf,
+) -> Result<(), MerkleRootGeneratorError> {
+    let stake_meta_coll: StakeMetaCollection = read_json_from_file(stake_meta_coll_path)?;
+
+    let merkle_tree_coll =
+        GeneratedMerkleTreeCollection::new_from_stake_meta_collection(stake_meta_coll)?;
+
+    write_to_json_file(&merkle_tree_coll, out_path)?;
+    Ok(())
+}
+
+fn write_to_json_file(
+    merkle_tree_coll: &GeneratedMerkleTreeCollection,
+    file_path: &PathBuf,
+) -> Result<(), MerkleRootGeneratorError> {
+    let file = File::create(file_path)?;
+    let mut writer = BufWriter::new(file);
+    let json = serde_json::to_string_pretty(&merkle_tree_coll).unwrap();
+    let _ = writer.write(json.as_bytes())?;
+    writer.flush()?;
+
+    Ok(())
+}
diff --git a/tip-distributor/src/merkle_root_upload_workflow.rs b/tip-distributor/src/merkle_root_upload_workflow.rs
new file mode 100644
index 0000000000..4925087fae
--- /dev/null
+++ b/tip-distributor/src/merkle_root_upload_workflow.rs
@@ -0,0 +1,126 @@
+use {
+    crate::{
+        read_json_from_file, send_transactions_with_retry, GeneratedMerkleTree,
+        GeneratedMerkleTreeCollection,
+    },
+    anchor_lang::AccountDeserialize,
+    log::{error, info},
+    solana_client::nonblocking::rpc_client::RpcClient,
+    solana_sdk::{
+        commitment_config::CommitmentConfig,
+        pubkey::Pubkey,
+        signature::{read_keypair_file, Signer},
+        transaction::Transaction,
+    },
+    std::{path::PathBuf, time::Duration},
+    thiserror::Error,
+    tip_distribution::{
+        sdk::instruction::{upload_merkle_root_ix, UploadMerkleRootAccounts, UploadMerkleRootArgs},
+        state::{Config, TipDistributionAccount},
+    },
+    tokio::runtime::Builder,
+};
+
+#[derive(Error, Debug)]
+pub enum MerkleRootUploadError {
+    #[error(transparent)]
+    IoError(#[from] std::io::Error),
+
+    #[error(transparent)]
+    JsonError(#[from] serde_json::Error),
+}
+
+pub fn upload_merkle_root(
+    merkle_root_path: &PathBuf,
+    keypair_path: &PathBuf,
+    rpc_url: &str,
+    tip_distribution_program_id: &Pubkey,
+) -> Result<(), MerkleRootUploadError> {
+    // max amount of time before blockhash expires
+    const MAX_RETRY_DURATION: Duration = Duration::from_secs(60);
+
+    let merkle_tree: GeneratedMerkleTreeCollection =
+        read_json_from_file(merkle_root_path).expect("read GeneratedMerkleTreeCollection");
+    let keypair = read_keypair_file(keypair_path).expect("read keypair file");
+
+    let tip_distribution_config =
+        Pubkey::find_program_address(&[Config::SEED], tip_distribution_program_id).0;
+
+    let runtime = Builder::new_multi_thread()
+        .worker_threads(16)
+        .enable_all()
+        .build()
+        .expect("build runtime");
+
+    runtime.block_on(async move {
+        let rpc_client =
+            RpcClient::new_with_commitment(rpc_url.to_string(), CommitmentConfig::confirmed());
+        let recent_blockhash = rpc_client
+            .get_latest_blockhash()
+            .await
+            .expect("get blockhash");
+
+        let trees: Vec<GeneratedMerkleTree> = merkle_tree
+            .generated_merkle_trees
+            .into_iter()
+            .filter(|tree| tree.merkle_root_upload_authority == keypair.pubkey())
+            .collect();
+
+        info!("num trees to upload: {:?}", trees.len());
+
+        let mut trees_needing_update: Vec<GeneratedMerkleTree> = vec![];
+        for tree in trees {
+            let account = rpc_client
+                .get_account(&tree.tip_distribution_account)
+                .await
+                .expect("fetch expect");
+
+            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");
+
+            let needs_upload = match fetched_tip_distribution_account.merkle_root {
+                Some(merkle_root) => {
+                    merkle_root.total_funds_claimed == 0
+                        && merkle_root.root != tree.merkle_root.to_bytes()
+                }
+                None => true,
+            };
+
+            if needs_upload {
+                trees_needing_update.push(tree);
+            }
+        }
+
+        info!("num trees need uploading: {:?}", trees_needing_update.len());
+
+        let transactions: Vec<Transaction> = trees_needing_update
+            .iter()
+            .map(|tree| {
+                let ix = upload_merkle_root_ix(
+                    *tip_distribution_program_id,
+                    UploadMerkleRootArgs {
+                        root: tree.merkle_root.to_bytes(),
+                        max_total_claim: tree.max_total_claim,
+                        max_num_nodes: tree.max_num_nodes,
+                    },
+                    UploadMerkleRootAccounts {
+                        config: tip_distribution_config,
+                        merkle_root_upload_authority: keypair.pubkey(),
+                        tip_distribution_account: tree.tip_distribution_account,
+                    },
+                );
+                Transaction::new_signed_with_payer(
+                    &[ix],
+                    Some(&keypair.pubkey()),
+                    &[&keypair],
+                    recent_blockhash,
+                )
+            })
+            .collect();
+        send_transactions_with_retry(&rpc_client, &transactions, MAX_RETRY_DURATION).await;
+    });
+
+    Ok(())
+}
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..fde03fd47c
--- /dev/null
+++ b/tip-distributor/src/stake_meta_generator_workflow.rs
@@ -0,0 +1,836 @@
+use {
+    crate::{
+        derive_tip_distribution_account_address, derive_tip_payment_pubkeys, Config, StakeMeta,
+        StakeMetaCollection, TipDistributionAccount, TipDistributionAccountWrapper,
+        TipDistributionMeta,
+    },
+    anchor_lang::AccountDeserialize,
+    itertools::Itertools,
+    log::*,
+    solana_client::client_error::ClientError,
+    solana_ledger::{
+        bank_forks_utils,
+        blockstore::BlockstoreError,
+        blockstore_processor::{BlockstoreProcessorError, ProcessOptions},
+    },
+    solana_program::{
+        borsh::try_from_slice_unchecked,
+        stake::state::{Delegation, StakeState},
+    },
+    solana_runtime::{
+        bank::Bank,
+        hardened_unpack::{open_genesis_config, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE},
+        snapshot_config::SnapshotConfig,
+        stakes::StakesCache,
+        vote_account::VoteAccount,
+    },
+    solana_sdk::{
+        account::{ReadableAccount, WritableAccount},
+        clock::Slot,
+        pubkey::Pubkey,
+    },
+    std::{
+        collections::HashMap,
+        fmt::{Debug, Display, Formatter},
+        fs::File,
+        io::{BufWriter, Write},
+        path::{Path, PathBuf},
+        sync::Arc,
+    },
+    thiserror::Error,
+};
+
+#[derive(Error, Debug)]
+pub enum StakeMetaGeneratorError {
+    #[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 StakeMetaGeneratorError {
+    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 generate_stake_meta(
+    ledger_path: &Path,
+    snapshot_slot: &Slot,
+    tip_distribution_program_id: &Pubkey,
+    out_path: &str,
+    tip_payment_program_id: &Pubkey,
+) -> Result<(), StakeMetaGeneratorError> {
+    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, tip_payment_program_id)?;
+
+    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<Arc<Bank>, StakeMetaGeneratorError> {
+    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,
+        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,
+    );
+
+    let working_bank = bank_forks.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: &str,
+) -> Result<(), StakeMetaGeneratorError> {
+    let file = File::create(out_path)?;
+    let mut writer = BufWriter::new(file);
+    let json = serde_json::to_string_pretty(&stake_meta_coll).unwrap();
+    let _ = writer.write(json.as_bytes())?;
+    writer.flush()?;
+
+    Ok(())
+}
+
+/// Creates a collection of [StakeMeta]'s from the given bank.
+pub fn generate_stake_meta_collection(
+    bank: &Arc<Bank>,
+    tip_distribution_program_id: &Pubkey,
+    tip_payment_program_id: &Pubkey,
+) -> Result<StakeMetaCollection, StakeMetaGeneratorError> {
+    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 voter_pubkey_to_delegations = group_delegations_by_voter_pubkey(delegations, bank);
+
+    // the last leader in an epoch may not crank the tip program before the epoch is over, which
+    // would result in MEV rewards for epoch N not being cranked until epoch N + 1. This means that
+    // the account balance in the snapshot could be incorrect.
+    // We assume that the rewards sitting in the tip program PDAs are cranked out by the time all of
+    // the rewards are claimed.
+    let tip_accounts = derive_tip_payment_pubkeys(tip_payment_program_id);
+    let account = bank.get_account(&tip_accounts.config_pda);
+    let maybe_tip_receiver: Option<Pubkey> = account
+        .and_then(|account| Config::try_deserialize(&mut account.data()).ok())
+        .map(|config| config.tip_receiver);
+
+    let excess_tip_balances: u64 = tip_accounts
+        .tip_pdas
+        .iter()
+        .map(|pubkey| {
+            bank.get_account(pubkey)
+                .map(|acc| {
+                    acc.lamports()
+                        .checked_sub(bank.get_minimum_balance_for_rent_exemption(acc.data().len()))
+                        .expect("tip balance underflow")
+                })
+                .unwrap_or_default()
+        })
+        .sum();
+
+    let vote_pk_and_maybe_tdas: Vec<(
+        (Pubkey, &VoteAccount),
+        Option<TipDistributionAccountWrapper>,
+    )> = epoch_vote_accounts
+        .iter()
+        .map(|(vote_pubkey, (_total_stake, vote_account))| {
+            let tip_distribution_pubkey = derive_tip_distribution_account_address(
+                tip_distribution_program_id,
+                vote_pubkey,
+                bank.epoch(),
+            )
+            .0;
+            let tda = bank
+                .get_account(&tip_distribution_pubkey)
+                .map(|mut account_data| {
+                    let tip_distribution_account =
+                        TipDistributionAccount::try_deserialize(&mut account_data.data())
+                            .expect("deserialized TipDistributionAccount");
+                    // this snapshot might have tips that weren't claimed by the time the epoch is over
+                    // assume that it will eventually be cranked and credit the excess to this account
+                    if maybe_tip_receiver.is_some()
+                        && tip_distribution_pubkey == maybe_tip_receiver.unwrap()
+                    {
+                        account_data.set_lamports(
+                            account_data
+                                .lamports()
+                                .checked_add(excess_tip_balances)
+                                .expect("tip overflow"),
+                        );
+                    }
+                    TipDistributionAccountWrapper {
+                        tip_distribution_account,
+                        account_data,
+                        tip_distribution_pubkey,
+                    }
+                });
+            Ok(((*vote_pubkey, vote_account), tda))
+        })
+        .collect::<Result<_, StakeMetaGeneratorError>>()?;
+
+    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.lamports_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: vote_pubkey,
+                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: *tip_distribution_program_id,
+        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<Pubkey, Delegation>,
+    bank: &Bank,
+) -> HashMap<Pubkey, Vec<crate::Delegation>> {
+    delegations
+        .into_iter()
+        .filter(|(_stake_pubkey, delegation)| delegation.stake(bank.epoch(), None) > 0)
+        .into_group_map_by(|(_stake_pubkey, delegation)| delegation.voter_pubkey)
+        .into_iter()
+        .map(|(voter_pubkey, group)| {
+            (
+                voter_pubkey,
+                group
+                    .into_iter()
+                    .map(|(stake_pubkey, delegation)| {
+                        let account_data = bank.get_account(stake_pubkey).expect("account exists");
+                        assert!(
+                            StakesCache::is_stake(&account_data),
+                            "{} is not StakeState",
+                            stake_pubkey
+                        );
+                        let stake_state =
+                            try_from_slice_unchecked::<StakeState>(account_data.data())
+                                .expect("StakeState deserializes");
+                        crate::Delegation {
+                            stake_account_pubkey: *stake_pubkey,
+                            staker_pubkey: stake_state
+                                .authorized()
+                                .map(|a| a.staker)
+                                .unwrap_or_default(),
+                            withdrawer_pubkey: stake_state
+                                .authorized()
+                                .map(|a| a.withdrawer)
+                                .unwrap_or_default(),
+                            lamports_delegated: delegation.stake,
+                        }
+                    })
+                    .collect::<Vec<crate::Delegation>>(),
+            )
+        })
+        .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,
+        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(),
+        );
+
+        bank.store_account(&delegator_0_pk, &d_0_data);
+        bank.store_account(&delegator_1_pk, &d_1_data);
+        bank.store_account(&delegator_2_pk, &d_2_data);
+        bank.store_account(&delegator_3_pk, &d_3_data);
+        bank.store_account(&delegator_4_pk, &d_4_data);
+
+        /* 3. Delegate some stake to the initial set of validators */
+        let mut validator_0_delegations = vec![crate::Delegation {
+            stake_account_pubkey: validator_keypairs_0.stake_keypair.pubkey(),
+            staker_pubkey: validator_keypairs_0.stake_keypair.pubkey(),
+            withdrawer_pubkey: validator_keypairs_0.stake_keypair.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_0.pubkey(),
+            withdrawer_pubkey: delegator_0.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_1.pubkey(),
+            withdrawer_pubkey: delegator_1.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_2.pubkey(),
+            withdrawer_pubkey: delegator_2.pubkey(),
+            lamports_delegated: 33_000_000_000,
+        });
+
+        let mut validator_1_delegations = vec![crate::Delegation {
+            stake_account_pubkey: validator_keypairs_1.stake_keypair.pubkey(),
+            staker_pubkey: validator_keypairs_1.stake_keypair.pubkey(),
+            withdrawer_pubkey: validator_keypairs_1.stake_keypair.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_3.pubkey(),
+            withdrawer_pubkey: delegator_3.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_4.pubkey(),
+            withdrawer_pubkey: delegator_4.pubkey(),
+            lamports_delegated: 6_000_000_527,
+        });
+
+        let mut validator_2_delegations = vec![crate::Delegation {
+            stake_account_pubkey: validator_keypairs_2.stake_keypair.pubkey(),
+            staker_pubkey: validator_keypairs_2.stake_keypair.pubkey(),
+            withdrawer_pubkey: validator_keypairs_2.stake_keypair.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_0.pubkey(),
+            withdrawer_pubkey: delegator_0.pubkey(),
+            lamports_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_pubkey: stake_account,
+            staker_pubkey: delegator_4.pubkey(),
+            withdrawer_pubkey: delegator_4.pubkey(),
+            lamports_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::<StakeHistory, _>(
+                                &bank.get_account(&sysvar::stake_history::id()).unwrap(),
+                            )
+                            .unwrap(),
+                        ),
+                    )
+                {
+                    return false;
+                }
+            }
+
+            true
+        }
+        fn next_epoch(bank: &Arc<Bank>) -> Arc<Bank> {
+            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| v.stake_account_pubkey)
+            .collect::<Vec<Pubkey>>();
+        stake_pubkeys.extend(
+            validator_1_delegations
+                .iter()
+                .map(|v| v.stake_account_pubkey),
+        );
+        stake_pubkeys.extend(
+            validator_2_delegations
+                .iter()
+                .map(|v| v.stake_account_pubkey),
+        );
+        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);
+
+        bank.store_account(&tip_distribution_account_0.0, &data_0);
+        bank.store_account(&tip_distribution_account_1.0, &data_1);
+        bank.store_account(&tip_distribution_account_2.0, &data_2);
+
+        bank.freeze();
+        let stake_meta_collection = generate_stake_meta_collection(
+            &bank,
+            &tip_distribution_program_id,
+            &Pubkey::new_unique(),
+        )
+        .unwrap();
+        assert_eq!(
+            stake_meta_collection.tip_distribution_program_id,
+            tip_distribution_program_id
+        );
+        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(
+            validator_keypairs_0.vote_keypair.pubkey(),
+            StakeMeta {
+                validator_vote_account: validator_keypairs_0.vote_keypair.pubkey(),
+                delegations: validator_0_delegations.clone(),
+                total_delegated: validator_0_delegations
+                    .iter()
+                    .fold(0u64, |sum, delegation| {
+                        sum.checked_add(delegation.lamports_delegated).unwrap()
+                    }),
+                maybe_tip_distribution_meta: Some(TipDistributionMeta {
+                    merkle_root_upload_authority,
+                    tip_distribution_pubkey: tda_0_fields.0,
+                    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(
+            validator_keypairs_1.vote_keypair.pubkey(),
+            StakeMeta {
+                validator_vote_account: validator_keypairs_1.vote_keypair.pubkey(),
+                delegations: validator_1_delegations.clone(),
+                total_delegated: validator_1_delegations
+                    .iter()
+                    .fold(0u64, |sum, delegation| {
+                        sum.checked_add(delegation.lamports_delegated).unwrap()
+                    }),
+                maybe_tip_distribution_meta: Some(TipDistributionMeta {
+                    merkle_root_upload_authority,
+                    tip_distribution_pubkey: tda_1_fields.0,
+                    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(
+            validator_keypairs_2.vote_keypair.pubkey(),
+            StakeMeta {
+                validator_vote_account: validator_keypairs_2.vote_keypair.pubkey(),
+                delegations: validator_2_delegations.clone(),
+                total_delegated: validator_2_delegations
+                    .iter()
+                    .fold(0u64, |sum, delegation| {
+                        sum.checked_add(delegation.lamports_delegated).unwrap()
+                    }),
+                maybe_tip_distribution_meta: Some(TipDistributionMeta {
+                    merkle_root_upload_authority,
+                    tip_distribution_pubkey: tda_2_fields.0,
+                    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_pubkey == expected_delegation.stake_account_pubkey)
+                    .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 {
+        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(&from_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 7da8c3275f..54c2b3141e 100644
--- a/validator/Cargo.toml
+++ b/validator/Cargo.toml
@@ -54,6 +54,7 @@ solana-test-validator = { path = "../test-validator", version = "=1.13.5" }
 solana-version = { path = "../version", version = "=1.13.5" }
 solana-vote-program = { path = "../programs/vote", version = "=1.13.5" }
 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 dd12f61cf6..bc873a8027 100644
--- a/validator/src/bootstrap.rs
+++ b/validator/src/bootstrap.rs
@@ -386,12 +386,13 @@ fn get_highest_local_snapshot_hash(
     snapshot_archives_dir: impl AsRef<Path>,
 ) -> Option<(Slot, Hash)> {
     if let Some(full_snapshot_info) =
-        snapshot_utils::get_highest_full_snapshot_archive_info(&snapshot_archives_dir)
+        snapshot_utils::get_highest_full_snapshot_archive_info(&snapshot_archives_dir, None)
     {
         if let Some(incremental_snapshot_info) =
             snapshot_utils::get_highest_incremental_snapshot_archive_info(
                 &snapshot_archives_dir,
                 full_snapshot_info.slot(),
+                None,
             )
         {
             Some((
diff --git a/validator/src/dashboard.rs b/validator/src/dashboard.rs
index ec94e0d5a7..684cc67008 100644
--- a/validator/src/dashboard.rs
+++ b/validator/src/dashboard.rs
@@ -275,6 +275,7 @@ fn get_validator_stats(
         Err(err) => {
             if let client_error::ClientErrorKind::RpcError(
                 rpc_request::RpcError::RpcResponseError {
+                    request_id: _,
                     code: _,
                     message: _,
                     data:
diff --git a/validator/src/main.rs b/validator/src/main.rs
index 9aca6a830b..ba5e4ffe76 100644
--- a/validator/src/main.rs
+++ b/validator/src/main.rs
@@ -26,7 +26,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},
@@ -89,6 +91,7 @@ use {
         sync::{Arc, RwLock},
         time::{Duration, SystemTime},
     },
+    tonic::transport::Endpoint,
 };
 
 #[cfg(not(target_env = "msvc"))]
@@ -1725,6 +1728,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")
+        )
         .after_help("The default subcommand is run")
         .subcommand(
             SubCommand::with_name("exit")
@@ -2480,6 +2577,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,
@@ -2603,6 +2782,12 @@ pub fn main() {
         tpu_coalesce_ms,
         no_wait_for_vote_to_start_leader: matches.is_present("no_wait_for_vote_to_start_leader"),
         accounts_shrink_ratio,
+        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")),
         ..ValidatorConfig::default()
     };
 
@@ -3058,3 +3243,58 @@ fn process_account_indexes(matches: &ArgMatches) -> AccountSecondaryIndexes {
         indexes: account_indexes,
     }
 }
+
+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
+            }),
+        },
+    }
+}