diff --git a/consensus/src/verifier.rs b/consensus/src/verifier.rs new file mode 100644 index 00000000..f96d1b3f --- /dev/null +++ b/consensus/src/verifier.rs @@ -0,0 +1,112 @@ +use futures::join; +use monero_serai::{block::Block, transaction::Transaction}; +use tower::ServiceExt; +use tracing::instrument; + +use crate::{ + block::{pow::difficulty::DifficultyCache, weight::BlockWeightsCache}, + hardforks::{HardForkConfig, HardForkState}, + ConsensusError, Database, DatabaseRequest, DatabaseResponse, +}; + +pub struct Config { + hard_fork_cfg: HardForkConfig, +} + +impl Config { + pub fn main_net() -> Config { + Config { + hard_fork_cfg: HardForkConfig::main_net(), + } + } +} + +#[derive(Clone)] +struct State { + block_weight: BlockWeightsCache, + difficulty: DifficultyCache, + hard_fork: HardForkState, + chain_height: u64, + top_hash: [u8; 32], +} + +impl State { + pub async fn init( + config: Config, + mut database: D, + ) -> Result { + let DatabaseResponse::ChainHeight(chain_height) = database + .ready() + .await? + .call(DatabaseRequest::ChainHeight) + .await? + else { + panic!("Database sent incorrect response") + }; + + Self::init_at_chain_height(config, chain_height, database).await + } + + #[instrument(name = "init_state", skip_all)] + pub async fn init_at_chain_height( + config: Config, + chain_height: u64, + mut database: D, + ) -> Result { + let DatabaseResponse::BlockHash(top_hash) = database + .ready() + .await? + .call(DatabaseRequest::BlockHash(chain_height - 1)) + .await? + else { + panic!("Database sent incorrect response") + }; + + let (block_weight, difficulty, hard_fork) = join!( + BlockWeightsCache::init_from_chain_height(chain_height, database.clone()), + DifficultyCache::init_from_chain_height(chain_height, database.clone()), + HardForkState::init_from_chain_height(config.hard_fork_cfg, chain_height, database) + ); + + Ok(State { + block_weight: block_weight?, + difficulty: difficulty?, + hard_fork: hard_fork?, + chain_height, + top_hash, + }) + } +} + +pub struct Verifier { + state: State, +} + +impl Verifier { + pub async fn init( + config: Config, + mut database: D, + ) -> Result { + let DatabaseResponse::ChainHeight(chain_height) = database + .ready() + .await? + .call(DatabaseRequest::ChainHeight) + .await? + else { + panic!("Database sent incorrect response") + }; + + Self::init_at_chain_height(config, chain_height, database).await + } + + #[instrument(name = "init_verifier", skip_all)] + pub async fn init_at_chain_height( + config: Config, + chain_height: u64, + database: D, + ) -> Result { + Ok(Verifier { + state: State::init_at_chain_height(config, chain_height, database).await?, + }) + } +} diff --git a/net/monero-wire/Cargo.toml b/net/monero-wire/Cargo.toml index 3d3aee5c..cf927503 100644 --- a/net/monero-wire/Cargo.toml +++ b/net/monero-wire/Cargo.toml @@ -10,6 +10,8 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi [dependencies] levin-cuprate = {path="../levin"} epee-encoding = { git = "https://github.com/boog900/epee-encoding"} +monero-epee-bin-serde = {git = "https://github.com/monero-rs/monero-epee-bin-serde.git"} +serde = {version = "1", features = ["derive"]} [dev-dependencies] hex = "0.4.3" diff --git a/net/monero-wire/src/messages/common.rs b/net/monero-wire/src/messages/common.rs index 1775091a..5228fb73 100644 --- a/net/monero-wire/src/messages/common.rs +++ b/net/monero-wire/src/messages/common.rs @@ -16,10 +16,12 @@ //! Common types that are used across multiple messages. // use epee_encoding::EpeeObject; +use serde::{Deserialize, Serialize}; use crate::NetworkAddress; mod builders; +mod serde_helpers; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PeerSupportFlags(u32); @@ -195,7 +197,7 @@ impl TransactionBlobs { } /// A Block that can contain transactions -#[derive(Clone, Debug, EpeeObject, PartialEq, Eq)] +#[derive(Clone, Debug, EpeeObject, Serialize, Deserialize, PartialEq, Eq)] pub struct BlockCompleteEntry { /// True if tx data is pruned #[epee_default(false)] diff --git a/net/monero-wire/src/messages/common/serde_helpers.rs b/net/monero-wire/src/messages/common/serde_helpers.rs new file mode 100644 index 00000000..88a9ffad --- /dev/null +++ b/net/monero-wire/src/messages/common/serde_helpers.rs @@ -0,0 +1,106 @@ +use serde::de::{Error, SeqAccess}; +use serde::ser::SerializeSeq; +use serde::{ + de::{Deserialize, Visitor}, + Deserializer, Serialize, Serializer, +}; +use std::fmt::Formatter; + +use super::TransactionBlobs; + +impl<'de> Deserialize<'de> for TransactionBlobs { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct TBVisitor; + + impl<'de> Visitor<'de> for TBVisitor { + type Value = TransactionBlobs; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + write!(formatter, "A sequence of transactions blob") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut normal = Vec::new(); + //let pruned = Vec::new(); + + while let Some(val) = seq.next_element::()? { + match val { + SingleBlob::Pruned(tx) => normal.push(tx), + } + } + + Ok(TransactionBlobs::Normal(normal)) + } + } + + deserializer.deserialize_any(TBVisitor) + } +} + +impl Serialize for TransactionBlobs { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + TransactionBlobs::Pruned(_) => todo!(), + TransactionBlobs::Normal(txs) => { + let mut seq_ser = serializer.serialize_seq(Some(txs.len()))?; + for tx in txs { + seq_ser.serialize_element(tx)?; + } + seq_ser.end() + } + } + } +} + +enum SingleBlob { + Pruned(Vec), +} + +impl<'de> Deserialize<'de> for SingleBlob { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct TBDSVisitor; + + impl<'de> Visitor<'de> for TBDSVisitor { + type Value = SingleBlob; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + write!(formatter, "A single transaction blob") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: Error, + { + Ok(SingleBlob::Pruned(v.into())) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: Error, + { + Ok(SingleBlob::Pruned(v)) + } + + fn visit_newtype_struct(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + todo!("Pruned blobs") + } + } + + deserializer.deserialize_any(TBDSVisitor) + } +}