diff --git a/evm_loader/Cargo.lock b/evm_loader/Cargo.lock index 4b2ac3410..072bdc8f4 100644 --- a/evm_loader/Cargo.lock +++ b/evm_loader/Cargo.lock @@ -543,9 +543,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2 1.0.66", "quote 1.0.32", @@ -903,9 +903,9 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bytestring" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" dependencies = [ "bytes", ] @@ -952,9 +952,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" dependencies = [ "serde", ] @@ -2640,9 +2640,9 @@ checksum = "9b085a4f2cde5781fc4b1717f2e86c62f5cda49de7ba99a7c2eae02b61c9064c" [[package]] name = "local-channel" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a493488de5f18c8ffcba89eebb8532ffc562dc400490eb65b84893fae0b178" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" dependencies = [ "futures-core", "futures-sink", @@ -2651,9 +2651,9 @@ dependencies = [ [[package]] name = "local-waker" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" @@ -2876,8 +2876,8 @@ dependencies = [ "num-traits", "shank", "solana-program", - "spl-associated-token-account 2.2.0", - "spl-token 4.0.0", + "spl-associated-token-account 1.1.3", + "spl-token 3.5.0", "thiserror", ] @@ -3197,11 +3197,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ - "num_enum_derive 0.7.0", + "num_enum_derive 0.7.1", ] [[package]] @@ -3230,9 +3230,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2 1.0.66", @@ -3621,9 +3621,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" dependencies = [ "bytes", "rand 0.8.5", @@ -4274,9 +4274,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "indexmap 2.0.0", "itoa", @@ -5892,7 +5892,7 @@ dependencies = [ "bytemuck", "num-derive 0.4.1", "num-traits", - "num_enum 0.7.0", + "num_enum 0.7.1", "solana-program", "solana-zk-token-sdk", "spl-memo 4.0.0", @@ -6674,9 +6674,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom 0.2.9", ] diff --git a/evm_loader/lib/src/types/mod.rs b/evm_loader/lib/src/types/mod.rs index 40954cebf..f446d7309 100644 --- a/evm_loader/lib/src/types/mod.rs +++ b/evm_loader/lib/src/types/mod.rs @@ -181,3 +181,68 @@ pub struct GetHolderRequest { pub pubkey: Pubkey, pub slot: Option, } + +#[cfg(test)] +mod tests { + use crate::types::tracer_ch_common::RevisionMap; + + #[test] + fn test_build_ranges_empty() { + let results = Vec::new(); + let exp = Vec::new(); + let res = RevisionMap::build_ranges(results); + assert_eq!(res, exp); + } + + #[test] + fn test_build_ranges_single_element() { + let results = vec![(1u64, String::from("Rev1"))]; + let exp = vec![(1u64, 2u64, String::from("Rev1"))]; + let res = RevisionMap::build_ranges(results); + assert_eq!(res, exp); + } + + #[test] + fn test_build_ranges_multiple_elements_different_revision() { + let results = vec![ + (222222222u64, String::from("Rev1")), + (333333333u64, String::from("Rev2")), + (444444444u64, String::from("Rev3")), + ]; + + let exp = vec![ + (222222222u64, 333333333u64, String::from("Rev1")), + (333333334u64, 444444444u64, String::from("Rev2")), + (444444445u64, 444444445u64, String::from("Rev3")), + ]; + let res = RevisionMap::build_ranges(results); + + assert_eq!(res, exp); + } + + #[test] + fn test_rangemap() { + let ranges = vec![ + (123456780, 123456788, String::from("Rev1")), + (123456789, 123456793, String::from("Rev2")), + (123456794, 123456799, String::from("Rev3")), + ]; + let map = RevisionMap::new(ranges); + + assert_eq!(map.get(123456779), None); // Below the bottom bound of the first range + + assert_eq!(map.get(123456780), Some(String::from("Rev1"))); // The bottom bound of the first range + assert_eq!(map.get(123456785), Some(String::from("Rev1"))); // Within the first range + assert_eq!(map.get(123456788), Some(String::from("Rev1"))); // The top bound of the first range + + assert_eq!(map.get(123456793), Some(String::from("Rev2"))); // The bottom bound of the second range + assert_eq!(map.get(123456790), Some(String::from("Rev2"))); // Within the second range + assert_eq!(map.get(123456793), Some(String::from("Rev2"))); // The top bound of the second range + + assert_eq!(map.get(123456799), Some(String::from("Rev3"))); // The bottom bound of the third range + assert_eq!(map.get(123456795), Some(String::from("Rev3"))); // Within the third range + assert_eq!(map.get(123456799), Some(String::from("Rev3"))); // The top bound of the third range + + assert_eq!(map.get(123456800), None); // Beyond the top end of the last range + } +} diff --git a/evm_loader/lib/src/types/tracer_ch_common.rs b/evm_loader/lib/src/types/tracer_ch_common.rs index c7c0e54f4..d61897d25 100644 --- a/evm_loader/lib/src/types/tracer_ch_common.rs +++ b/evm_loader/lib/src/types/tracer_ch_common.rs @@ -3,6 +3,8 @@ use std::fmt; use clickhouse::Row; use serde::{Deserialize, Serialize}; use solana_sdk::{account::Account, pubkey::Pubkey}; +use std::collections::BTreeMap; +use std::time::Instant; use thiserror::Error; pub const ROOT_BLOCK_DELAY: u8 = 100; @@ -52,6 +54,13 @@ impl SlotParent { } } +// NEON_REVISION row +#[derive(Row, Deserialize)] +pub struct RevisionRow { + pub slot: u64, + pub data: Vec, +} + #[derive(Row, serde::Deserialize, Clone)] pub struct AccountRow { pub owner: Vec, @@ -129,3 +138,61 @@ pub struct EthSyncing { pub current_block: u64, pub highest_block: u64, } + +pub struct RevisionMap { + map: BTreeMap, + pub last_update: Instant, +} + +impl RevisionMap { + pub fn new(neon_revision_ranges: Vec<(u64, u64, String)>) -> Self { + let mut map = BTreeMap::new(); + + for (start, end, value) in neon_revision_ranges { + map.insert(start, value.clone()); + map.insert(end, value); + } + + let last_update = std::time::Instant::now(); + + RevisionMap { map, last_update } + } + + // When deploying a program for the first time it is now only available in the next slot (the slot after the one the deployment transaction landed in). + // When undeploying / closing a program the change is visible immediately and the very next instruction even within the transaction can not access it anymore. + // When redeploying the program becomes temporarily closed immediately and will reopen with the new version in the next slot. + pub fn build_ranges(input: Vec<(u64, String)>) -> Vec<(u64, u64, String)> { + let mut ranges = Vec::new(); + + for i in 0..input.len() { + let (start, rev) = input[i].clone(); + let end = if i < input.len() - 1 { + input[i + 1].0 - 1 + } else { + start + }; + + match i { + 0 => ranges.push((start, end + 1, rev.clone())), + _ if i == input.len() - 1 => ranges.push((start + 1, end + 1, rev.clone())), + _ => ranges.push((start + 1, end + 1, rev.clone())), + } + } + ranges + } + + pub fn get(&self, slot: u64) -> Option { + // Check if slot is less than the starting range or + // greater than the ending range + let (start, _) = self.map.iter().next()?; + let (end, _) = self.map.iter().last()?; + + if slot < *start || slot > *end { + return None; + } + + let value = self.map.range(..=slot).rev().next(); + + value.map(|(_, v)| v.clone()) + } +} diff --git a/evm_loader/lib/src/types/tracer_ch_db.rs b/evm_loader/lib/src/types/tracer_ch_db.rs index e4b9ff9df..f9140fc41 100644 --- a/evm_loader/lib/src/types/tracer_ch_db.rs +++ b/evm_loader/lib/src/types/tracer_ch_db.rs @@ -1,10 +1,10 @@ use crate::{ commands::get_neon_elf::get_elf_parameter, - types::tracer_ch_common::{AccountRow, ChError, SlotParent, ROOT_BLOCK_DELAY}, + types::tracer_ch_common::{AccountRow, ChError, RevisionRow, SlotParent, ROOT_BLOCK_DELAY}, }; use super::{ - tracer_ch_common::{ChResult, EthSyncStatus, EthSyncing, SlotParentRooted}, + tracer_ch_common::{ChResult, EthSyncStatus, EthSyncing, RevisionMap, SlotParentRooted}, ChDbConfig, }; @@ -586,6 +586,39 @@ impl ClickHouseDb { } } + pub async fn get_neon_revisions(&self, pubkey: &Pubkey) -> ChResult { + let query = r#"SELECT slot, data + FROM events.update_account_distributed + WHERE + pubkey = ? + ORDER BY + slot ASC, + write_version ASC"#; + + let pubkey_str = format!("{:?}", pubkey.to_bytes()); + let rows: Vec = self + .client + .query(query) + .bind(pubkey_str) + .fetch_all() + .await?; + + let mut results: Vec<(u64, String)> = Vec::new(); + + for row in rows { + let neon_revision = get_elf_parameter(&row.data, "NEON_REVISION").map_err(|e| { + ChError::Db(clickhouse::error::Error::Custom(format!( + "Failed to get NEON_REVISION, error: {:?}", + e + ))) + })?; + results.push((row.slot, neon_revision)); + } + let ranges = RevisionMap::build_ranges(results); + + Ok(RevisionMap::new(ranges)) + } + pub async fn get_slot_by_blockhash(&self, blockhash: &str) -> ChResult { let query = r#"SELECT slot FROM events.notify_block_distributed