From 196b400e15805488ced230af99e6a7c4fecfafa8 Mon Sep 17 00:00:00 2001 From: galactus <96341601+godmodegalactus@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:59:46 +0100 Subject: [PATCH] Adding support for address lookup tables (#319) * Initial implementation of address lookup tables * Implementing fetching of address lookup tables * Use address lookup tables to calculate account prio fees * Adding some logs and renaming classes * preloading all the alts correctly * Changes after groovies review * Fixing issues after rebase --- Cargo.lock | 177 +++++++++----- Cargo.toml | 6 +- address_lookup_tables/Cargo.toml | 43 ++++ .../src/address_lookup_table_store.rs | 229 ++++++++++++++++++ address_lookup_tables/src/lib.rs | 1 + blockstore/Cargo.toml | 2 +- .../block_stores/postgres/postgres_block.rs | 1 + .../postgres/postgres_block_store_writer.rs | 1 + .../postgres/postgres_transaction.rs | 1 + cluster-endpoints/Cargo.toml | 2 +- cluster-endpoints/src/grpc_subscription.rs | 6 + .../src/rpc_polling/poll_blocks.rs | 7 + core/Cargo.toml | 2 +- core/src/structures/produced_block.rs | 2 + .../traits/address_lookup_table_interface.rs | 15 ++ core/src/traits/mod.rs | 1 + lite-rpc/Cargo.toml | 3 +- lite-rpc/src/cli.rs | 15 ++ lite-rpc/src/lib.rs | 3 + lite-rpc/src/main.rs | 42 +++- prioritization_fees/Cargo.toml | 2 +- .../src/account_prio_service.rs | 11 +- prioritization_fees/src/account_priofees.rs | 46 +++- .../Cargo.toml | 2 +- quic-forward-proxy/Cargo.toml | 2 +- services/Cargo.toml | 2 +- stake_vote/Cargo.toml | 2 +- 27 files changed, 544 insertions(+), 82 deletions(-) create mode 100644 address_lookup_tables/Cargo.toml create mode 100644 address_lookup_tables/src/address_lookup_table_store.rs create mode 100644 address_lookup_tables/src/lib.rs create mode 100644 core/src/traits/address_lookup_table_interface.rs diff --git a/Cargo.lock b/Cargo.lock index 9358f037..aaf92d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -1214,9 +1214,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" dependencies = [ "darling_core", "darling_macro", @@ -1224,9 +1224,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" dependencies = [ "fnv", "ident_case", @@ -1238,9 +1238,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" dependencies = [ "darling_core", "quote", @@ -1873,7 +1873,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.1", + "indexmap 2.2.2", "slab", "tokio", "tokio-util", @@ -1936,9 +1936,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" [[package]] name = "histogram" @@ -2079,9 +2079,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2144,9 +2144,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2410,9 +2410,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libredox" @@ -2519,6 +2519,7 @@ dependencies = [ "quinn", "serde", "serde_json", + "solana-lite-rpc-address-lookup-tables", "solana-lite-rpc-blockstore", "solana-lite-rpc-cluster-endpoints", "solana-lite-rpc-core", @@ -2646,9 +2647,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -2767,6 +2768,12 @@ dependencies = [ "num-traits 0.2.17", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" @@ -2846,7 +2853,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.5", "libc", ] @@ -3068,7 +3075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.1", + "indexmap 2.2.2", ] [[package]] @@ -3252,7 +3259,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -3621,9 +3628,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "async-compression", "base64 0.21.7", @@ -3648,6 +3655,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls", @@ -3657,7 +3665,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", "winreg", ] @@ -3743,9 +3751,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -4155,6 +4163,27 @@ dependencies = [ "zstd", ] +[[package]] +name = "solana-address-lookup-table-program" +version = "1.17.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89cd9fd1668735eab8c83407bde1d073a35c0486c0d9f1afc9ef75b9b726f94" +dependencies = [ + "bincode", + "bytemuck", + "log", + "num-derive 0.3.3", + "num-traits 0.2.17", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + [[package]] name = "solana-clap-utils" version = "1.17.15" @@ -4183,7 +4212,7 @@ dependencies = [ "dashmap 4.0.2", "futures", "futures-util", - "indexmap 2.2.1", + "indexmap 2.2.2", "indicatif", "log", "quinn", @@ -4229,7 +4258,7 @@ dependencies = [ "bincode", "crossbeam-channel", "futures-util", - "indexmap 2.2.1", + "indexmap 2.2.2", "log", "rand 0.8.5", "rayon", @@ -4283,6 +4312,43 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "solana-lite-rpc-address-lookup-tables" +version = "0.2.4" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "bincode", + "bs58", + "bytes", + "chrono", + "dashmap 5.5.3", + "futures", + "itertools 0.10.5", + "lazy_static", + "log", + "prometheus", + "quinn", + "rustls", + "serde", + "serde_json", + "solana-account-decoder", + "solana-address-lookup-table-program", + "solana-client", + "solana-lite-rpc-core", + "solana-net-utils", + "solana-pubsub-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", + "solana-streamer", + "solana-transaction-status", + "solana-version", + "thiserror", + "tokio", +] + [[package]] name = "solana-lite-rpc-blockstore" version = "0.2.4" @@ -4921,7 +4987,7 @@ dependencies = [ "crossbeam-channel", "futures-util", "histogram", - "indexmap 2.2.1", + "indexmap 2.2.2", "itertools 0.10.5", "libc", "log", @@ -4966,7 +5032,7 @@ dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 2.2.1", + "indexmap 2.2.2", "indicatif", "log", "rayon", @@ -5158,9 +5224,9 @@ dependencies = [ [[package]] name = "spl-discriminator-derive" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" +checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", "spl-discriminator-syn", @@ -5169,9 +5235,9 @@ dependencies = [ [[package]] name = "spl-discriminator-syn" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2" +checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" dependencies = [ "proc-macro2", "quote", @@ -5226,9 +5292,9 @@ dependencies = [ [[package]] name = "spl-program-error-derive" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c" +checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" dependencies = [ "proc-macro2", "quote", @@ -5437,13 +5503,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] @@ -5504,12 +5569,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -5524,10 +5590,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -5567,9 +5634,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -5674,7 +5741,7 @@ dependencies = [ "tokio", "tokio-rustls", "tungstenite", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", ] [[package]] @@ -5713,18 +5780,18 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.1", + "indexmap 2.2.2", "toml_datetime", "winnow", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.1", + "indexmap 2.2.2", "toml_datetime", "winnow", ] @@ -6157,9 +6224,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "which" @@ -6357,9 +6424,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.35" +version = "0.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" +checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index f0fa94cc..1f3f864e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,8 @@ members = [ "cluster-endpoints", "blockstore", "prioritization_fees", - "bench" + "bench", + "address_lookup_tables" ] [workspace.package] @@ -33,6 +34,8 @@ solana-streamer = "~1.17.15" solana-account-decoder = "~1.17.15" solana-ledger = "~1.17.15" solana-program = "~1.17.15" +solana-address-lookup-table-program = "~1.17.15" + itertools = "0.10.5" rangetools = "0.1.4" serde = { version = "1.0.160", features = ["derive"] } @@ -71,6 +74,7 @@ solana-lite-rpc-cluster-endpoints = {path = "cluster-endpoints", version="0.2.4" solana-lite-rpc-blockstore = {path = "blockstore", version="0.2.4"} solana-lite-rpc-stakevote = {path = "stake_vote", version="0.2.4"} solana-lite-rpc-prioritization-fees = {path = "prioritization_fees", version="0.2.4"} +solana-lite-rpc-address-lookup-tables = {path = "address_lookup_tables", version="0.2.4"} async-trait = "0.1.68" yellowstone-grpc-client = { git = "https://github.com/rpcpool/yellowstone-grpc.git", tag = "v1.12.0+solana.1.17.15" } diff --git a/address_lookup_tables/Cargo.toml b/address_lookup_tables/Cargo.toml new file mode 100644 index 00000000..ee51705b --- /dev/null +++ b/address_lookup_tables/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "solana-lite-rpc-address-lookup-tables" +version = "0.2.4" +edition = "2021" +description = "Library to save and update address lookup tables in lite-rpc" +rust-version = "1.73.0" +repository = "https://github.com/blockworks-foundation/lite-rpc" +license = "AGPL" + +[dependencies] +solana-sdk = { workspace = true } +solana-rpc-client-api = { workspace = true } +solana-transaction-status = { workspace = true } +solana-version = { workspace = true } +solana-client = { workspace = true } +solana-net-utils = { workspace = true } +solana-pubsub-client = { workspace = true } +solana-rpc-client = { workspace = true } +solana-streamer = { workspace = true } +solana-account-decoder = { workspace = true } +solana-address-lookup-table-program = { workspace = true } + +serde = { workspace = true } +serde_json = { workspace = true } +tokio = "1.*" +bincode = { workspace = true } +bs58 = { workspace = true } +base64 = { workspace = true } +thiserror = { workspace = true } +futures = { workspace = true } +bytes = { workspace = true } +anyhow = { workspace = true } +log = { workspace = true } +dashmap = { workspace = true } +quinn = { workspace = true } +chrono = { workspace = true } +rustls = { workspace = true } +async-trait = { workspace = true } +itertools = { workspace = true } +prometheus = { workspace = true } +lazy_static = { workspace = true } + +solana-lite-rpc-core = { workspace = true } \ No newline at end of file diff --git a/address_lookup_tables/src/address_lookup_table_store.rs b/address_lookup_tables/src/address_lookup_table_store.rs new file mode 100644 index 00000000..46685fc6 --- /dev/null +++ b/address_lookup_tables/src/address_lookup_table_store.rs @@ -0,0 +1,229 @@ +use async_trait::async_trait; +use dashmap::DashMap; +use itertools::Itertools; +use prometheus::{opts, register_int_gauge, IntGauge}; +use serde::{Deserialize, Serialize}; +use solana_address_lookup_table_program::state::AddressLookupTable; +use solana_lite_rpc_core::traits::address_lookup_table_interface::AddressLookupTableInterface; +use solana_rpc_client::nonblocking::rpc_client::RpcClient; +use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; +use std::{sync::Arc, time::Duration}; + +lazy_static::lazy_static! { + static ref LRPC_ALTS_IN_STORE: IntGauge = + register_int_gauge!(opts!("literpc_alts_stored", "Alts stored in literpc")).unwrap(); +} + +#[derive(Clone)] +pub struct AddressLookupTableStore { + rpc_client: Arc, + pub map: Arc>>, +} + +impl AddressLookupTableStore { + pub fn new(rpc_client: Arc) -> Self { + Self { + rpc_client, + map: Arc::new(DashMap::new()), + } + } + + async fn load_alts_list(&self, alts_list: &[Pubkey]) { + log::trace!("Preloading {} ALTs", alts_list.len()); + for batches in alts_list.chunks(1000).map(|x| x.to_vec()) { + let tasks = batches.chunks(100).map(|batch| { + let batch = batch.to_vec(); + let rpc_client = self.rpc_client.clone(); + let this = self.clone(); + tokio::spawn(async move { + let data = rpc_client + .get_multiple_accounts_with_commitment( + &batch, + CommitmentConfig::processed(), + ) + .await; + + match data { + Ok(multiple_accounts) => { + for (index, acc) in multiple_accounts.value.iter().enumerate() { + if let Some(acc) = acc { + this.save_account(&batch[index], &acc.data); + } + } + } + Err(e) => { + log::error!( + "error loading {} alts with error {}, skipping lookup for this batch and continue", + batch.len(), + e.to_string() + ); + } + }; + }) + }); + if tokio::time::timeout(Duration::from_secs(60), futures::future::join_all(tasks)) + .await + .is_err() + { + log::error!( + "timeout loading {} alts, skipping lookup for this batch and continue", + alts_list.len() + ); + } + } + LRPC_ALTS_IN_STORE.set(self.map.len() as i64); + } + + pub fn save_account(&self, address: &Pubkey, data: &[u8]) { + let lookup_table = AddressLookupTable::deserialize(data).unwrap(); + if self + .map + .insert(*address, lookup_table.addresses.to_vec()) + .is_none() + { + LRPC_ALTS_IN_STORE.inc(); + } + drop(lookup_table); + } + + pub async fn reload_if_necessary( + &self, + alt_messages: &[&solana_sdk::message::v0::MessageAddressTableLookup], + ) { + let accounts_to_load = alt_messages + .iter() + .filter_map(|alt| match self.map.get(&alt.account_key) { + Some(alt_data) => { + let size = alt_data.len() as u8; + if alt.readonly_indexes.iter().any(|x| *x >= size) + || alt.writable_indexes.iter().any(|x| *x >= size) + { + Some(alt.account_key) + } else { + None + } + } + None => Some(alt.account_key), + }) + .collect_vec(); + self.load_alts_list(&accounts_to_load).await; + } + + pub async fn reload_alt_account(&self, address: &Pubkey) { + log::info!("Reloading {address:?}"); + + let account = match self + .rpc_client + .get_account_with_commitment(address, CommitmentConfig::processed()) + .await + { + Ok(acc) => acc.value, + Err(e) => { + log::error!( + "Error for fetching address lookup table {} error :{}", + address.to_string(), + e.to_string() + ); + None + } + }; + match account { + Some(account) => { + self.save_account(address, &account.data); + } + None => { + log::error!("Cannot find address lookup table {}", address.to_string()); + } + } + } + + async fn get_accounts_in_address_lookup_table( + &self, + alt: &Pubkey, + accounts: &[u8], + ) -> Option> { + let alt_account = self.map.get(alt); + match alt_account { + Some(alt_account) => Some( + accounts + .iter() + .map(|i| alt_account[*i as usize]) + .collect_vec(), + ), + None => { + log::error!("address lookup table {} was not found", alt); + None + } + } + } + + pub async fn get_accounts(&self, alt: &Pubkey, accounts: &[u8]) -> Vec { + match self + .get_accounts_in_address_lookup_table(alt, accounts) + .await + { + Some(x) => x, + None => { + // forget alt for now, start loading it for next blocks + // loading should be on its way + vec![] + } + } + } + + pub fn serialize_binary(&self) -> Vec { + bincode::serialize::(&BinaryALTData::new(&self.map)).unwrap() + } + + /// To load binary ALT file at the startup + pub fn load_binary(&self, binary_data: Vec) { + let binary_alt_data = bincode::deserialize::(&binary_data).unwrap(); + for (alt, accounts) in binary_alt_data.data.iter() { + self.map.insert(*alt, accounts.clone()); + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct BinaryALTData { + data: Vec<(Pubkey, Vec)>, +} + +impl BinaryALTData { + pub fn new(map: &Arc>>) -> Self { + let data = map + .iter() + .map(|x| (*x.key(), x.value().clone())) + .collect_vec(); + Self { data } + } +} + +#[async_trait] +impl AddressLookupTableInterface for AddressLookupTableStore { + async fn resolve_addresses_from_lookup_table( + &self, + message_address_table_lookup: &solana_sdk::message::v0::MessageAddressTableLookup, + ) -> (Vec, Vec) { + ( + self.get_accounts( + &message_address_table_lookup.account_key, + &message_address_table_lookup.writable_indexes, + ) + .await, + self.get_accounts( + &message_address_table_lookup.account_key, + &message_address_table_lookup.readonly_indexes, + ) + .await, + ) + } + + async fn reload_if_necessary( + &self, + message_address_table_lookups: &[&solana_sdk::message::v0::MessageAddressTableLookup], + ) { + self.reload_if_necessary(message_address_table_lookups) + .await; + } +} diff --git a/address_lookup_tables/src/lib.rs b/address_lookup_tables/src/lib.rs new file mode 100644 index 00000000..dc6bce40 --- /dev/null +++ b/address_lookup_tables/src/lib.rs @@ -0,0 +1 @@ +pub mod address_lookup_table_store; diff --git a/blockstore/Cargo.toml b/blockstore/Cargo.toml index 96e235b8..8462f3d7 100644 --- a/blockstore/Cargo.toml +++ b/blockstore/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-blockstore" version = "0.2.4" edition = "2021" description = "Store and proved blocks in PostgreSQL DB and via Yellowstone Faithful" -rust-version = "1.70.0" +rust-version = "1.73.0" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" diff --git a/blockstore/src/block_stores/postgres/postgres_block.rs b/blockstore/src/block_stores/postgres/postgres_block.rs index 8b3285f1..b51697eb 100644 --- a/blockstore/src/block_stores/postgres/postgres_block.rs +++ b/blockstore/src/block_stores/postgres/postgres_block.rs @@ -225,6 +225,7 @@ mod tests { message: "message".to_string(), writable_accounts: vec![], readable_accounts: vec![], + address_lookup_tables: vec![], } } } diff --git a/blockstore/src/block_stores/postgres/postgres_block_store_writer.rs b/blockstore/src/block_stores/postgres/postgres_block_store_writer.rs index 13bf647c..abbad945 100644 --- a/blockstore/src/block_stores/postgres/postgres_block_store_writer.rs +++ b/blockstore/src/block_stores/postgres/postgres_block_store_writer.rs @@ -391,6 +391,7 @@ mod tests { message: "some message".to_string(), writable_accounts: vec![], readable_accounts: vec![], + address_lookup_tables: vec![], } } } diff --git a/blockstore/src/block_stores/postgres/postgres_transaction.rs b/blockstore/src/block_stores/postgres/postgres_transaction.rs index f3374906..353c9768 100644 --- a/blockstore/src/block_stores/postgres/postgres_transaction.rs +++ b/blockstore/src/block_stores/postgres/postgres_transaction.rs @@ -59,6 +59,7 @@ impl PostgresTransaction { readable_accounts: vec![], writable_accounts: vec![], is_vote: false, + address_lookup_tables: vec![], } } diff --git a/cluster-endpoints/Cargo.toml b/cluster-endpoints/Cargo.toml index dfc47458..0a89f3fb 100644 --- a/cluster-endpoints/Cargo.toml +++ b/cluster-endpoints/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-cluster-endpoints" version = "0.2.4" edition = "2021" description = "Core classes and methods used by solana lite rpc" -rust-version = "1.70.0" +rust-version = "1.73.0" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" diff --git a/cluster-endpoints/src/grpc_subscription.rs b/cluster-endpoints/src/grpc_subscription.rs index a5ae34c4..bca4fe45 100644 --- a/cluster-endpoints/src/grpc_subscription.rs +++ b/cluster-endpoints/src/grpc_subscription.rs @@ -205,6 +205,11 @@ pub fn from_grpc_block_update( .map(|(_, pk)| *pk) .collect(); + let address_lookup_tables = message + .address_table_lookups() + .map(|x| x.to_vec()) + .unwrap_or_default(); + Some(TransactionInfo { signature: signature.to_string(), is_vote: is_vote_transaction, @@ -216,6 +221,7 @@ pub fn from_grpc_block_update( message: BASE64.encode(message.serialize()), readable_accounts, writable_accounts, + address_lookup_tables, }) }) .collect(); diff --git a/cluster-endpoints/src/rpc_polling/poll_blocks.rs b/cluster-endpoints/src/rpc_polling/poll_blocks.rs index 154df72e..d68910b4 100644 --- a/cluster-endpoints/src/rpc_polling/poll_blocks.rs +++ b/cluster-endpoints/src/rpc_polling/poll_blocks.rs @@ -286,6 +286,12 @@ pub fn from_ui_block( } } + let address_lookup_tables = tx + .message + .address_table_lookups() + .map(|x| x.to_vec()) + .unwrap_or_default(); + Some(TransactionInfo { signature, is_vote: is_vote_transaction, @@ -297,6 +303,7 @@ pub fn from_ui_block( message, readable_accounts, writable_accounts, + address_lookup_tables, }) }) .collect(); diff --git a/core/Cargo.toml b/core/Cargo.toml index 40eff23e..d0fa6dba 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-core" version = "0.2.4" edition = "2021" description = "Core classes and methods used by solana lite rpc" -rust-version = "1.70.0" +rust-version = "1.73.0" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" diff --git a/core/src/structures/produced_block.rs b/core/src/structures/produced_block.rs index 75c92625..746f28e2 100644 --- a/core/src/structures/produced_block.rs +++ b/core/src/structures/produced_block.rs @@ -1,4 +1,5 @@ use solana_sdk::commitment_config::CommitmentConfig; +use solana_sdk::message::v0::MessageAddressTableLookup; use solana_sdk::pubkey::Pubkey; use solana_sdk::{slot_history::Slot, transaction::TransactionError}; use solana_transaction_status::Reward; @@ -15,6 +16,7 @@ pub struct TransactionInfo { pub message: String, pub writable_accounts: Vec, pub readable_accounts: Vec, + pub address_lookup_tables: Vec, } // TODO try to remove Clone diff --git a/core/src/traits/address_lookup_table_interface.rs b/core/src/traits/address_lookup_table_interface.rs new file mode 100644 index 00000000..5125d86f --- /dev/null +++ b/core/src/traits/address_lookup_table_interface.rs @@ -0,0 +1,15 @@ +use async_trait::async_trait; +use solana_sdk::{message::v0::MessageAddressTableLookup, pubkey::Pubkey}; + +#[async_trait] +pub trait AddressLookupTableInterface: Send + Sync { + async fn resolve_addresses_from_lookup_table( + &self, + message_address_table_lookup: &MessageAddressTableLookup, + ) -> (Vec, Vec); + + async fn reload_if_necessary( + &self, + message_address_table_lookups: &[&MessageAddressTableLookup], + ); +} diff --git a/core/src/traits/mod.rs b/core/src/traits/mod.rs index fd724272..1a5a03c4 100644 --- a/core/src/traits/mod.rs +++ b/core/src/traits/mod.rs @@ -1,2 +1,3 @@ +pub mod address_lookup_table_interface; pub mod leaders_fetcher_interface; pub mod subscription_sink; diff --git a/lite-rpc/Cargo.toml b/lite-rpc/Cargo.toml index f47165a0..ab6f5ade 100644 --- a/lite-rpc/Cargo.toml +++ b/lite-rpc/Cargo.toml @@ -3,7 +3,7 @@ name = "lite-rpc" version = "0.2.4" edition = "2021" description = "A lite version of solana rpc to send and confirm transactions" -rust-version = "1.70.0" +rust-version = "1.73.0" default-run = "lite-rpc" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" @@ -49,6 +49,7 @@ solana-lite-rpc-services = { workspace = true } solana-lite-rpc-cluster-endpoints = { workspace = true } solana-lite-rpc-blockstore = { workspace = true } solana-lite-rpc-prioritization-fees = { workspace = true } +solana-lite-rpc-address-lookup-tables = { workspace = true } [dev-dependencies] bench = { path = "../bench" } diff --git a/lite-rpc/src/cli.rs b/lite-rpc/src/cli.rs index 28f8b8e3..5d579d6d 100644 --- a/lite-rpc/src/cli.rs +++ b/lite-rpc/src/cli.rs @@ -68,7 +68,14 @@ pub struct Config { #[serde(default)] pub postgres: Option, + #[serde(default)] pub max_number_of_connection: Option, + + #[serde(default)] + pub address_lookup_tables_binary: Option, + + #[serde(default)] + pub enable_address_lookup_tables: Option, } impl Config { @@ -182,6 +189,14 @@ impl Config { config.postgres = postgres_logger::PostgresSessionConfig::new_from_env()?.or(config.postgres); + config.enable_address_lookup_tables = env::var("ENABLE_ADDRESS_LOOKUP_TABLES") + .map(|value| value.parse::().unwrap()) + .ok() + .or(config.enable_address_lookup_tables); + + config.address_lookup_tables_binary = env::var("ADDRESS_LOOKUP_TABLES_BINARY") + .ok() + .or(config.address_lookup_tables_binary); Ok(config) } diff --git a/lite-rpc/src/lib.rs b/lite-rpc/src/lib.rs index ff4e6937..a2c24f7f 100644 --- a/lite-rpc/src/lib.rs +++ b/lite-rpc/src/lib.rs @@ -44,3 +44,6 @@ pub const NB_SLOTS_TRANSACTIONS_TO_CACHE: u64 = 1000; #[from_env] pub const MAX_NB_OF_CONNECTIONS_WITH_LEADERS: usize = 8; + +#[from_env] +pub const ENABLE_ADDRESS_LOOKUP_TABLES: bool = false; diff --git a/lite-rpc/src/main.rs b/lite-rpc/src/main.rs index 7e4d4a91..264c24cc 100644 --- a/lite-rpc/src/main.rs +++ b/lite-rpc/src/main.rs @@ -9,6 +9,7 @@ use lite_rpc::postgres_logger::PostgresLogger; use lite_rpc::service_spawner::ServiceSpawner; use lite_rpc::{DEFAULT_MAX_NUMBER_OF_TXS_IN_QUEUE, MAX_NB_OF_CONNECTIONS_WITH_LEADERS}; use log::{debug, info}; +use solana_lite_rpc_address_lookup_tables::address_lookup_table_store::AddressLookupTableStore; use solana_lite_rpc_blockstore::history::History; use solana_lite_rpc_cluster_endpoints::endpoint_stremers::EndpointStreaming; use solana_lite_rpc_cluster_endpoints::grpc_subscription::create_grpc_subscription; @@ -31,6 +32,7 @@ use solana_lite_rpc_core::structures::{ epoch::EpochCache, identity_stakes::IdentityStakes, notifications::NotificationSender, produced_block::ProducedBlock, }; +use solana_lite_rpc_core::traits::address_lookup_table_interface::AddressLookupTableInterface; use solana_lite_rpc_core::types::BlockStream; use solana_lite_rpc_core::AnyhowJoinHandle; use solana_lite_rpc_prioritization_fees::account_prio_service::AccountPrioService; @@ -50,6 +52,7 @@ use solana_sdk::signer::Signer; use std::net::{SocketAddr, ToSocketAddrs}; use std::sync::Arc; use std::time::Duration; +use tokio::io::AsyncReadExt; use tokio::sync::mpsc; use tokio::sync::RwLock; use tokio::time::{timeout, Instant}; @@ -115,6 +118,8 @@ pub async fn start_lite_rpc(args: Config, rpc_client: Arc) -> anyhow: transaction_retry_after_secs, quic_proxy_addr, use_grpc, + enable_address_lookup_tables, + address_lookup_tables_binary, .. } = args; @@ -146,12 +151,6 @@ pub async fn start_lite_rpc(args: Config, rpc_client: Arc) -> anyhow: }) .collect(), )? - - // create_grpc_subscription( - // rpc_client.clone(), - // grpc_addr.clone(), - // GRPC_VERSION.to_string(), - // )? } else { info!("Creating RPC poll subscription..."); create_json_rpc_polling_subscription(rpc_client.clone(), NUM_PARALLEL_TASKS_DEFAULT)? @@ -203,8 +202,37 @@ pub async fn start_lite_rpc(args: Config, rpc_client: Arc) -> anyhow: let (block_priofees_task, block_priofees_service) = start_block_priofees_task(blocks_notifier.resubscribe(), 100); + let address_lookup_tables: Option> = + if enable_address_lookup_tables.unwrap_or_default() { + log::info!("ALTs enabled"); + let alts_store = AddressLookupTableStore::new(rpc_client.clone()); + if let Some(address_lookup_tables_binary) = address_lookup_tables_binary { + match tokio::fs::File::open(address_lookup_tables_binary).await { + Ok(mut alts_file) => { + let mut buf = vec![]; + alts_file.read_to_end(&mut buf).await.unwrap(); + alts_store.load_binary(buf); + + log::info!("{} ALTs loaded from binary file", alts_store.map.len()); + } + Err(e) => { + log::error!("Error loading address lookup tables binary : {e:?}"); + anyhow::bail!(e.to_string()); + } + } + } + Some(Arc::new(alts_store)) + } else { + log::info!("ALTs disabled"); + None + }; + let (account_priofees_task, account_priofees_service) = - AccountPrioService::start_account_priofees_task(blocks_notifier.resubscribe(), 100); + AccountPrioService::start_account_priofees_task( + blocks_notifier.resubscribe(), + 100, + address_lookup_tables, + ); let (notification_channel, postgres) = start_postgres(postgres).await?; diff --git a/prioritization_fees/Cargo.toml b/prioritization_fees/Cargo.toml index 729decec..b65f6334 100644 --- a/prioritization_fees/Cargo.toml +++ b/prioritization_fees/Cargo.toml @@ -17,4 +17,4 @@ jsonrpsee = { workspace = true } tracing-subscriber = { workspace = true } prometheus = { workspace = true } lazy_static = { workspace = true } -tokio = { version = "1.28.2", features = ["full"]} +tokio = { version = "1.28.2", features = ["full"]} \ No newline at end of file diff --git a/prioritization_fees/src/account_prio_service.rs b/prioritization_fees/src/account_prio_service.rs index 813d0318..cb9fd71f 100644 --- a/prioritization_fees/src/account_prio_service.rs +++ b/prioritization_fees/src/account_prio_service.rs @@ -1,4 +1,8 @@ -use solana_lite_rpc_core::types::BlockStream; +use std::sync::Arc; + +use solana_lite_rpc_core::{ + traits::address_lookup_table_interface::AddressLookupTableInterface, types::BlockStream, +}; use solana_sdk::{pubkey::Pubkey, slot_history::Slot}; use tokio::{sync::broadcast::Sender, task::JoinHandle}; @@ -18,8 +22,9 @@ impl AccountPrioService { pub fn start_account_priofees_task( mut block_stream: BlockStream, slots_to_retain: usize, + address_lookup_tables_impl: Option>, ) -> (JoinHandle<()>, AccountPrioService) { - let account_store = AccountPrioStore::new(slots_to_retain); + let account_store = AccountPrioStore::new(slots_to_retain, address_lookup_tables_impl); let (priofees_update_sender, _priofees_update_receiver) = tokio::sync::broadcast::channel(64); @@ -35,7 +40,7 @@ impl AccountPrioService { continue; } - let account_fee_message = account_store.update(&block); + let account_fee_message = account_store.update(&block).await; let _ = priofees_update_sender.send(account_fee_message); } Err(Lagged(_lagged)) => { diff --git a/prioritization_fees/src/account_priofees.rs b/prioritization_fees/src/account_priofees.rs index a0958f5e..fa060770 100644 --- a/prioritization_fees/src/account_priofees.rs +++ b/prioritization_fees/src/account_priofees.rs @@ -5,7 +5,10 @@ use std::{ use dashmap::{mapref::multiple::RefMutMulti, DashMap}; use itertools::Itertools; -use solana_lite_rpc_core::structures::produced_block::ProducedBlock; +use solana_lite_rpc_core::{ + structures::produced_block::ProducedBlock, + traits::address_lookup_table_interface::AddressLookupTableInterface, +}; use solana_sdk::{pubkey::Pubkey, slot_history::Slot}; use crate::{ @@ -23,37 +26,66 @@ pub struct AccountPrioStore { pub account_by_prio_fees_writeonly: Arc>, pub number_of_slots_to_save: usize, pub last_slot: Arc, + pub address_lookup_tables_impl: Option>, } impl AccountPrioStore { - pub fn new(number_of_slots_to_save: usize) -> Self { + pub fn new( + number_of_slots_to_save: usize, + address_lookup_tables_impl: Option>, + ) -> Self { Self { account_by_prio_fees_all: Arc::new(DashMap::new()), account_by_prio_fees_writeonly: Arc::new(DashMap::new()), number_of_slots_to_save, last_slot: Arc::new(AtomicU64::new(0)), + address_lookup_tables_impl, } } - pub fn update(&self, produced_block: &ProducedBlock) -> AccountPrioFeesUpdateMessage { - // sort by ascending order + pub async fn update(&self, produced_block: &ProducedBlock) -> AccountPrioFeesUpdateMessage { + // sort by ascending order of priority let transactions = produced_block .transactions .iter() .filter(|x| !x.is_vote) .sorted_by(|a, b| a.prioritization_fees.cmp(&b.prioritization_fees)) - .rev(); + .collect_vec(); // accounts let mut accounts_by_prioritization_write: HashMap> = HashMap::new(); let mut accounts_by_prioritization_read_write: HashMap> = HashMap::new(); + + if let Some(alt_fetcher) = &self.address_lookup_tables_impl { + let alt_messages = transactions + .iter() + .flat_map(|x| &x.address_lookup_tables) + .collect_vec(); + + log::trace!("Checking to reload {} accounts", alt_messages.len()); + alt_fetcher.reload_if_necessary(&alt_messages).await; + } + for transaction in transactions { let value = PrioFeesData { priority: transaction.prioritization_fees.unwrap_or_default(), cu_consumed: transaction.cu_consumed.unwrap_or_default(), }; - for write_lock in &transaction.writable_accounts { + let mut writable_accounts = transaction.writable_accounts.clone(); + let mut readable_accounts = transaction.readable_accounts.clone(); + + if let Some(alt_fetcher) = &self.address_lookup_tables_impl { + for transaction_lookup_table in &transaction.address_lookup_tables { + let (mut alts_w, mut alts_r) = alt_fetcher + .resolve_addresses_from_lookup_table(transaction_lookup_table) + .await; + writable_accounts.append(&mut alts_w); + readable_accounts.append(&mut alts_r); + } + } + + for write_lock in &writable_accounts { match accounts_by_prioritization_write.get_mut(write_lock) { Some(acc_vec) => { acc_vec.push(value); @@ -73,7 +105,7 @@ impl AccountPrioStore { } } - for readlock in &transaction.readable_accounts { + for readlock in &readable_accounts { match accounts_by_prioritization_read_write.get_mut(readlock) { Some(acc_vec) => { acc_vec.push(value); diff --git a/quic-forward-proxy-integration-test/Cargo.toml b/quic-forward-proxy-integration-test/Cargo.toml index d2481d2a..cf7d02dc 100644 --- a/quic-forward-proxy-integration-test/Cargo.toml +++ b/quic-forward-proxy-integration-test/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-quic-forward-proxy-integration-test" version = "0.1.0" edition = "2021" description = "Integration test for quic proxy " -rust-version = "1.70.0" +rust-version = "1.73.0" #default-run = "lite-rpc" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" diff --git a/quic-forward-proxy/Cargo.toml b/quic-forward-proxy/Cargo.toml index 95dddc80..89c27260 100644 --- a/quic-forward-proxy/Cargo.toml +++ b/quic-forward-proxy/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-quic-forward-proxy" version = "0.1.0" edition = "2021" description = "A staked proxy for transaction forwarding to TPU nodes" -rust-version = "1.70.0" +rust-version = "1.73.0" #default-run = "lite-rpc" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" diff --git a/services/Cargo.toml b/services/Cargo.toml index 97b22b2d..a0ae7e47 100644 --- a/services/Cargo.toml +++ b/services/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-services" version = "0.2.4" edition = "2021" description = "Services used by solana lite rpc" -rust-version = "1.70.0" +rust-version = "1.73.0" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL" diff --git a/stake_vote/Cargo.toml b/stake_vote/Cargo.toml index 796613ca..7f4e678c 100644 --- a/stake_vote/Cargo.toml +++ b/stake_vote/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-lite-rpc-stakevote" version = "0.2.4" edition = "2021" description = "History implementations used by solana lite rpc" -rust-version = "1.70.0" +rust-version = "1.73.0" repository = "https://github.com/blockworks-foundation/lite-rpc" license = "AGPL"