diff --git a/lib/Cargo.lock b/lib/Cargo.lock index a067d117062..207591fb4b9 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -191,7 +191,7 @@ dependencies = [ "serde_json", "serde_with", "sha3", - "syn 2.0.39", + "syn 2.0.41", "tokio", "tonic", "tonic-build", @@ -205,7 +205,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -217,6 +217,7 @@ dependencies = [ "axum 0.7.2", "bitcoin", "bitcoin_hashes 0.12.0", + "chrono", "cached", "ctrlc", "dftx-rs", @@ -379,7 +380,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -423,7 +424,7 @@ dependencies = [ "bytes", "futures-util", "http 0.2.11", - "http-body 0.4.5", + "http-body 0.4.6", "hyper 0.14.27", "itoa", "matchit", @@ -483,7 +484,7 @@ dependencies = [ "bytes", "futures-util", "http 0.2.11", - "http-body 0.4.5", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -519,7 +520,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -613,7 +614,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -939,8 +940,10 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.48.5", ] @@ -1224,7 +1227,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1236,7 +1239,7 @@ dependencies = [ "codespan-reporting", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1253,7 +1256,7 @@ checksum = "587663dd5fb3d10932c8aecfe7c844db1bcf0aee93eeab08fac13dc1212c2e7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1301,7 +1304,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1323,7 +1326,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1359,9 +1362,9 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1abaf4d861455be59f64fd2b55606cb151fce304ede7165f410243ce96bde6" +checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" [[package]] name = "dftx-macro" @@ -1370,7 +1373,7 @@ source = "git+https://github.com/Jouzo/dftx-rs.git#7973f1ad240d5f7724e51c6b090bc dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1986,7 +1989,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2331,9 +2334,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http 0.2.11", @@ -2399,7 +2402,7 @@ dependencies = [ "futures-util", "h2 0.3.22", "http 0.2.11", - "http-body 0.4.5", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -2630,7 +2633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -2654,9 +2657,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" @@ -3070,9 +3073,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libloading" @@ -3288,7 +3291,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.26", + "rustix 0.38.28", ] [[package]] @@ -3570,7 +3573,7 @@ dependencies = [ "proc-macro-crate 2.0.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -3596,9 +3599,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -3838,7 +3841,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -3876,7 +3879,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -3942,7 +3945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4296,7 +4299,7 @@ checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4371,7 +4374,7 @@ dependencies = [ "futures-util", "h2 0.3.22", "http 0.2.11", - "http-body 0.4.5", + "http-body 0.4.6", "hyper 0.14.27", "hyper-rustls", "ipnet", @@ -4532,9 +4535,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", @@ -4545,9 +4548,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", @@ -4594,9 +4597,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "safe_arch" @@ -4732,7 +4735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" dependencies = [ "bitcoin_hashes 0.13.0", - "secp256k1-sys 0.9.0", + "secp256k1-sys 0.9.1", ] [[package]] @@ -4746,9 +4749,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" dependencies = [ "cc", ] @@ -4826,7 +4829,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4888,7 +4891,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5153,7 +5156,7 @@ checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5247,7 +5250,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5476,7 +5479,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5544,9 +5547,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", @@ -5646,7 +5649,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -5696,7 +5699,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5792,9 +5795,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -5827,7 +5830,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5921,7 +5924,7 @@ dependencies = [ "futures-util", "h2 0.3.22", "http 0.2.11", - "http-body 0.4.5", + "http-body 0.4.6", "hyper 0.14.27", "hyper-timeout", "percent-encoding", @@ -5981,7 +5984,7 @@ dependencies = [ "futures-core", "futures-util", "http 0.2.11", - "http-body 0.4.5", + "http-body 0.4.6", "http-range-header", "httpdate", "iri-string", @@ -6030,7 +6033,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -6144,9 +6147,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "twox-hash" @@ -6195,9 +6198,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -6405,7 +6408,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "wasm-bindgen-shared", ] @@ -6439,7 +6442,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6617,7 +6620,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.26", + "rustix 0.38.28", ] [[package]] @@ -6870,9 +6873,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.25" +version = "0.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e87b8dfbe3baffbe687eef2e164e32286eff31a5ee16463ce03d991643ec94" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" dependencies = [ "memchr", ] @@ -6904,22 +6907,22 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zerocopy" -version = "0.7.29" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.29" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -6939,7 +6942,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] diff --git a/lib/ain-evm/src/storage/block_store.rs b/lib/ain-evm/src/storage/block_store.rs index e2a816015a3..57b56a81f48 100644 --- a/lib/ain-evm/src/storage/block_store.rs +++ b/lib/ain-evm/src/storage/block_store.rs @@ -116,7 +116,6 @@ impl BlockStorage for BlockStore { fn put_block(&self, block: &BlockAny) -> Result<()> { self.extend_transactions_from_block(block)?; - let block_number = block.header.number; let hash = block.header.hash(); let blocks_cf = self.column::(); diff --git a/lib/ain-ocean/Cargo.toml b/lib/ain-ocean/Cargo.toml index defc49f40fe..aadce6cb3de 100644 --- a/lib/ain-ocean/Cargo.toml +++ b/lib/ain-ocean/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.test] +debug = true [dependencies] ain-cpp-imports.workspace = true @@ -31,4 +33,5 @@ bitcoin_hashes = "0.12.0" structopt = { version = "0.3", default-features = false } tempfile = "3.8.1" anyhow.workspace = true +chrono = "0.4.31" cached.workspace = true diff --git a/lib/ain-ocean/src/data_acces/block.rs b/lib/ain-ocean/src/data_acces/block.rs index 41b4415a1a4..8b4fd1a32ad 100644 --- a/lib/ain-ocean/src/data_acces/block.rs +++ b/lib/ain-ocean/src/data_acces/block.rs @@ -1,43 +1,171 @@ -use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; - use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::block::Block, }; +use anyhow::{anyhow, Result}; +use rocksdb::IteratorMode; +use std::convert::TryInto; + #[derive(Debug)] pub struct BlockDb { pub db: RocksDB, } impl BlockDb { - pub async fn get_by_hash(&self) -> Result { - todo!() + pub async fn get_by_hash(&self, hash: String) -> Result> { + let number = match self.db.get("block_map", hash.as_bytes()) { + Ok(Some(value)) => { + // Convert the stored bytes to a block number + let block_number_bytes: [u8; 4] = match value.try_into() { + Ok(bytes) => bytes, + Err(e) => { + return Err(anyhow!("Error converting bytes to block number: {:?}", e)) + } + }; + let block_number = i32::from_be_bytes(block_number_bytes); + Some(block_number) + } + Ok(None) => None, + Err(e) => return Err(anyhow!("Error retrieving block number: {:?}", e)), + }; + + if let Some(block_number) = number { + let block_key = block_number.to_be_bytes(); + match self.db.get("block", &block_key) { + Ok(Some(value)) => { + let block: Block = serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; + Ok(Some(block)) + } + Ok(None) => Ok(None), + Err(e) => Err(anyhow!(e)), + } + } else { + Ok(None) + } } - pub async fn get_by_height(&self) -> Result { - todo!() + + pub async fn get_by_height(&self, height: i32) -> Result> { + match self.db.get("block", &height.to_be_bytes()) { + Ok(Some(value)) => { + let block: Block = serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; + Ok(Some(block)) + } + Ok(None) => Ok(None), + Err(e) => Err(anyhow!(e)), + } } - pub async fn get_heighest(&self) -> Result { - todo!() + + pub async fn get_highest(&self) -> Result> { + // Retrieve the latest block height + let latest_height_bytes = match self.db.get("latest_block_height", b"latest_block_height") { + Ok(Some(value)) => value, + Ok(None) => return Ok(None), // No latest block height set + Err(e) => return Err(anyhow!(e)), + }; + + // Convert the latest height bytes back to an integer + let latest_height = i32::from_be_bytes( + latest_height_bytes + .as_slice() + .try_into() + .map_err(|_| anyhow!("Byte length mismatch for latest height"))?, + ); + + // Retrieve the block with the latest height + match self.db.get("block", &latest_height.to_be_bytes()) { + Ok(Some(value)) => { + let block: Block = serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; + Ok(Some(block)) + } + Ok(None) => Ok(None), // No block found for the latest height + Err(e) => Err(anyhow!(e)), + } } - pub async fn query_by_height(&self, limit: i32, lt: i32) -> Result> { - todo!() + + pub async fn query_by_height( + &self, + limit: i32, + lt: i32, + sort_order: SortOrder, + ) -> Result> { + let mut blocks: Vec = Vec::new(); + + let iterator = self.db.iterator("block", IteratorMode::End)?; + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let block: Block = serde_json::from_slice(&value)?; + + if block.height < lt { + blocks.push(block); + + if blocks.len() == limit as usize { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => blocks.sort_by(|a, b| a.height.cmp(&b.height)), + SortOrder::Descending => blocks.sort_by(|a, b| b.height.cmp(&a.height)), + } + + Ok(blocks) } - pub async fn store_block(&self, block: Block) -> Result<()> { + + pub async fn put_block(&self, block: Block) -> Result<()> { match serde_json::to_string(&block) { Ok(value) => { - let key = block.id.clone(); - self.db.put("raw_block", key.as_bytes(), value.as_bytes())?; + let block_number = block.height; + self.db + .put("block", &block_number.to_be_bytes(), value.as_bytes())?; + let block_map_key = block.hash.as_bytes(); + self.db + .put("block_map", block_map_key, &block_number.to_be_bytes())?; + self.db + .delete("latest_block_height", b"latest_block_height")?; + self.db.put( + "latest_block_height", + b"latest_block_height", + &block_number.to_be_bytes(), + )?; Ok(()) } Err(e) => Err(anyhow!(e)), } } pub async fn delete_block(&self, hash: String) -> Result<()> { - match self.db.delete("raw_block", hash.as_bytes()) { - Ok(_) => Ok(()), - Err(e) => Err(anyhow!(e)), + let number = match self.db.get("block_map", hash.as_bytes()) { + Ok(Some(value)) => { + // Convert the stored bytes to a block number + let block_number_bytes: [u8; 4] = match value.try_into() { + Ok(bytes) => bytes, + Err(e) => { + return Err(anyhow!("Error converting bytes to block number: {:?}", e)) + } + }; + let block_number = i32::from_be_bytes(block_number_bytes); + Some(block_number) + } + Ok(None) => None, + Err(e) => return Err(anyhow!("Error retrieving block number: {:?}", e)), + }; + + if let Some(block_number) = number { + let block_key = block_number.to_be_bytes(); + match self.db.delete("block", &block_key) { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!(e)), + } + } else { + Ok(()) } } } diff --git a/lib/ain-ocean/src/data_acces/masternode.rs b/lib/ain-ocean/src/data_acces/masternode.rs index c2e7466a63d..cdf54f8d6fc 100644 --- a/lib/ain-ocean/src/data_acces/masternode.rs +++ b/lib/ain-ocean/src/data_acces/masternode.rs @@ -1,19 +1,47 @@ -use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; -use serde_json; - use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::masternode::Masternode, }; +use anyhow::{anyhow, Result}; +use rocksdb::IteratorMode; +use serde_json; pub struct MasterNodeDB { pub db: RocksDB, } impl MasterNodeDB { - pub async fn query(&self, limit: i32, lt: i32) -> Result> { - todo!() + pub async fn query( + &self, + limit: i32, + lt: u32, + sort_order: SortOrder, + ) -> Result> { + let iter_mode: IteratorMode = sort_order.into(); + let master_node: Result> = self + .db + .iterator("masternode", iter_mode)? + .into_iter() + .take(limit as usize) + .map(|result| { + result + .map_err(|e| { + anyhow!("Error during iteration: {}", e) + .context("error master_node query error") + }) + .and_then(|(_key, value)| { + let stats: Masternode = serde_json::from_slice(&value)?; + if stats.block.height < lt { + Ok(stats) + } else { + Err(anyhow!("Value is not less than lt") + .context("Contextual error message")) + } + }) + }) + .collect(); + + master_node.and_then(|result| Ok(result)) } pub async fn get(&self, id: String) -> Result> { match self.db.get("masternode", id.as_bytes()) { diff --git a/lib/ain-ocean/src/data_acces/masternode_states.rs b/lib/ain-ocean/src/data_acces/masternode_states.rs deleted file mode 100644 index 8fb54b2a779..00000000000 --- a/lib/ain-ocean/src/data_acces/masternode_states.rs +++ /dev/null @@ -1,66 +0,0 @@ -use anyhow::{anyhow, Result}; -use bitcoin::absolute::Height; -use rocksdb::{ColumnFamilyDescriptor, IteratorMode, DB}; -use serde::{Deserialize, Serialize}; - -use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, - model::masternode_stats::MasternodeStats, -}; - -#[derive(Debug)] -pub struct MasterStatsDb { - pub db: RocksDB, -} -impl MasterStatsDb { - pub async fn get_latest(&self) -> Result { - // let mut latest_stats: Option = None; - // let mut highest_height = -1; - - // let iter = self.db.iterator("masternode_stats", IteratorMode::End); // Start from the end of the DB - - // for (key, value) in iter { - // let stats: MasternodeStats = serde_json::from_slice(&value)?; - // if stats.block.height > highest_height { - // highest_height = stats.block.height; - // latest_stats = Some(stats); - // } - // } - - // Ok(latest_stats); - todo!() - } - pub async fn query(&self, limit: i32, lt: i32) -> Result> { - todo!() - } - pub async fn get(&self, height: i32) -> Result> { - let bytes: &[u8] = &height.to_be_bytes(); - match self.db.get("masternode_stats", bytes) { - Ok(Some(value)) => { - let master_states: MasternodeStats = - serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; - Ok(Some(master_states)) - } - Ok(None) => Ok(None), - Err(e) => Err(anyhow!(e)), - } - } - pub async fn store(&self, stats: MasternodeStats) -> Result<()> { - match serde_json::to_string(&stats) { - Ok(value) => { - let key = stats.block.height.clone(); - let bytes: &[u8] = &key.to_be_bytes(); - self.db.put("masternode_stats", bytes, value.as_bytes())?; - Ok(()) - } - Err(e) => Err(anyhow!(e)), - } - } - pub async fn delete(&self, height: i32) -> Result<()> { - let bytes: &[u8] = &height.to_be_bytes(); - match self.db.delete("masternode_stats", bytes) { - Ok(_) => Ok(()), - Err(e) => Err(anyhow!(e)), - } - } -} diff --git a/lib/ain-ocean/src/data_acces/masternode_stats.rs b/lib/ain-ocean/src/data_acces/masternode_stats.rs new file mode 100644 index 00000000000..4cb7ed20f76 --- /dev/null +++ b/lib/ain-ocean/src/data_acces/masternode_stats.rs @@ -0,0 +1,110 @@ +use crate::{ + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, + model::masternode_stats::MasternodeStats, +}; +use anyhow::Context; +use anyhow::{anyhow, Result}; +use rocksdb::IteratorMode; +#[derive(Debug)] +pub struct MasterStatsDb { + pub db: RocksDB, +} +impl MasterStatsDb { + pub async fn get_latest(&self) -> Result> { + let latest_height_bytes = match self + .db + .get("masternode_block_height", b"master_block_height") + { + Ok(Some(value)) => value, + Ok(None) => return Ok(None), // No latest block height set + Err(e) => return Err(anyhow!(e)), + }; + + // Convert the latest height bytes back to an integer + let latest_height = i32::from_be_bytes( + latest_height_bytes + .as_slice() + .try_into() + .map_err(|_| anyhow!("Byte length mismatch for latest height"))?, + ); + + // Retrieve the block with the latest height + match self.db.get("block", &latest_height.to_be_bytes()) { + Ok(Some(value)) => { + let block: MasternodeStats = + serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; + Ok(Some(block)) + } + Ok(None) => Ok(None), // No block found for the latest height + Err(e) => Err(anyhow!(e)), + } + } + + pub async fn query( + &self, + limit: i32, + lt: i32, + sort_order: SortOrder, + ) -> Result> { + let iter_mode: IteratorMode = sort_order.into(); + let master_node: Result> = self + .db + .iterator("masternode_stats", iter_mode)? + .into_iter() + .take(limit as usize) + .map(|result| { + result + .map_err(|e| { + anyhow!("Error during iteration: {}", e).context("Contextual error message") + }) + .and_then(|(_key, value)| { + let stats: MasternodeStats = serde_json::from_slice(&value)?; + if stats.block.height < lt { + Ok(stats) + } else { + Err(anyhow!("Value is not less than lt") + .context("Contextual error message")) + } + }) + }) + .collect(); + Ok(master_node?) + } + + pub async fn get(&self, height: i32) -> Result> { + let bytes: &[u8] = &height.to_be_bytes(); + match self.db.get("masternode_stats", bytes) { + Ok(Some(value)) => { + let master_states: MasternodeStats = + serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; + Ok(Some(master_states)) + } + Ok(None) => Ok(None), + Err(e) => Err(anyhow!(e)), + } + } + pub async fn store(&self, stats: MasternodeStats) -> Result<()> { + match serde_json::to_string(&stats) { + Ok(value) => { + let key = stats.block.height.clone(); + let height: &[u8] = &key.to_be_bytes(); + self.db.put("masternode_stats", height, value.as_bytes())?; + self.db + .put("masternode_map", stats.block.hash.as_bytes(), height)?; + self.db + .delete("masternode_block_height", b"master_block_height")?; + self.db + .put("masternode_block_height", b"master_block_height", height)?; + Ok(()) + } + Err(e) => Err(anyhow!(e)), + } + } + pub async fn delete(&self, height: i32) -> Result<()> { + let bytes: &[u8] = &height.to_be_bytes(); + match self.db.delete("masternode_stats", bytes) { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!(e)), + } + } +} diff --git a/lib/ain-ocean/src/data_acces/mod.rs b/lib/ain-ocean/src/data_acces/mod.rs index 4072333a14e..426541e79e2 100644 --- a/lib/ain-ocean/src/data_acces/mod.rs +++ b/lib/ain-ocean/src/data_acces/mod.rs @@ -1,9 +1,11 @@ pub mod block; pub mod masternode; -pub mod masternode_states; +pub mod masternode_stats; pub mod oracle; +pub mod oracle_price_active; pub mod oracle_price_aggregated; pub mod oracle_price_aggregated_interval; +pub mod oracle_price_feed; pub mod oracle_token_currency; pub mod order_history; pub mod pool_swap; diff --git a/lib/ain-ocean/src/data_acces/oracle.rs b/lib/ain-ocean/src/data_acces/oracle.rs index 2d9badd0927..e106b94c0ce 100644 --- a/lib/ain-ocean/src/data_acces/oracle.rs +++ b/lib/ain-ocean/src/data_acces/oracle.rs @@ -1,9 +1,10 @@ use anyhow::{anyhow, Error, Result}; +use rocksdb::IteratorMode; use serde::{Deserialize, Serialize}; use serde_json; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::oracle::Oracle, }; @@ -12,9 +13,37 @@ pub struct OracleDb { } impl OracleDb { - pub async fn query(&self, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("oracle", IteratorMode::End)?; + let mut oracles: Vec = Vec::new(); + let collected_items: Vec<_> = iterator.collect(); + + for result in collected_items.into_iter().rev() { + let value = match result { + Ok((_, value)) => value, + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle: Oracle = serde_json::from_slice(&value)?; + oracles.push(oracle); + if oracles.len() as i32 >= limit { + break; + } + } + + match sort_order { + SortOrder::Ascending => oracles.sort_by(|a, b| a.id.cmp(&b.id)), + SortOrder::Descending => oracles.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(oracles) } + pub async fn store(&self, oracle: Oracle) -> Result<()> { match serde_json::to_string(&oracle) { Ok(value) => { diff --git a/lib/ain-ocean/src/data_acces/oracle_price_active.rs b/lib/ain-ocean/src/data_acces/oracle_price_active.rs new file mode 100644 index 00000000000..aa3a08a1141 --- /dev/null +++ b/lib/ain-ocean/src/data_acces/oracle_price_active.rs @@ -0,0 +1,67 @@ +use crate::model::oracle_price_feed::OraclePriceFeed; +use crate::{ + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, + model::oracle_price_active::OraclePriceActive, +}; +use anyhow::{anyhow, Result}; +use rocksdb::IteratorMode; + +pub struct OraclePriceActiveDb { + pub db: RocksDB, +} + +impl OraclePriceActiveDb { + pub async fn query( + &self, + oracle_id: String, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("oracle_price_active", IteratorMode::End)?; + let mut oracle_price_feed: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle: OraclePriceActive = serde_json::from_slice(&value)?; + if oracle.key == oracle_id { + oracle_price_feed.push(oracle); + if oracle_price_feed.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => { + oracle_price_feed.sort_by(|a: &OraclePriceActive, b| a.id.cmp(&b.id)) + } + SortOrder::Descending => oracle_price_feed.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(oracle_price_feed) + } + pub async fn put(&self, oracle_price_feed: OraclePriceActive) -> Result<()> { + match serde_json::to_string(&oracle_price_feed) { + Ok(value) => { + let key = oracle_price_feed.id.clone(); + self.db + .put("oracle_price_active", key.as_bytes(), value.as_bytes())?; + Ok(()) + } + Err(e) => Err(anyhow!(e)), + } + } + pub async fn delete(&self, id: String) -> Result<()> { + match self.db.delete("oracle_price_active", id.as_bytes()) { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!(e)), + } + } +} diff --git a/lib/ain-ocean/src/data_acces/oracle_price_aggregated.rs b/lib/ain-ocean/src/data_acces/oracle_price_aggregated.rs index e1e89e96733..182cdea9c56 100644 --- a/lib/ain-ocean/src/data_acces/oracle_price_aggregated.rs +++ b/lib/ain-ocean/src/data_acces/oracle_price_aggregated.rs @@ -1,10 +1,9 @@ -use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; - use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::oracle_price_aggregated::OraclePriceAggregated, }; +use anyhow::{anyhow, Result}; +use rocksdb::IteratorMode; pub struct OraclePriceAggrigatedDb { pub db: RocksDB, @@ -13,16 +12,46 @@ pub struct OraclePriceAggrigatedDb { impl OraclePriceAggrigatedDb { pub async fn query( &self, - key: String, + oracle_key: String, limit: i32, lt: String, + sort_order: SortOrder, ) -> Result> { - todo!() + let iterator = self + .db + .iterator("oracle_price_aggregated", IteratorMode::End)?; + let mut oracle_pa: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle: OraclePriceAggregated = serde_json::from_slice(&value)?; + if oracle.key == oracle_key { + oracle_pa.push(oracle); + if oracle_pa.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => { + oracle_pa.sort_by(|a: &OraclePriceAggregated, b| a.id.cmp(&b.id)) + } + SortOrder::Descending => oracle_pa.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(oracle_pa) } pub async fn put(&self, oracle: OraclePriceAggregated) -> Result<()> { match serde_json::to_string(&oracle) { Ok(value) => { - let key = oracle.id.clone(); + let key = oracle.key.clone(); self.db .put("oracle_price_aggregated", key.as_bytes(), value.as_bytes())?; Ok(()) @@ -30,14 +59,14 @@ impl OraclePriceAggrigatedDb { Err(e) => Err(anyhow!(e)), } } - pub async fn get(&self, id: String) -> Option { - match self.db.get("oracle_price_aggregated", id.as_bytes()) { + pub async fn get(&self, key: String) -> Option { + match self.db.get("oracle_price_aggregated", key.as_bytes()) { Ok(Some(value)) => serde_json::from_slice(&value).ok(), _ => None, } } - pub async fn delete(&self, id: String) -> Result<()> { - match self.db.delete("oracle_price_aggregated", id.as_bytes()) { + pub async fn delete(&self, key: String) -> Result<()> { + match self.db.delete("oracle_price_aggregated", key.as_bytes()) { Ok(_) => Ok(()), Err(e) => Err(anyhow!(e)), } diff --git a/lib/ain-ocean/src/data_acces/oracle_price_aggregated_interval.rs b/lib/ain-ocean/src/data_acces/oracle_price_aggregated_interval.rs index cb36cf9c3d0..4ff3977e85d 100644 --- a/lib/ain-ocean/src/data_acces/oracle_price_aggregated_interval.rs +++ b/lib/ain-ocean/src/data_acces/oracle_price_aggregated_interval.rs @@ -1,10 +1,9 @@ -use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; - use crate::{ database::db_manager::{ColumnFamilyOperations, RocksDB}, model::oracle_price_aggregated_interval::OraclePriceAggregatedInterval, }; +use anyhow::{anyhow, Result}; +use rocksdb::{ColumnFamilyDescriptor, IteratorMode, DB}; pub struct OraclePriceAggregatedIntervalDb { pub db: RocksDB, @@ -17,7 +16,31 @@ impl OraclePriceAggregatedIntervalDb { limit: i32, lt: String, ) -> Result> { - todo!() + let iterator = self + .db + .iterator("oracle_price_aggregated_interval", IteratorMode::End)?; + let mut oracle_prices: Vec = Vec::new(); + let collected_items: Vec<_> = iterator.collect(); + + for result in collected_items.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle_price: OraclePriceAggregatedInterval = serde_json::from_slice(&value)?; + + // Check if the id is less than 'lt' + if String::from_utf8(key.to_vec())? < lt { + oracle_prices.push(oracle_price); + + if oracle_prices.len() == limit as usize { + break; + } + } + } + + Ok(oracle_prices) } pub async fn put(&self, oracle: OraclePriceAggregatedInterval) -> Result<()> { match serde_json::to_string(&oracle) { diff --git a/lib/ain-ocean/src/data_acces/oracle_price_feed.rs b/lib/ain-ocean/src/data_acces/oracle_price_feed.rs new file mode 100644 index 00000000000..05cc2e90f01 --- /dev/null +++ b/lib/ain-ocean/src/data_acces/oracle_price_feed.rs @@ -0,0 +1,66 @@ +use crate::{ + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, + model::oracle_price_feed::OraclePriceFeed, +}; +use anyhow::{anyhow, Result}; +use rocksdb::IteratorMode; + +pub struct OraclePriceFeedDb { + pub db: RocksDB, +} + +impl OraclePriceFeedDb { + pub async fn query( + &self, + oracle_id: String, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("oracle_price_feed", IteratorMode::End)?; + let mut oracle_price_feed: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle: OraclePriceFeed = serde_json::from_slice(&value)?; + if oracle.key == oracle_id { + oracle_price_feed.push(oracle); + if oracle_price_feed.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => { + oracle_price_feed.sort_by(|a: &OraclePriceFeed, b| a.id.cmp(&b.id)) + } + SortOrder::Descending => oracle_price_feed.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(oracle_price_feed) + } + pub async fn put(&self, oracle_price_feed: OraclePriceFeed) -> Result<()> { + match serde_json::to_string(&oracle_price_feed) { + Ok(value) => { + let key = oracle_price_feed.id.clone(); + self.db + .put("oracle_price_feed", key.as_bytes(), value.as_bytes())?; + Ok(()) + } + Err(e) => Err(anyhow!(e)), + } + } + pub async fn delete(&self, id: String) -> Result<()> { + match self.db.delete("oracle_price_feed", id.as_bytes()) { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!(e)), + } + } +} diff --git a/lib/ain-ocean/src/data_acces/oracle_token_currency.rs b/lib/ain-ocean/src/data_acces/oracle_token_currency.rs index 907e1633c94..e66e7421599 100644 --- a/lib/ain-ocean/src/data_acces/oracle_token_currency.rs +++ b/lib/ain-ocean/src/data_acces/oracle_token_currency.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::oracle_token_currency::OracleTokenCurrency, }; @@ -11,8 +11,41 @@ pub struct OracleTokenCurrencyDb { } impl OracleTokenCurrencyDb { - pub async fn query(&self, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + oracle_id: String, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self + .db + .iterator("oracle_token_currency", IteratorMode::End)?; + let mut oracle_tc: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle: OracleTokenCurrency = serde_json::from_slice(&value)?; + if oracle.key == oracle_id { + oracle_tc.push(oracle); + if oracle_tc.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => oracle_tc.sort_by(|a: &OracleTokenCurrency, b| a.id.cmp(&b.id)), + SortOrder::Descending => oracle_tc.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(oracle_tc) } pub async fn put(&self, oracle_token: OracleTokenCurrency) -> Result<()> { match serde_json::to_string(&oracle_token) { diff --git a/lib/ain-ocean/src/data_acces/order_history.rs b/lib/ain-ocean/src/data_acces/order_history.rs index 9b274c279c9..6f5fff45ba2 100644 --- a/lib/ain-ocean/src/data_acces/order_history.rs +++ b/lib/ain-ocean/src/data_acces/order_history.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::oracle_history::OracleHistory, }; @@ -11,14 +11,45 @@ pub struct OracleHistoryDB { } impl OracleHistoryDB { - pub async fn query(&self, oracleId: String, limit: i32, lt: String) -> Result { - todo!() + pub async fn query( + &self, + oracle_id: String, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("oracle_history", IteratorMode::End)?; + let mut oracle_history: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let oracle: OracleHistory = serde_json::from_slice(&value)?; + if oracle.id == oracle_id { + oracle_history.push(oracle); + if oracle_history.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => oracle_history.sort_by(|a, b| a.oracle_id.cmp(&b.oracle_id)), + SortOrder::Descending => oracle_history.sort_by(|a, b| b.oracle_id.cmp(&a.oracle_id)), + } + + Ok(oracle_history) } pub async fn store(&self, oracle_history: OracleHistory) -> Result<()> { match serde_json::to_string(&oracle_history) { Ok(value) => { - let key = oracle_history.id.clone(); + let key = oracle_history.oracle_id.clone(); self.db .put("oracle_history", key.as_bytes(), value.as_bytes())?; Ok(()) @@ -26,8 +57,8 @@ impl OracleHistoryDB { Err(e) => Err(anyhow!(e)), } } - pub async fn delete(&self, id: String) -> Result<()> { - match self.db.delete("oracle_history", id.as_bytes()) { + pub async fn delete(&self, oracle_id: String) -> Result<()> { + match self.db.delete("oracle_history", oracle_id.as_bytes()) { Ok(_) => Ok(()), Err(e) => Err(anyhow!(e)), } diff --git a/lib/ain-ocean/src/data_acces/pool_swap.rs b/lib/ain-ocean/src/data_acces/pool_swap.rs index ad5a6b2d89c..25e857d4bfb 100644 --- a/lib/ain-ocean/src/data_acces/pool_swap.rs +++ b/lib/ain-ocean/src/data_acces/pool_swap.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::poolswap::PoolSwap, }; @@ -11,8 +11,39 @@ pub struct PoolSwapDb { } impl PoolSwapDb { - pub async fn query(&self, key: String, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + id: String, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("pool_swap", IteratorMode::End)?; + let mut pool_vin: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let vin: PoolSwap = serde_json::from_slice(&value)?; + if vin.id == id { + pool_vin.push(vin); + if pool_vin.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => pool_vin.sort_by(|a, b| a.txid.cmp(&b.txid)), + SortOrder::Descending => pool_vin.sort_by(|a, b| b.txid.cmp(&a.txid)), + } + + Ok(pool_vin) } pub async fn put(&self, swap: PoolSwap) -> Result<()> { match serde_json::to_string(&swap) { diff --git a/lib/ain-ocean/src/data_acces/pool_swap_aggregated.rs b/lib/ain-ocean/src/data_acces/pool_swap_aggregated.rs index 9259760086e..1af6936a46f 100644 --- a/lib/ain-ocean/src/data_acces/pool_swap_aggregated.rs +++ b/lib/ain-ocean/src/data_acces/pool_swap_aggregated.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::poolswap_aggregated::PoolSwapAggregated, }; @@ -13,11 +13,39 @@ pub struct PoolSwapAggregatedDb { impl PoolSwapAggregatedDb { pub async fn query( &self, - key: String, + id: String, limit: i32, lt: String, + sort_order: SortOrder, ) -> Result<(Vec)> { - todo!() + let iterator = self + .db + .iterator("pool_swap_aggregated", IteratorMode::End)?; + let mut pool_swap: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let ps: PoolSwapAggregated = serde_json::from_slice(&value)?; + if ps.id == id { + pool_swap.push(ps); + if pool_swap.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => pool_swap.sort_by(|a, b| a.id.cmp(&b.id)), + SortOrder::Descending => pool_swap.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(pool_swap) } pub async fn put(&self, aggregated: PoolSwapAggregated) -> Result<()> { match serde_json::to_string(&aggregated) { diff --git a/lib/ain-ocean/src/data_acces/price_ticker.rs b/lib/ain-ocean/src/data_acces/price_ticker.rs index ac7e1a0140e..e6491f888b5 100644 --- a/lib/ain-ocean/src/data_acces/price_ticker.rs +++ b/lib/ain-ocean/src/data_acces/price_ticker.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::price_ticker::PriceTicker, }; @@ -11,8 +11,35 @@ pub struct price_ticker { } impl price_ticker { - pub async fn query(&self, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("price_ticker", IteratorMode::End)?; + let mut pt: Vec = Vec::new(); + let collected_items: Vec<_> = iterator.collect(); + + for result in collected_items.into_iter().rev() { + let value = match result { + Ok((_, value)) => value, + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let price_ticker: PriceTicker = serde_json::from_slice(&value)?; + pt.push(price_ticker); + if pt.len() as i32 >= limit { + break; + } + } + + match sort_order { + SortOrder::Ascending => pt.sort_by(|a, b| a.id.cmp(&b.id)), + SortOrder::Descending => pt.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(pt) } pub async fn get(&self, id: String) -> Result { match self.db.get("price_ticker", id.as_bytes()) { diff --git a/lib/ain-ocean/src/data_acces/raw_block.rs b/lib/ain-ocean/src/data_acces/raw_block.rs index b2beb705bf4..024222dcba4 100644 --- a/lib/ain-ocean/src/data_acces/raw_block.rs +++ b/lib/ain-ocean/src/data_acces/raw_block.rs @@ -1,5 +1,4 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; use crate::{ database::db_manager::{ColumnFamilyOperations, RocksDB}, diff --git a/lib/ain-ocean/src/data_acces/script_activity.rs b/lib/ain-ocean/src/data_acces/script_activity.rs index 5a24394fa0a..feeaa12abc9 100644 --- a/lib/ain-ocean/src/data_acces/script_activity.rs +++ b/lib/ain-ocean/src/data_acces/script_activity.rs @@ -1,24 +1,56 @@ -use anyhow::{anyhow, Error, Result}; -use serde::{Deserialize, Serialize}; -use serde_json; - use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::script_activity::ScriptActivity, }; +use anyhow::{anyhow, Result}; +use rocksdb::{Direction, IteratorMode}; +use serde_json; pub struct ScriptUnspentDB { pub db: RocksDB, } impl ScriptUnspentDB { - pub async fn query(&self, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + hid: String, + limit: i32, + lt: Option, + ) -> Result> { + let prefix = format!("script_activity_hid_sort:{}:", lt.unwrap_or_default()); + let iterator_result = self.db.iterator( + "script_activity", + IteratorMode::From(prefix.as_bytes(), Direction::Forward), + )?; + let mut results = Vec::new(); + for item in iterator_result { + match item { + Ok((key, value)) => { + let key_str = String::from_utf8_lossy(&key); + if !key_str.starts_with(&prefix) { + break; + } + + let script_unspent: ScriptActivity = serde_json::from_slice(&value)?; + + results.push(script_unspent); + + if results.len() >= limit as usize { + break; + } + } + Err(err) => { + eprintln!("Error iterating over the database: {:?}", err); + return Err(err.into()); + } + } + } + Ok(results) } pub async fn store(&self, unspent: ScriptActivity) -> Result<()> { match serde_json::to_string(&unspent) { Ok(value) => { - let key = unspent.id.clone(); + let key = unspent.hid.clone(); self.db .put("script_activity", key.as_bytes(), value.as_bytes())?; Ok(()) @@ -26,8 +58,8 @@ impl ScriptUnspentDB { Err(e) => Err(anyhow!(e)), } } - pub async fn delete(&self, id: String) -> Result<()> { - match self.db.delete("script_activity", id.as_bytes()) { + pub async fn delete(&self, hid: String) -> Result<()> { + match self.db.delete("script_activity", hid.as_bytes()) { Ok(_) => Ok(()), Err(e) => Err(anyhow!(e)), } diff --git a/lib/ain-ocean/src/data_acces/script_aggregation.rs b/lib/ain-ocean/src/data_acces/script_aggregation.rs index ec386717bad..2f2788aaf5d 100644 --- a/lib/ain-ocean/src/data_acces/script_aggregation.rs +++ b/lib/ain-ocean/src/data_acces/script_aggregation.rs @@ -1,9 +1,9 @@ -use anyhow::{anyhow, Error, Result}; -use serde::{Deserialize, Serialize}; +use anyhow::{anyhow, Result}; +use rocksdb::{Direction, IteratorMode}; use serde_json; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::script_aggregation::ScriptAggregation, }; @@ -12,22 +12,69 @@ pub struct ScriptAggretionDB { } impl ScriptAggretionDB { - pub async fn query(&self, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + hid: String, + limit: i32, + lt: String, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator( + "script_aggregation", + IteratorMode::From(lt.as_bytes(), Direction::Reverse), + )?; + + let mut script_aggre: Vec = Vec::new(); + + for item in iterator { + match item { + Ok((key, value)) => { + let key_str = String::from_utf8_lossy(&key); + + if !key_str.starts_with(&hid) { + break; + } + + let vout: ScriptAggregation = serde_json::from_slice(&value)?; + script_aggre.push(vout); + + if script_aggre.len() >= limit as usize { + break; + } + } + Err(err) => { + eprintln!("Error iterating over the database: {:?}", err); + return Err(err.into()); + } + } + } + + // Sorting based on the SortOrder + match sort_order { + SortOrder::Ascending => script_aggre.sort_by(|a, b| a.id.cmp(&b.id)), + SortOrder::Descending => script_aggre.sort_by(|a, b| b.id.cmp(&a.id)), + } + + Ok(script_aggre) } pub async fn store(&self, aggregation: ScriptAggregation) -> Result<()> { match serde_json::to_string(&aggregation) { Ok(value) => { - let key = aggregation.id.clone(); + let key = aggregation.hid.clone(); self.db .put("script_aggregation", key.as_bytes(), value.as_bytes())?; + + let h = aggregation.block.height.clone(); + let height: &[u8] = &h.to_be_bytes(); + self.db + .put("script_aggregation_mapper", height, key.as_bytes())?; Ok(()) } Err(e) => Err(anyhow!(e)), } } - pub async fn get(&self, id: String) -> Result> { - match self.db.get("script_aggregation", id.as_bytes()) { + pub async fn get(&self, hid: String) -> Result> { + match self.db.get("script_aggregation", hid.as_bytes()) { Ok(Some(value)) => { let oracle: ScriptAggregation = serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; @@ -37,8 +84,8 @@ impl ScriptAggretionDB { Err(e) => Err(anyhow!(e)), } } - pub async fn delete(&self, id: String) -> Result<()> { - match self.db.delete("script_aggregation", id.as_bytes()) { + pub async fn delete(&self, hid: String) -> Result<()> { + match self.db.delete("script_aggregation", hid.as_bytes()) { Ok(_) => Ok(()), Err(e) => Err(anyhow!(e)), } diff --git a/lib/ain-ocean/src/data_acces/script_unspent.rs b/lib/ain-ocean/src/data_acces/script_unspent.rs index a5c1a462f08..5db7c88e066 100644 --- a/lib/ain-ocean/src/data_acces/script_unspent.rs +++ b/lib/ain-ocean/src/data_acces/script_unspent.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Error, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::{Direction, IteratorMode}; use serde_json; use crate::{ @@ -12,13 +12,46 @@ pub struct ScriptUnspentDB { } impl ScriptUnspentDB { - pub async fn query(&self, limit: i32, lt: String) -> Result> { - todo!() + pub async fn query( + &self, + hid: String, + limit: i32, + gt: Option, + ) -> Result> { + let prefix = format!("script_unspent_hid_sort:{}:", gt.unwrap_or_default()); + let iterator_result = self.db.iterator( + "script_unspent", + IteratorMode::From(prefix.as_bytes(), Direction::Forward), + )?; + let mut results = Vec::new(); + for item in iterator_result { + match item { + Ok((key, value)) => { + let key_str = String::from_utf8_lossy(&key); + if !key_str.starts_with(&prefix) { + break; + } + + let script_unspent: ScriptUnspent = serde_json::from_slice(&value)?; + + results.push(script_unspent); + + if results.len() >= limit as usize { + break; + } + } + Err(err) => { + eprintln!("Error iterating over the database: {:?}", err); + return Err(err.into()); + } + } + } + Ok(results) } pub async fn store(&self, unspent: ScriptUnspent) -> Result<()> { match serde_json::to_string(&unspent) { Ok(value) => { - let key = unspent.id.clone(); + let key = unspent.hid.clone(); self.db .put("script_unspent", key.as_bytes(), value.as_bytes())?; Ok(()) @@ -26,8 +59,8 @@ impl ScriptUnspentDB { Err(e) => Err(anyhow!(e)), } } - pub async fn delete(&self, id: String) -> Result<()> { - match self.db.delete("script_unspent", id.as_bytes()) { + pub async fn delete(&self, hid: String) -> Result<()> { + match self.db.delete("script_unspent", hid.as_bytes()) { Ok(_) => Ok(()), Err(e) => Err(anyhow!(e)), } diff --git a/lib/ain-ocean/src/data_acces/test/block_test.rs b/lib/ain-ocean/src/data_acces/test/block_test.rs new file mode 100644 index 00000000000..4fa1d2d5cb2 --- /dev/null +++ b/lib/ain-ocean/src/data_acces/test/block_test.rs @@ -0,0 +1,217 @@ +#[cfg(test)] +mod tests { + use super::*; + use crate::data_acces::block::BlockDb; + use crate::database::db_manager::SortOrder; + use crate::database::db_manager::{ColumnFamilyOperations, RocksDB}; + use crate::model::block::Block; + use tempdir::TempDir; + + pub fn create_dummy_block(height: i32) -> Block { + Block { + id: "1".to_string(), + hash: "block_hash_1".to_string(), + previous_hash: "previous_block_hash".to_string(), + height: height, + version: 1, + time: 1634054400, // Replace with an actual timestamp + median_time: 1634054400, // Replace with an actual timestamp + transaction_count: 10, + difficulty: 12345, + masternode: "masternode_address".to_string(), + minter: "minter_address".to_string(), + minter_block_count: 5, + reward: "10.0".to_string(), + stake_modifier: "stake_modifier_value".to_string(), + merkleroot: "merkleroot_value".to_string(), + size: 2000, + size_stripped: 1800, + weight: 1900, + } + } + + #[tokio::test] + async fn test_put_get_by_hash_and_height() { + // Create a temporary RocksDB instance for testing + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Create a dummy block for testing + let dummy_block = create_dummy_block(1); + + // Test the put_block method + block_db.put_block(dummy_block.clone()).await.unwrap(); + + // Test the get_by_hash method + let result_by_hash = block_db + .get_by_hash(dummy_block.hash.clone()) + .await + .unwrap(); + assert_eq!(result_by_hash.unwrap(), dummy_block.clone()); + + // Test the get_by_height method + let result_by_height = block_db.get_by_height(dummy_block.height).await.unwrap(); + assert_eq!(result_by_height.unwrap(), dummy_block); + } + + #[tokio::test] + async fn test_get_nonexistent_block_by_hash() { + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Attempt to get a block using a hash that doesn't exist + let result = block_db + .get_by_hash("nonexistent_hash".to_string()) + .await + .unwrap(); + assert_eq!(result, None); + } + + #[tokio::test] + async fn test_get_nonexistent_block_by_height() { + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Attempt to get a block using a height that doesn't exist + let result = block_db.get_by_height(999).await.unwrap(); + assert_eq!(result, None); + } + + #[tokio::test] + async fn test_delete_block() { + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Create a dummy block for testing + let dummy_block = create_dummy_block(1); + + // Put the block + block_db.put_block(dummy_block.clone()).await.unwrap(); + + // Delete the block + block_db + .delete_block(dummy_block.hash.clone()) + .await + .unwrap(); + + // Attempt to get the block after deletion + let result_by_hash = block_db + .get_by_hash(dummy_block.hash.clone()) + .await + .unwrap(); + + assert!(result_by_hash.is_none()); + } + + #[tokio::test] + async fn test_query_by_height_descending() { + // Create a temporary RocksDB instance for testing + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Create dummy blocks with heights 1 to 5 and insert them into the database + for height in 1..=10 { + let dummy_block = create_dummy_block(height); + block_db.put_block(dummy_block.clone()).await.unwrap(); + } + + // Test the query_by_height method + let result_blocks = block_db + .query_by_height(3, 4, SortOrder::Descending) + .await + .unwrap(); + // Verify that the result contains the expected blocks + assert_eq!(result_blocks.len(), 3); + assert_eq!(result_blocks[0].height, 3); + assert_eq!(result_blocks[1].height, 2); + } + + #[tokio::test] + async fn test_query_by_height_ascending() { + // Create a temporary RocksDB instance for testing + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Create dummy blocks with heights 1 to 5 and insert them into the database + for height in 1..=10 { + let dummy_block = create_dummy_block(height); + block_db.put_block(dummy_block.clone()).await.unwrap(); + } + + // Test the query_by_height method + let result_blocks = block_db + .query_by_height(5, 6, SortOrder::Ascending) + .await + .unwrap(); + // Verify that the result contains the expected blocks + assert_eq!(result_blocks.len(), 5); + assert_eq!(result_blocks[0].height, 1); + assert_eq!(result_blocks[1].height, 2); + } + + #[tokio::test] + async fn test_put_block_updates_latest_height() { + // Setup test environment + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Create and insert sample blocks + for height in 1..=10 { + let dummy_block = create_dummy_block(height); + block_db.put_block(dummy_block).await.unwrap(); + } + + // Retrieve latest block height + let latest_height_bytes = block_db + .db + .get("latest_block_height", b"latest_block_height") + .expect("Failed to retrieve latest block height") + .expect("Latest block height not found"); + let latest_height = i32::from_be_bytes( + latest_height_bytes + .as_slice() + .try_into() + .expect("Byte length mismatch"), + ); + + // Assert correctness + assert_eq!(latest_height, 10); // Assert that latest height is 10 + } + + #[tokio::test] + async fn test_get_highest_retrieves_correct_block() -> Result<(), Box> { + // Setup test environment + let temp_dir = TempDir::new("test_rocksdb").expect("Failed to create temp directory"); + let rocksdb = RocksDB::new(temp_dir.path().to_str().unwrap()) + .expect("Failed to create RocksDB instance"); + let block_db = BlockDb { db: rocksdb }; + + // Insert sample blocks + for height in 1..=5 { + let dummy_block = create_dummy_block(height); + block_db.put_block(dummy_block).await.unwrap(); + } + + // Call get_highest + let highest_block = block_db.get_highest().await.unwrap().unwrap(); + + // Assert that the highest block is the one with height 5 + assert_eq!(highest_block.height, 5); + + Ok(()) + } +} diff --git a/lib/ain-ocean/src/data_acces/test/masternode_test.rs b/lib/ain-ocean/src/data_acces/test/masternode_test.rs new file mode 100644 index 00000000000..7ad29de0228 --- /dev/null +++ b/lib/ain-ocean/src/data_acces/test/masternode_test.rs @@ -0,0 +1,82 @@ +#[cfg(test)] +mod tests { + use super::*; + use crate::data_acces::masternode::MasterNodeDB; + use crate::database::db_manager::{RocksDB, SortOrder}; + use crate::model::masternode::{HistoryItem, Masternode, MasternodeBlock}; + use chrono::Utc; + use tempfile::tempdir; + + fn setup_test_db() -> MasterNodeDB { + let temp_dir = tempdir().unwrap(); + let db = RocksDB::new(temp_dir.path().to_str().unwrap()).unwrap(); // Adjust this according to your RocksDB struct + MasterNodeDB { db } + } + fn create_dummy_masternode(id: &str) -> Masternode { + Masternode { + id: id.to_string(), + sort: Some("dummy_sort".to_string()), + owner_address: "dummy_owner_address".to_string(), + operator_address: "dummy_operator_address".to_string(), + creation_height: 100, + resign_height: 200, + resign_tx: Some("dummy_resign_tx".to_string()), + minted_blocks: 50, + timelock: 10, + collateral: "dummy_collateral".to_string(), + block: create_dummy_masternode_block(), + history: Some(vec![create_dummy_history_item()]), + } + } + + fn create_dummy_masternode_block() -> MasternodeBlock { + MasternodeBlock { + hash: "dummy_hash".to_string(), + height: 100, + time: Utc::now().timestamp() as u64, + median_time: Utc::now().timestamp() as u64, + } + } + + fn create_dummy_history_item() -> HistoryItem { + HistoryItem { + txid: "dummy_txid".to_string(), + owner_address: "dummy_history_owner_address".to_string(), + operator_address: "dummy_history_operator_address".to_string(), + } + } + + #[tokio::test] + async fn test_query_with_dummy_data() { + let master_node = setup_test_db(); + let request = create_dummy_masternode("1"); + let result = master_node.store(request).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_store_and_query_dummy_data() { + let master_node = setup_test_db(); + let dummy_data = vec![ + create_dummy_masternode("1"), + create_dummy_masternode("2"), + create_dummy_masternode("3"), + create_dummy_masternode("4"), + create_dummy_masternode("5"), + create_dummy_masternode("6"), + create_dummy_masternode("7"), + create_dummy_masternode("8"), + create_dummy_masternode("9"), + create_dummy_masternode("10"), + ]; + + for masternode in dummy_data { + let result = master_node.store(masternode).await; + assert!(result.is_ok()); + } + // let query_result = master_node.query(5, 8, SortOrder::Ascending).await; + // assert!(query_result.is_err()); + // let queried_masternodes = query_result.unwrap(); + // assert_eq!(queried_masternodes.len(), 5); + } +} diff --git a/lib/ain-ocean/src/data_acces/test/mod.rs b/lib/ain-ocean/src/data_acces/test/mod.rs index bd9b68cbdc5..350db04c962 100644 --- a/lib/ain-ocean/src/data_acces/test/mod.rs +++ b/lib/ain-ocean/src/data_acces/test/mod.rs @@ -1,2 +1,4 @@ +mod block_test; +mod masternode_test; mod oracle_test; mod transaction_test; diff --git a/lib/ain-ocean/src/data_acces/test/transaction_test.rs b/lib/ain-ocean/src/data_acces/test/transaction_test.rs index bde9c788fba..e87196a35a2 100644 --- a/lib/ain-ocean/src/data_acces/test/transaction_test.rs +++ b/lib/ain-ocean/src/data_acces/test/transaction_test.rs @@ -100,4 +100,20 @@ mod tests { let result = txn_vin_db.delete(trx_id).await; assert!(result.is_ok()); } + + #[tokio::test] + async fn test_query_transaction() { + let txn_vin_db = setup_test_db(); + + let test_transaction = sample_transaction("tx1"); + let trx_id = test_transaction.id.clone(); + + let result = txn_vin_db.store(test_transaction.clone()).await; + assert!(result.is_ok()); + + let result = txn_vin_db + .query_by_block_hash(test_transaction.block.hash.to_string(), 10, 0) + .await; + assert!(result.is_ok()); + } } diff --git a/lib/ain-ocean/src/data_acces/transaction.rs b/lib/ain-ocean/src/data_acces/transaction.rs index 061074eb125..487a954c4a6 100644 --- a/lib/ain-ocean/src/data_acces/transaction.rs +++ b/lib/ain-ocean/src/data_acces/transaction.rs @@ -23,15 +23,29 @@ impl TransactionVinDb { } } pub async fn store(&self, txn: Transaction) -> Result<()> { - match serde_json::to_string(&txn) { - Ok(value) => { - let key = txn.id.clone(); - self.db - .put("transaction", key.as_bytes(), value.as_bytes())?; - Ok(()) - } - Err(e) => Err(anyhow!(e)), - } + let value = serde_json::to_string(&txn)?; + let txid_key = txn.txid.clone(); + self.db + .put("transaction", txid_key.as_bytes(), value.as_bytes())?; + + // Retrieve existing transaction IDs for the block hash, if any + let mut txn_ids = match self + .db + .get("transaction_mapper", txn.block.hash.as_bytes())? + { + Some(bytes) => serde_json::from_slice::>(&bytes)?, + None => vec![], + }; + + // Add the new transaction ID to the list + txn_ids.push(txn.txid); + let txn_ids_value = serde_json::to_string(&txn_ids)?; + self.db.put( + "transaction_mapper", + txn.block.hash.as_bytes(), + txn_ids_value.as_bytes(), + )?; + Ok(()) } pub async fn delete(&self, txid: String) -> Result<()> { match self.db.delete("transaction", txid.as_bytes()) { @@ -39,12 +53,39 @@ impl TransactionVinDb { Err(e) => Err(anyhow!(e)), } } - pub async fn query_by_blockhash( + pub async fn query_by_block_hash( &self, hash: String, limit: i32, lt: i32, ) -> Result> { - todo!() + let mut transactions = Vec::new(); + + // Retrieve the transaction ID(s) associated with the block hash + match self.db.get("transaction_mapper", hash.as_bytes()) { + Ok(Some(txn_id_bytes)) => { + // Assuming one block hash maps to multiple transaction IDs + println!("the value in trx{:?}", txn_id_bytes); + let txn_ids: Vec = serde_json::from_slice::>(&txn_id_bytes)?; + + for txn_id in txn_ids.iter().take(limit as usize) { + // Retrieve the transaction details for each transaction ID + match self.db.get("transaction", txn_id.as_bytes()) { + Ok(Some(txn_bytes)) => { + let txn: Transaction = serde_json::from_slice(&txn_bytes)?; + transactions.push(txn); + } + Ok(None) => { + return Err(anyhow!("Transaction not found for ID: {}", txn_id)) + } + Err(e) => return Err(anyhow!("Database error: {}", e)), + } + } + } + Ok(None) => return Err(anyhow!("No transactions found for block hash: {}", hash)), + Err(e) => return Err(anyhow!("Database error: {}", e)), + } + + Ok(transactions) } } diff --git a/lib/ain-ocean/src/data_acces/transaction_vin.rs b/lib/ain-ocean/src/data_acces/transaction_vin.rs index 8964743c4d2..20b80f81093 100644 --- a/lib/ain-ocean/src/data_acces/transaction_vin.rs +++ b/lib/ain-ocean/src/data_acces/transaction_vin.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::transaction_vin::TransactionVin, }; @@ -16,7 +16,13 @@ impl TransactionVinDb { match serde_json::to_string(&trx_vin) { Ok(value) => { let key = trx_vin.id.clone(); - self.db.put("oracle", key.as_bytes(), value.as_bytes())?; + self.db + .put("transaction_vin", key.as_bytes(), value.as_bytes())?; + self.db.put( + "transaction_vin_mapper", + trx_vin.txid.as_bytes(), + trx_vin.id.as_bytes(), + )?; Ok(()) } Err(e) => Err(anyhow!(e)), @@ -28,7 +34,38 @@ impl TransactionVinDb { Err(e) => Err(anyhow!(e)), } } - pub async fn query(&self, txid: String, limit: i32, lt: i32) -> Result> { - todo!() + pub async fn query( + &self, + tx_id: String, + limit: i32, + lt: i32, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("transaction_vin", IteratorMode::End)?; + let mut trx_vin: Vec = Vec::new(); + let collected_blocks: Vec<_> = iterator.collect(); + + for result in collected_blocks.into_iter().rev() { + let (key, value) = match result { + Ok((key, value)) => (key, value), + Err(err) => return Err(anyhow!("Error during iteration: {}", err)), + }; + + let vin: TransactionVin = serde_json::from_slice(&value)?; + if vin.txid == tx_id { + trx_vin.push(vin); + if trx_vin.len() as i32 >= limit { + break; + } + } + } + + // Sort blocks based on the specified sort order + match sort_order { + SortOrder::Ascending => trx_vin.sort_by(|a, b| a.txid.cmp(&b.txid)), + SortOrder::Descending => trx_vin.sort_by(|a, b| b.txid.cmp(&a.txid)), + } + + Ok(trx_vin) } } diff --git a/lib/ain-ocean/src/data_acces/transaction_vout.rs b/lib/ain-ocean/src/data_acces/transaction_vout.rs index 9f11f982789..aab89713d08 100644 --- a/lib/ain-ocean/src/data_acces/transaction_vout.rs +++ b/lib/ain-ocean/src/data_acces/transaction_vout.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; +use rocksdb::IteratorMode; use crate::{ - database::db_manager::{ColumnFamilyOperations, RocksDB}, + database::db_manager::{ColumnFamilyOperations, RocksDB, SortOrder}, model::transaction_vout::TransactionVout, }; @@ -12,8 +12,8 @@ pub struct TransactionVoutDb { } impl TransactionVoutDb { - pub async fn get(&self, txid: String, n: i64) -> Result> { - match self.db.get("transaction_vout", txid.as_bytes()) { + pub async fn get(&self, tx_id: String, n: i64) -> Result> { + match self.db.get("transaction_vout", tx_id.as_bytes()) { Ok(Some(value)) => { let master_node: TransactionVout = serde_json::from_slice(&value).map_err(|e| anyhow!(e))?; @@ -24,23 +24,59 @@ impl TransactionVoutDb { } } pub async fn store(&self, trx_out: TransactionVout) -> Result<()> { - match serde_json::to_string(&trx_out) { - Ok(value) => { - let key = trx_out.id.clone(); - self.db - .put("transaction_vout", key.as_bytes(), value.as_bytes())?; - Ok(()) - } - Err(e) => Err(anyhow!(e)), - } + let value = serde_json::to_string(&trx_out)?; + let key = trx_out.id.clone(); + self.db + .put("transaction_vout", key.as_bytes(), value.as_bytes())?; + + // Accumulate transaction vout IDs for each txid + let mut vout_ids = match self + .db + .get("transaction_vout_mapper", trx_out.txid.as_bytes())? + { + Some(bytes) => serde_json::from_slice::>(&bytes)?, + None => vec![], + }; + vout_ids.push(trx_out.id); + let vout_ids_value = serde_json::to_string(&vout_ids)?; + self.db.put( + "transaction_vout_mapper", + trx_out.txid.as_bytes(), + vout_ids_value.as_bytes(), + )?; + + Ok(()) } - pub async fn delete(&self, id: String) -> Result<()> { - match self.db.delete("transaction_vout", id.as_bytes()) { + pub async fn delete(&self, trx_id: String) -> Result<()> { + match self.db.delete("transaction_vout", trx_id.as_bytes()) { Ok(_) => Ok(()), Err(e) => Err(anyhow!(e)), } } - pub async fn query(&self, txid: String, limit: i32, lt: i32) -> Result { - todo!() + pub async fn query( + &self, + tx_id: String, + limit: i32, + lt: i32, + sort_order: SortOrder, + ) -> Result> { + let iterator = self.db.iterator("transaction_vout", IteratorMode::End)?; + let mut trx_vout: Vec = Vec::new(); + if let Some(bytes) = self.db.get("transaction_vout_mapper", tx_id.as_bytes())? { + let vout_ids: Vec = serde_json::from_slice(&bytes)?; + for vout_id in vout_ids.iter().take(limit as usize) { + if let Some(vout_bytes) = self.db.get("transaction_vout", vout_id.as_bytes())? { + let vout: TransactionVout = serde_json::from_slice(&vout_bytes)?; + trx_vout.push(vout); + } + } + } + + match sort_order { + SortOrder::Ascending => trx_vout.sort_by(|a, b| a.n.cmp(&b.n)), + SortOrder::Descending => trx_vout.sort_by(|a, b| b.n.cmp(&a.n)), + } + + Ok(trx_vout) } } diff --git a/lib/ain-ocean/src/data_acces/vault_auction_batch_history.rs b/lib/ain-ocean/src/data_acces/vault_auction_batch_history.rs index eaf89997f83..1f91c9c20a4 100644 --- a/lib/ain-ocean/src/data_acces/vault_auction_batch_history.rs +++ b/lib/ain-ocean/src/data_acces/vault_auction_batch_history.rs @@ -1,5 +1,4 @@ -use anyhow::{anyhow, Error, Result}; -use serde::{Deserialize, Serialize}; +use anyhow::{anyhow, Result}; use serde_json; use crate::{ diff --git a/lib/ain-ocean/src/database/db_manager.rs b/lib/ain-ocean/src/database/db_manager.rs index 4a7392e186c..1c06b1901c8 100644 --- a/lib/ain-ocean/src/database/db_manager.rs +++ b/lib/ain-ocean/src/database/db_manager.rs @@ -5,7 +5,7 @@ use bitcoin::{ blockdata::block::{Block, Header}, consensus::encode::serialize, }; -use rocksdb::{ColumnFamilyDescriptor, DBIterator, IteratorMode, Options, DB}; +use rocksdb::{ColumnFamilyDescriptor, DBIterator, Direction, IteratorMode, Options, DB}; use serde::{Deserialize, Serialize}; use crate::model::oracle::Oracle; @@ -16,6 +16,12 @@ pub struct RocksDB { cfs: HashSet, } +#[derive(Debug, PartialEq, Clone)] +pub enum SortOrder { + Ascending, + Descending, +} + pub trait ColumnFamilyOperations { fn get(&self, cf_name: &str, key: &[u8]) -> Result>>; fn put(&self, cf_name: &str, key: &[u8], value: &[u8]) -> Result<()>; @@ -281,3 +287,12 @@ impl ColumnFamilyOperations for RocksDB { } } } + +impl<'a> From for IteratorMode<'a> { + fn from(sort_order: SortOrder) -> Self { + match sort_order { + SortOrder::Ascending => IteratorMode::Start, + SortOrder::Descending => IteratorMode::From(b"", Direction::Reverse), + } + } +} diff --git a/lib/ain-ocean/src/model/block.rs b/lib/ain-ocean/src/model/block.rs index 55517511da5..bf542a2c276 100644 --- a/lib/ain-ocean/src/model/block.rs +++ b/lib/ain-ocean/src/model/block.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Block { pub id: String, diff --git a/lib/ain-ocean/src/model/poolswap.rs b/lib/ain-ocean/src/model/poolswap.rs index 777d7e3a3f9..16dd5d0ba58 100644 --- a/lib/ain-ocean/src/model/poolswap.rs +++ b/lib/ain-ocean/src/model/poolswap.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PoolSwap { pub id: String, @@ -13,7 +13,7 @@ pub struct PoolSwap { pub block: PoolSwapBlock, } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PoolSwapBlock { pub hash: String, diff --git a/src/dfi/validation.cpp b/src/dfi/validation.cpp index 97e00004860..ea006038ec2 100644 --- a/src/dfi/validation.cpp +++ b/src/dfi/validation.cpp @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include #include