diff --git a/lib/Cargo.lock b/lib/Cargo.lock index e55bb2d6a5..1606660e17 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -128,6 +128,7 @@ dependencies = [ "bitflags 2.6.0", "hex", "lazy_static", + "serde", ] [[package]] @@ -3511,6 +3512,18 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parser" +version = "0.1.0" +dependencies = [ + "ain-dftx", + "ain-macros", + "bitcoin", + "hex", + "serde", + "serde_json", +] + [[package]] name = "password-hash" version = "0.4.2" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 1c25deda6f..4a3c3aafea 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -2,6 +2,7 @@ members = [ "ain-*", "cli", + "parser" ] default-members = [ diff --git a/lib/ain-dftx/Cargo.toml b/lib/ain-dftx/Cargo.toml index b0f0d4ee42..1a3cddcb52 100644 --- a/lib/ain-dftx/Cargo.toml +++ b/lib/ain-dftx/Cargo.toml @@ -11,3 +11,4 @@ bitcoin.workspace = true hex.workspace = true bitflags = "2.4.1" lazy_static.workspace = true +serde.workspace = true diff --git a/lib/ain-dftx/src/types/balance.rs b/lib/ain-dftx/src/types/balance.rs index 44f2ca22d0..20bddb5163 100644 --- a/lib/ain-dftx/src/types/balance.rs +++ b/lib/ain-dftx/src/types/balance.rs @@ -1,5 +1,6 @@ use ain_macros::ConsensusEncoding; use bitcoin::{io, ScriptBuf, VarInt}; +use serde::Serialize; use super::common::CompactVec; diff --git a/lib/ain-dftx/src/types/common.rs b/lib/ain-dftx/src/types/common.rs index 154f8835a8..0724ee387c 100644 --- a/lib/ain-dftx/src/types/common.rs +++ b/lib/ain-dftx/src/types/common.rs @@ -4,6 +4,7 @@ use bitcoin::{ consensus::{Decodable, Encodable}, io::{self, ErrorKind}, }; +use serde::Serialize; #[derive(Debug, PartialEq, Eq, Clone)] pub struct CompactVec(Vec); @@ -110,7 +111,7 @@ impl Decodable for RawBytes { /// In the rust-bitcoin library, variable-length integers are implemented as CompactSize. /// See [issue #1016](https://github.com/rust-bitcoin/rust-bitcoin/issues/1016) -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct VarInt(pub u64); impl Encodable for VarInt { diff --git a/lib/ain-dftx/src/types/price.rs b/lib/ain-dftx/src/types/price.rs index bba56a5f8a..b87972dbc4 100644 --- a/lib/ain-dftx/src/types/price.rs +++ b/lib/ain-dftx/src/types/price.rs @@ -1,7 +1,7 @@ +use super::common::CompactVec; use ain_macros::ConsensusEncoding; use bitcoin::io; - -use super::common::CompactVec; +use serde::Serialize; #[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CurrencyPair { @@ -9,7 +9,7 @@ pub struct CurrencyPair { pub currency: String, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone, Serialize)] pub struct TokenAmount { pub currency: String, pub amount: i64, diff --git a/lib/ain-macros/src/lib.rs b/lib/ain-macros/src/lib.rs index 958c3a28c1..47f1da5be6 100644 --- a/lib/ain-macros/src/lib.rs +++ b/lib/ain-macros/src/lib.rs @@ -115,6 +115,7 @@ pub fn ocean_endpoint(_attr: TokenStream, item: TokenStream) -> TokenStream { TokenStream::from(expanded) } + #[proc_macro_derive(ConsensusEncoding)] pub fn consensus_encoding_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/lib/parser/Cargo.toml b/lib/parser/Cargo.toml new file mode 100644 index 0000000000..cf49f40afd --- /dev/null +++ b/lib/parser/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "parser" +version = "0.1.0" +edition = "2021" + +[dependencies] +ain-macros.workspace = true +ain-dftx.workspace = true +bitcoin = { workspace = true, features = ["serde"] } +hex.workspace = true +serde_json.workspace = true +serde.workspace = true diff --git a/lib/parser/src/main.rs b/lib/parser/src/main.rs new file mode 100644 index 0000000000..72cff934c2 --- /dev/null +++ b/lib/parser/src/main.rs @@ -0,0 +1,160 @@ +use ain_dftx::balance::TokenBalanceVarInt; +use ain_dftx::common::VarInt; +use ain_dftx::price::TokenAmount; +use ain_macros::ConsensusEncoding; +use bitcoin::{consensus::Decodable, ScriptBuf}; +use bitcoin::{io, Txid}; +use serde::Serialize; +use std::io::BufRead; + +#[derive(Debug)] +pub struct RawDbEntry { + pub prefix: u8, + pub key: Vec, + pub value: Vec, +} + +impl RawDbEntry { + pub fn parse(input: &str) -> Result> { + let parts: Vec<&str> = input.split_whitespace().collect(); + if parts.len() != 3 { + return Err("Invalid input format: expected 3 space-separated parts".into()); + } + + let prefix = u8::from_str_radix(parts[0], 16)?; + + let key = hex::decode(parts[1])?; + let value = hex::decode(parts[2])?; + + Ok(RawDbEntry { prefix, key, value }) + } +} + +#[derive(ConsensusEncoding, Debug, Clone, PartialEq, Eq, Serialize)] +pub struct BalanceKey { + pub owner: ScriptBuf, + pub token_id: VarInt, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct PrefixedData { + pub key: K, + pub value: V, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum VMDomainEdge { + DVMToEVM, + EVMToDVM, +} + +impl Decodable for VMDomainEdge { + fn consensus_decode( + reader: &mut R, + ) -> Result { + let edge = u8::consensus_decode(reader)?; + match edge { + 1 => Ok(Self::DVMToEVM), + 2 => Ok(Self::EVMToDVM), + _ => Err(bitcoin::consensus::encode::Error::ParseFailed( + "Unsupported VMDomainEdge", + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +struct UndoKey { + height: u32, + id: Txid, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +struct Undo { + data: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum Prefix { + ByBalance(PrefixedData), + ByHeight(PrefixedData), + VMDomainTxEdge(PrefixedData<(VMDomainEdge, String), String>), + VMDomainBlockEdge(PrefixedData<(VMDomainEdge, String), String>), + Undo(PrefixedData), +} + +impl TryFrom for Prefix { + type Error = bitcoin::consensus::encode::Error; + + fn try_from(value: RawDbEntry) -> Result { + match value.prefix { + b'a' => Ok(Prefix::ByBalance(PrefixedData::try_from(value)?)), + b'b' => Ok(Prefix::ByHeight(PrefixedData::try_from(value)?)), + b'e' => Ok(Prefix::VMDomainTxEdge(PrefixedData::try_from(value)?)), + b'N' => Ok(Prefix::VMDomainBlockEdge(PrefixedData::try_from(value)?)), + _ => Err(bitcoin::consensus::encode::Error::ParseFailed( + "Unknown prefix", + )), + } + } +} + +impl TryFrom for PrefixedData +where + K: Decodable, + V: Decodable, +{ + type Error = bitcoin::consensus::encode::Error; + + fn try_from(value: RawDbEntry) -> Result { + let mut key_slice = value.key.as_slice(); + let mut value_slice = value.value.as_slice(); + + let key = K::consensus_decode(&mut key_slice)?; + let value = V::consensus_decode(&mut value_slice)?; + Ok(Self { key, value }) + } +} + +fn process_line(line: &str) -> Result<(), Box> { + let raw_entry = RawDbEntry::parse(line)?; + + match Prefix::try_from(raw_entry) { + Ok(entry) => println!("{}", serde_json::to_string(&entry)?), + Err(_) => {} + } + + Ok(()) +} + +fn main() -> Result<(), Box> { + let stdin = std::io::stdin(); + let reader = stdin.lock(); + + for line in reader.lines() { + let line = line?; + if !line.trim().is_empty() { + if let Err(e) = process_line(&line) { + eprintln!("Error processing line: {}", e); + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse() { + let input = "65 014031386566363362646661313837366264383735333933636363626133306430356465356238306261643161646536616131383931376261313939343532353163 4063343432376563623137373563333038346637313439383235313965616161323665636536643561653462303534626239393561326434666130346330353065"; + + let raw_entry = RawDbEntry::parse(input).unwrap(); + println!("raw_entry : {:?}", raw_entry); + let entry = Prefix::try_from(raw_entry).unwrap(); + + println!("entry : {:?}", entry); + } +}