1#![deny(clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)]
3#![allow(clippy::module_name_repetitions)]
4#![allow(clippy::must_use_candidate)]
5#![allow(clippy::needless_lifetimes)]
6
7#[cfg(feature = "uniffi")]
8uniffi::setup_scaffolding!();
9
10pub mod endpoint;
11
12use std::fmt;
13use std::str::FromStr;
14
15use config::MetaClientConfig;
16use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
17use fedimint_core::encoding::{Decodable, DecodeError, Encodable};
18use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
19use fedimint_core::plugin_types_trait_impl_common;
20use fedimint_logging::LOG_MODULE_META;
21use serde::de::{self, Visitor};
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23use thiserror::Error;
24use tracing::warn;
25pub mod config;
29
30pub const KIND: ModuleKind = ModuleKind::from_static_str("meta");
32
33pub const MODULE_CONSENSUS_VERSION: ModuleConsensusVersion = ModuleConsensusVersion::new(0, 0);
35
36pub const DEFAULT_META_KEY: MetaKey = MetaKey(0);
39
40#[derive(
47 Debug,
48 Copy,
49 Clone,
50 Encodable,
51 Decodable,
52 PartialEq,
53 Eq,
54 PartialOrd,
55 Ord,
56 Hash,
57 Serialize,
58 Deserialize,
59)]
60pub struct MetaKey(pub u8);
61
62#[cfg(feature = "uniffi")]
63uniffi::custom_newtype!(MetaKey, u8);
64
65impl fmt::Display for MetaKey {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 self.0.fmt(f)
68 }
69}
70
71impl FromStr for MetaKey {
72 type Err = <u8 as FromStr>::Err;
73
74 fn from_str(s: &str) -> Result<Self, Self::Err> {
75 Ok(Self(FromStr::from_str(s)?))
76 }
77}
78#[derive(Debug, Clone, Encodable, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub struct MetaValue(Vec<u8>);
85
86#[cfg(feature = "uniffi")]
87uniffi::custom_newtype!(MetaValue, Vec<u8>);
88
89impl FromStr for MetaValue {
90 type Err = anyhow::Error;
91
92 fn from_str(s: &str) -> Result<Self, Self::Err> {
93 Ok(Self(hex::decode(s)?))
94 }
95}
96
97impl fmt::Display for MetaValue {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.write_str(&hex::encode(&self.0))
100 }
101}
102impl From<&[u8]> for MetaValue {
103 fn from(value: &[u8]) -> Self {
104 Self(value.to_vec())
105 }
106}
107
108impl MetaValue {
109 pub const MAX_LEN_BYTES: usize = 1024 * 1024 * 1024;
112
113 pub fn as_slice(&self) -> &[u8] {
114 &self.0
115 }
116
117 pub fn to_json(&self) -> anyhow::Result<serde_json::Value> {
118 Ok(serde_json::from_slice(&self.0)?)
119 }
120
121 pub fn to_json_lossy(&self) -> anyhow::Result<serde_json::Value> {
123 let maybe_lossy_str = String::from_utf8_lossy(self.as_slice());
124
125 if maybe_lossy_str.as_bytes() != self.as_slice() {
126 warn!(target: LOG_MODULE_META, "Value contains invalid utf-8, converting to lossy string");
127 }
128
129 Ok(serde_json::from_str(&maybe_lossy_str)?)
130 }
131}
132
133impl Decodable for MetaValue {
134 fn consensus_decode_partial<R: std::io::Read>(
135 r: &mut R,
136 modules: &fedimint_core::module::registry::ModuleDecoderRegistry,
137 ) -> Result<Self, fedimint_core::encoding::DecodeError> {
138 let bytes = Vec::consensus_decode_partial(r, modules)?;
139
140 if Self::MAX_LEN_BYTES < bytes.len() {
141 return Err(DecodeError::new_custom(anyhow::format_err!("Too long")));
142 }
143
144 Ok(Self(bytes))
145 }
146}
147impl Serialize for MetaValue {
148 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: Serializer,
151 {
152 assert!(self.0.len() <= Self::MAX_LEN_BYTES);
153 serializer.serialize_str(&hex::encode(&self.0))
154 }
155}
156
157impl<'de> Deserialize<'de> for MetaValue {
159 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160 where
161 D: Deserializer<'de>,
162 {
163 struct MetaValueVisitor;
164
165 impl Visitor<'_> for MetaValueVisitor {
166 type Value = MetaValue;
167
168 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
169 formatter.write_str("a hex string")
170 }
171
172 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
173 where
174 E: de::Error,
175 {
176 let val = hex::decode(value).map_err(de::Error::custom)?;
177
178 if MetaValue::MAX_LEN_BYTES < val.len() {
179 return Err(de::Error::custom("Too long"));
180 }
181
182 Ok(MetaValue(val))
183 }
184 }
185
186 deserializer.deserialize_str(MetaValueVisitor)
187 }
188}
189
190#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
191pub struct MetaConsensusItem {
192 pub salt: u64,
196 pub key: MetaKey,
197 pub value: MetaValue,
198}
199
200#[derive(Debug, Clone, Encodable, Decodable, Serialize, Deserialize, PartialEq, Eq)]
202#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
203pub struct MetaConsensusValue {
204 pub revision: u64,
205 pub value: MetaValue,
206}
207
208#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
210pub struct MetaInput;
211
212#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
214pub struct MetaOutput;
215
216#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
218pub struct MetaOutputOutcome;
219
220#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
222pub enum MetaInputError {
223 #[error("This module does not support inputs")]
224 NotSupported,
225}
226
227#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
229pub enum MetaOutputError {
230 #[error("This module does not support outputs")]
231 NotSupported,
232}
233
234pub struct MetaModuleTypes;
236
237plugin_types_trait_impl_common!(
239 KIND,
240 MetaModuleTypes,
241 MetaClientConfig,
242 MetaInput,
243 MetaOutput,
244 MetaOutputOutcome,
245 MetaConsensusItem,
246 MetaInputError,
247 MetaOutputError
248);
249
250#[derive(Debug)]
251pub struct MetaCommonInit;
252
253impl CommonModuleInit for MetaCommonInit {
254 const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
255 const KIND: ModuleKind = KIND;
256
257 type ClientConfig = MetaClientConfig;
258
259 fn decoder() -> Decoder {
260 MetaModuleTypes::decoder_builder().build()
261 }
262}
263
264impl fmt::Display for MetaClientConfig {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 write!(f, "MetaClientConfig")
267 }
268}
269impl fmt::Display for MetaInput {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 write!(f, "MetaInput")
272 }
273}
274
275impl fmt::Display for MetaOutput {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 write!(f, "MetaOutput")
278 }
279}
280
281impl fmt::Display for MetaOutputOutcome {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 write!(f, "MetaOutputOutcome")
284 }
285}
286
287impl fmt::Display for MetaConsensusItem {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 write!(f, "Meta - len: {}", self.value.0.len())
290 }
291}