Skip to content

Commit

Permalink
Add raw parser for db integrity checks (#3122)
Browse files Browse the repository at this point in the history
* Parse leveldb entries

* Add VMDomainEdge

* Output as json

---------

Co-authored-by: jouzo <[email protected]>
  • Loading branch information
prasannavl and Jouzo authored Nov 14, 2024
1 parent 4e3933b commit 058b490
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 4 deletions.
13 changes: 13 additions & 0 deletions lib/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"ain-*",
"cli",
"parser"
]

default-members = [
Expand Down
1 change: 1 addition & 0 deletions lib/ain-dftx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ bitcoin.workspace = true
hex.workspace = true
bitflags = "2.4.1"
lazy_static.workspace = true
serde.workspace = true
1 change: 1 addition & 0 deletions lib/ain-dftx/src/types/balance.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use ain_macros::ConsensusEncoding;
use bitcoin::{io, ScriptBuf, VarInt};
use serde::Serialize;

use super::common::CompactVec;

Expand Down
3 changes: 2 additions & 1 deletion lib/ain-dftx/src/types/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use bitcoin::{
consensus::{Decodable, Encodable},
io::{self, ErrorKind},
};
use serde::Serialize;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CompactVec<T>(Vec<T>);
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions lib/ain-dftx/src/types/price.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
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 {
pub token: String,
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,
Expand Down
1 change: 1 addition & 0 deletions lib/ain-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions lib/parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
160 changes: 160 additions & 0 deletions lib/parser/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<u8>,
pub value: Vec<u8>,
}

impl RawDbEntry {
pub fn parse(input: &str) -> Result<Self, Box<dyn std::error::Error>> {
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<K, V> {
pub key: K,
pub value: V,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum VMDomainEdge {
DVMToEVM,
EVMToDVM,
}

impl Decodable for VMDomainEdge {
fn consensus_decode<R: io::Read + ?Sized>(
reader: &mut R,
) -> Result<Self, bitcoin::consensus::encode::Error> {
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<u8>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum Prefix {
ByBalance(PrefixedData<BalanceKey, i64>),
ByHeight(PrefixedData<ScriptBuf, u32>),
VMDomainTxEdge(PrefixedData<(VMDomainEdge, String), String>),
VMDomainBlockEdge(PrefixedData<(VMDomainEdge, String), String>),
Undo(PrefixedData<UndoKey, Undo>),
}

impl TryFrom<RawDbEntry> for Prefix {
type Error = bitcoin::consensus::encode::Error;

fn try_from(value: RawDbEntry) -> Result<Self, Self::Error> {
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<K, V> TryFrom<RawDbEntry> for PrefixedData<K, V>
where
K: Decodable,
V: Decodable,
{
type Error = bitcoin::consensus::encode::Error;

fn try_from(value: RawDbEntry) -> Result<Self, Self::Error> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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);
}
}

0 comments on commit 058b490

Please sign in to comment.