#![deny(clippy::pedantic)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
pub mod endpoint;
use std::fmt;
use std::str::FromStr;
use config::MetaClientConfig;
use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
use fedimint_core::encoding::{Decodable, DecodeError, Encodable};
use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
use fedimint_core::plugin_types_trait_impl_common;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use tracing::warn;
pub mod config;
pub const KIND: ModuleKind = ModuleKind::from_static_str("meta");
pub const MODULE_CONSENSUS_VERSION: ModuleConsensusVersion = ModuleConsensusVersion::new(0, 0);
pub const DEFAULT_META_KEY: MetaKey = MetaKey(0);
#[derive(
Debug,
Copy,
Clone,
Encodable,
Decodable,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
pub struct MetaKey(pub u8);
impl fmt::Display for MetaKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for MetaKey {
type Err = <u8 as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(FromStr::from_str(s)?))
}
}
#[derive(Debug, Clone, Encodable, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MetaValue(Vec<u8>);
impl FromStr for MetaValue {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(hex::decode(s)?))
}
}
impl fmt::Display for MetaValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&hex::encode(&self.0))
}
}
impl From<&[u8]> for MetaValue {
fn from(value: &[u8]) -> Self {
Self(value.to_vec())
}
}
impl MetaValue {
pub const MAX_LEN_BYTES: usize = 1024 * 1024 * 1024;
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn to_json(&self) -> anyhow::Result<serde_json::Value> {
Ok(serde_json::from_slice(&self.0)?)
}
pub fn to_json_lossy(&self) -> anyhow::Result<serde_json::Value> {
let maybe_lossy_str = String::from_utf8_lossy(self.as_slice());
if maybe_lossy_str.as_bytes() != self.as_slice() {
warn!("Value contains invalid utf-8, converting to lossy string");
}
Ok(serde_json::from_str(&maybe_lossy_str)?)
}
}
impl Decodable for MetaValue {
fn consensus_decode<R: std::io::Read>(
r: &mut R,
modules: &fedimint_core::module::registry::ModuleDecoderRegistry,
) -> Result<Self, fedimint_core::encoding::DecodeError> {
let bytes = Vec::consensus_decode(r, modules)?;
if Self::MAX_LEN_BYTES < bytes.len() {
return Err(DecodeError::new_custom(anyhow::format_err!("Too long")));
}
Ok(Self(bytes))
}
}
impl Serialize for MetaValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
assert!(self.0.len() <= Self::MAX_LEN_BYTES);
serializer.serialize_str(&hex::encode(&self.0))
}
}
impl<'de> Deserialize<'de> for MetaValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MetaValueVisitor;
impl<'de> Visitor<'de> for MetaValueVisitor {
type Value = MetaValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a hex string")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let val = hex::decode(value).map_err(de::Error::custom)?;
if MetaValue::MAX_LEN_BYTES < val.len() {
return Err(de::Error::custom("Too long"));
}
Ok(MetaValue(val))
}
}
deserializer.deserialize_str(MetaValueVisitor)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
pub struct MetaConsensusItem {
pub salt: u64,
pub key: MetaKey,
pub value: MetaValue,
}
#[derive(Debug, Clone, Encodable, Decodable, Serialize, Deserialize, PartialEq, Eq)]
pub struct MetaConsensusValue {
pub revision: u64,
pub value: MetaValue,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MetaInput;
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MetaOutput;
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MetaOutputOutcome;
#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
pub enum MetaInputError {
#[error("This module does not support inputs")]
NotSupported,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
pub enum MetaOutputError {
#[error("This module does not support outputs")]
NotSupported,
}
pub struct MetaModuleTypes;
plugin_types_trait_impl_common!(
KIND,
MetaModuleTypes,
MetaClientConfig,
MetaInput,
MetaOutput,
MetaOutputOutcome,
MetaConsensusItem,
MetaInputError,
MetaOutputError
);
#[derive(Debug)]
pub struct MetaCommonInit;
impl CommonModuleInit for MetaCommonInit {
const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
const KIND: ModuleKind = KIND;
type ClientConfig = MetaClientConfig;
fn decoder() -> Decoder {
MetaModuleTypes::decoder_builder().build()
}
}
impl fmt::Display for MetaClientConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MetaClientConfig")
}
}
impl fmt::Display for MetaInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MetaInput")
}
}
impl fmt::Display for MetaOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MetaOutput")
}
}
impl fmt::Display for MetaOutputOutcome {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MetaOutputOutcome")
}
}
impl fmt::Display for MetaConsensusItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MetaConsensusItem")
}
}