hyperware_process_lib/
hypermap.rs

1use crate::eth::{
2    BlockNumberOrTag, EthError, Filter as EthFilter, FilterBlockOption, Log as EthLog, Provider,
3};
4use crate::hypermap::contract::getCall;
5use crate::hyperware::process::hypermap_cacher::{
6    CacherRequest, CacherResponse, CacherStatus, GetLogsByRangeOkResponse, GetLogsByRangeRequest,
7    LogsMetadata, Manifest, ManifestItem,
8};
9use crate::{net, sign};
10use crate::{print_to_terminal, Address as HyperAddress, Request};
11use alloy::hex;
12use alloy::rpc::types::request::{TransactionInput, TransactionRequest};
13use alloy_primitives::{keccak256, Address, Bytes, FixedBytes, B256};
14use alloy_sol_types::{SolCall, SolEvent, SolValue};
15use contract::tokenCall;
16use serde::{
17    self,
18    de::{self, MapAccess, Visitor},
19    ser::{SerializeMap, SerializeStruct},
20    Deserialize, Deserializer, Serialize, Serializer,
21};
22use std::error::Error;
23use std::fmt;
24use std::str::FromStr;
25
26/// hypermap deployment address on base
27pub const HYPERMAP_ADDRESS: &'static str = "0x000000000044C6B8Cb4d8f0F889a3E47664EAeda";
28/// base chain id
29#[cfg(not(feature = "simulation-mode"))]
30pub const HYPERMAP_CHAIN_ID: u64 = 8453; // base
31#[cfg(feature = "simulation-mode")]
32pub const HYPERMAP_CHAIN_ID: u64 = 31337; // fakenet
33/// first block (minus one) of hypermap deployment on base
34#[cfg(not(feature = "simulation-mode"))]
35pub const HYPERMAP_FIRST_BLOCK: u64 = 27_270_411;
36#[cfg(feature = "simulation-mode")]
37pub const HYPERMAP_FIRST_BLOCK: u64 = 0;
38/// the root hash of hypermap, empty bytes32
39pub const HYPERMAP_ROOT_HASH: &'static str =
40    "0x0000000000000000000000000000000000000000000000000000000000000000";
41
42#[derive(Serialize, Deserialize, Debug, Clone)]
43pub struct LogCache {
44    pub metadata: LogsMetadata,
45    pub logs: Vec<EthLog>,
46}
47
48const CACHER_REQUEST_TIMEOUT_S: u64 = 15;
49
50/// Sol structures for Hypermap requests
51pub mod contract {
52    use alloy_sol_macro::sol;
53
54    sol! {
55        /// Emitted when a new namespace entry is minted.
56        /// - parenthash: The hash of the parent namespace entry.
57        /// - childhash: The hash of the minted namespace entry's full path.
58        /// - labelhash: The hash of only the label (the final entry in the path).
59        /// - label: The label (the final entry in the path) of the new entry.
60        event Mint(
61            bytes32 indexed parenthash,
62            bytes32 indexed childhash,
63            bytes indexed labelhash,
64            bytes label
65        );
66
67        /// Emitted when a fact is created on an existing namespace entry.
68        /// Facts are immutable and may only be written once. A fact label is
69        /// prepended with an exclamation mark (!) to indicate that it is a fact.
70        /// - parenthash The hash of the parent namespace entry.
71        /// - facthash The hash of the newly created fact's full path.
72        /// - labelhash The hash of only the label (the final entry in the path).
73        /// - label The label of the fact.
74        /// - data The data stored at the fact.
75        event Fact(
76            bytes32 indexed parenthash,
77            bytes32 indexed facthash,
78            bytes indexed labelhash,
79            bytes label,
80            bytes data
81        );
82
83        /// Emitted when a new note is created on an existing namespace entry.
84        /// Notes are mutable. A note label is prepended with a tilde (~) to indicate
85        /// that it is a note.
86        /// - parenthash: The hash of the parent namespace entry.
87        /// - notehash: The hash of the newly created note's full path.
88        /// - labelhash: The hash of only the label (the final entry in the path).
89        /// - label: The label of the note.
90        /// - data: The data stored at the note.
91        event Note(
92            bytes32 indexed parenthash,
93            bytes32 indexed notehash,
94            bytes indexed labelhash,
95            bytes label,
96            bytes data
97        );
98
99        /// Emitted when a gene is set for an existing namespace entry.
100        /// A gene is a specific TBA implementation which will be applied to all
101        /// sub-entries of the namespace entry.
102        /// - entry: The namespace entry's namehash.
103        /// - gene: The address of the TBA implementation.
104        event Gene(bytes32 indexed entry, address indexed gene);
105
106        /// Emitted when the zeroth namespace entry is minted.
107        /// Occurs exactly once at initialization.
108        /// - zeroTba: The address of the zeroth TBA
109        event Zero(address indexed zeroTba);
110
111        /// Emitted when a namespace entry is transferred from one address
112        /// to another.
113        /// - from: The address of the sender.
114        /// - to: The address of the recipient.
115        /// - id: The namehash of the namespace entry (converted to uint256).
116        event Transfer(
117            address indexed from,
118            address indexed to,
119            uint256 indexed id
120        );
121
122        /// Emitted when a namespace entry is approved for transfer.
123        /// - owner: The address of the owner.
124        /// - spender: The address of the spender.
125        /// - id: The namehash of the namespace entry (converted to uint256).
126        event Approval(
127            address indexed owner,
128            address indexed spender,
129            uint256 indexed id
130        );
131
132        /// Emitted when an operator is approved for all of an owner's
133        /// namespace entries.
134        /// - owner: The address of the owner.
135        /// - operator: The address of the operator.
136        /// - approved: Whether the operator is approved.
137        event ApprovalForAll(
138            address indexed owner,
139            address indexed operator,
140            bool approved
141        );
142
143        /// Retrieves information about a specific namespace entry.
144        /// - namehash The namehash of the namespace entry to query.
145        ///
146        /// Returns:
147        /// - tba: The address of the token-bound account associated
148        /// with the entry.
149        /// - owner: The address of the entry owner.
150        /// - data: The note or fact bytes associated with the entry
151        /// (empty if not a note or fact).
152        function get(
153            bytes32 namehash
154        ) external view returns (address tba, address owner, bytes memory data);
155
156        /// Mints a new namespace entry and creates a token-bound account for
157        /// it. Must be called by a parent namespace entry token-bound account.
158        /// - who: The address to own the new namespace entry.
159        /// - label: The label to mint beneath the calling parent entry.
160        /// - initialization: Initialization calldata applied to the new
161        /// minted entry's token-bound account.
162        /// - erc721Data: ERC-721 data -- passed to comply with
163        /// `ERC721TokenReceiver.onERC721Received()`.
164        /// - implementation: The address of the implementation contract for
165        /// the token-bound account: this will be overriden by the gene if the
166        /// parent entry has one set.
167        ///
168        /// Returns:
169        /// - tba: The address of the new entry's token-bound account.
170        function mint(
171            address who,
172            bytes calldata label,
173            bytes calldata initialization,
174            bytes calldata erc721Data,
175            address implementation
176        ) external returns (address tba);
177
178        /// Sets the gene for the calling namespace entry.
179        /// - _gene: The address of the TBA implementation to set for all
180        /// children of the calling namespace entry.
181        function gene(address _gene) external;
182
183        /// Creates a new fact beneath the calling namespace entry.
184        /// - fact: The fact label to create. Must be prepended with an
185        /// exclamation mark (!).
186        /// - data: The data to be stored at the fact.
187        ///
188        /// Returns:
189        /// - facthash: The namehash of the newly created fact.
190        function fact(
191            bytes calldata fact,
192            bytes calldata data
193        ) external returns (bytes32 facthash);
194
195        /// Creates a new note beneath the calling namespace entry.
196        /// - note: The note label to create. Must be prepended with a tilde (~).
197        /// - data: The data to be stored at the note.
198        ///
199        /// Returns:
200        /// - notehash: The namehash of the newly created note.
201        function note(
202            bytes calldata note,
203            bytes calldata data
204        ) external returns (bytes32 notehash);
205
206        /// Retrieves the token-bound account address of a namespace entry.
207        /// - entry: The entry namehash (as uint256) for which to get the
208        /// token-bound account.
209        ///
210        /// Returns:
211        /// - tba: The token-bound account address of the namespace entry.
212        function tbaOf(uint256 entry) external view returns (address tba);
213
214        function balanceOf(address owner) external view returns (uint256);
215
216        function getApproved(uint256 entry) external view returns (address);
217
218        function isApprovedForAll(
219            address owner,
220            address operator
221        ) external view returns (bool);
222
223        function ownerOf(uint256 entry) external view returns (address);
224
225        function setApprovalForAll(address operator, bool approved) external;
226
227        function approve(address spender, uint256 entry) external;
228
229        function safeTransferFrom(address from, address to, uint256 id) external;
230
231        function safeTransferFrom(
232            address from,
233            address to,
234            uint256 id,
235            bytes calldata data
236        ) external;
237
238        function transferFrom(address from, address to, uint256 id) external;
239
240        function supportsInterface(bytes4 interfaceId) external view returns (bool);
241
242        /// Gets the token identifier that owns this token-bound account (TBA).
243        /// This is a core function of the ERC-6551 standard that returns the
244        /// identifying information about the NFT that owns this account.
245        /// The return values are constant and cannot change over time.
246        ///
247        /// Returns:
248        /// - chainId: The EIP-155 chain ID where the owning NFT exists
249        /// - tokenContract: The contract address of the owning NFT
250        /// - tokenId: The token ID of the owning NFT
251        function token()
252            external
253            view
254            returns (uint256 chainId, address tokenContract, uint256 tokenId);
255    }
256}
257
258/// A mint log from the hypermap, converted to a 'resolved' format using
259/// namespace data saved in the hns-indexer.
260#[derive(Clone, Debug, Deserialize, Serialize)]
261pub struct Mint {
262    pub name: String,
263    pub parent_path: String,
264}
265
266/// A note log from the hypermap, converted to a 'resolved' format using
267/// namespace data saved in the hns-indexer
268#[derive(Clone, Debug, Deserialize, Serialize)]
269pub struct Note {
270    pub note: String,
271    pub parent_path: String,
272    pub data: Bytes,
273}
274
275/// A fact log from the hypermap, converted to a 'resolved' format using
276/// namespace data saved in the hns-indexer
277#[derive(Clone, Debug, Deserialize, Serialize)]
278pub struct Fact {
279    pub fact: String,
280    pub parent_path: String,
281    pub data: Bytes,
282}
283
284/// Errors that can occur when decoding a log from the hypermap using
285/// [`decode_mint_log()`] or [`decode_note_log()`].
286#[derive(Clone, Debug, Deserialize, Serialize)]
287pub enum DecodeLogError {
288    /// The log's topic is not a mint or note event.
289    UnexpectedTopic(B256),
290    /// The name is not valid (according to [`valid_name`]).
291    InvalidName(String),
292    /// An error occurred while decoding the log.
293    DecodeError(String),
294    /// The parent name could not be resolved with `hns-indexer`.
295    UnresolvedParent(String),
296}
297
298impl fmt::Display for DecodeLogError {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        match self {
301            DecodeLogError::UnexpectedTopic(topic) => write!(f, "Unexpected topic: {:?}", topic),
302            DecodeLogError::InvalidName(name) => write!(f, "Invalid name: {}", name),
303            DecodeLogError::DecodeError(err) => write!(f, "Decode error: {}", err),
304            DecodeLogError::UnresolvedParent(parent) => {
305                write!(f, "Could not resolve parent: {}", parent)
306            }
307        }
308    }
309}
310
311impl Error for DecodeLogError {}
312
313/// Canonical function to determine if a hypermap entry is valid.
314///
315/// This checks a **single name**, not the full path-name. A full path-name
316/// is comprised of valid names separated by `.`
317pub fn valid_entry(entry: &str, note: bool, fact: bool) -> bool {
318    if note && fact {
319        return false;
320    }
321    if note {
322        valid_note(entry)
323    } else if fact {
324        valid_fact(entry)
325    } else {
326        valid_name(entry)
327    }
328}
329
330pub fn valid_name(name: &str) -> bool {
331    name.is_ascii()
332        && name.len() >= 1
333        && name
334            .chars()
335            .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
336}
337
338pub fn valid_note(note: &str) -> bool {
339    note.is_ascii()
340        && note.len() >= 2
341        && note.chars().next() == Some('~')
342        && note
343            .chars()
344            .skip(1)
345            .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
346}
347
348pub fn valid_fact(fact: &str) -> bool {
349    fact.is_ascii()
350        && fact.len() >= 2
351        && fact.chars().next() == Some('!')
352        && fact
353            .chars()
354            .skip(1)
355            .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
356}
357
358/// Produce a namehash from a hypermap name.
359pub fn namehash(name: &str) -> String {
360    let mut node = B256::default();
361
362    let mut labels: Vec<&str> = name.split('.').collect();
363    labels.reverse();
364
365    for label in labels.iter() {
366        let l = keccak256(label);
367        node = keccak256((node, l).abi_encode_packed());
368    }
369    format!("0x{}", hex::encode(node))
370}
371
372/// Decode a mint log from the hypermap into a 'resolved' format.
373///
374/// Uses [`valid_name()`] to check if the name is valid.
375pub fn decode_mint_log(log: &crate::eth::Log) -> Result<Mint, DecodeLogError> {
376    let contract::Note::SIGNATURE_HASH = log.topics()[0] else {
377        return Err(DecodeLogError::UnexpectedTopic(log.topics()[0]));
378    };
379    let decoded = contract::Mint::decode_log_data(log.data(), true)
380        .map_err(|e| DecodeLogError::DecodeError(e.to_string()))?;
381    let name = String::from_utf8_lossy(&decoded.label).to_string();
382    if !valid_name(&name) {
383        return Err(DecodeLogError::InvalidName(name));
384    }
385    match resolve_parent(log, None) {
386        Some(parent_path) => Ok(Mint { name, parent_path }),
387        None => Err(DecodeLogError::UnresolvedParent(name)),
388    }
389}
390
391/// Decode a note log from the hypermap into a 'resolved' format.
392///
393/// Uses [`valid_name()`] to check if the name is valid.
394pub fn decode_note_log(log: &crate::eth::Log) -> Result<Note, DecodeLogError> {
395    let contract::Note::SIGNATURE_HASH = log.topics()[0] else {
396        return Err(DecodeLogError::UnexpectedTopic(log.topics()[0]));
397    };
398    let decoded = contract::Note::decode_log_data(log.data(), true)
399        .map_err(|e| DecodeLogError::DecodeError(e.to_string()))?;
400    let note = String::from_utf8_lossy(&decoded.label).to_string();
401    if !valid_note(&note) {
402        return Err(DecodeLogError::InvalidName(note));
403    }
404    match resolve_parent(log, None) {
405        Some(parent_path) => Ok(Note {
406            note,
407            parent_path,
408            data: decoded.data,
409        }),
410        None => Err(DecodeLogError::UnresolvedParent(note)),
411    }
412}
413
414pub fn decode_fact_log(log: &crate::eth::Log) -> Result<Fact, DecodeLogError> {
415    let contract::Fact::SIGNATURE_HASH = log.topics()[0] else {
416        return Err(DecodeLogError::UnexpectedTopic(log.topics()[0]));
417    };
418    let decoded = contract::Fact::decode_log_data(log.data(), true)
419        .map_err(|e| DecodeLogError::DecodeError(e.to_string()))?;
420    let fact = String::from_utf8_lossy(&decoded.label).to_string();
421    if !valid_fact(&fact) {
422        return Err(DecodeLogError::InvalidName(fact));
423    }
424    match resolve_parent(log, None) {
425        Some(parent_path) => Ok(Fact {
426            fact,
427            parent_path,
428            data: decoded.data,
429        }),
430        None => Err(DecodeLogError::UnresolvedParent(fact)),
431    }
432}
433
434/// Given a [`crate::eth::Log`] (which must be a log from hypermap), resolve the parent name
435/// of the new entry or note.
436pub fn resolve_parent(log: &crate::eth::Log, timeout: Option<u64>) -> Option<String> {
437    let parent_hash = log.topics()[1].to_string();
438    net::get_name(&parent_hash, log.block_number, timeout)
439}
440
441/// Given a [`crate::eth::Log`] (which must be a log from hypermap), resolve the full name
442/// of the new entry or note.
443///
444/// Uses [`valid_name()`] to check if the name is valid.
445pub fn resolve_full_name(log: &crate::eth::Log, timeout: Option<u64>) -> Option<String> {
446    let parent_hash = log.topics()[1].to_string();
447    let parent_name = net::get_name(&parent_hash, log.block_number, timeout)?;
448    let log_name = match log.topics()[0] {
449        contract::Mint::SIGNATURE_HASH => {
450            let decoded = contract::Mint::decode_log_data(log.data(), true).unwrap();
451            decoded.label
452        }
453        contract::Note::SIGNATURE_HASH => {
454            let decoded = contract::Note::decode_log_data(log.data(), true).unwrap();
455            decoded.label
456        }
457        contract::Fact::SIGNATURE_HASH => {
458            let decoded = contract::Fact::decode_log_data(log.data(), true).unwrap();
459            decoded.label
460        }
461        _ => return None,
462    };
463    let name = String::from_utf8_lossy(&log_name);
464    if !valid_entry(
465        &name,
466        log.topics()[0] == contract::Note::SIGNATURE_HASH,
467        log.topics()[0] == contract::Fact::SIGNATURE_HASH,
468    ) {
469        return None;
470    }
471    Some(format!("{name}.{parent_name}"))
472}
473
474pub fn eth_apply_filter(logs: &[EthLog], filter: &EthFilter) -> Vec<EthLog> {
475    let mut matched_logs = Vec::new();
476
477    let (filter_from_block, filter_to_block) = match filter.block_option {
478        FilterBlockOption::Range {
479            from_block,
480            to_block,
481        } => {
482            let parse_block_num = |bn: Option<BlockNumberOrTag>| -> Option<u64> {
483                match bn {
484                    Some(BlockNumberOrTag::Number(n)) => Some(n),
485                    _ => None,
486                }
487            };
488            (parse_block_num(from_block), parse_block_num(to_block))
489        }
490        _ => (None, None),
491    };
492
493    for log in logs.iter() {
494        let mut match_address = filter.address.is_empty();
495        if !match_address {
496            if filter.address.matches(&log.address()) {
497                match_address = true;
498            }
499        }
500        if !match_address {
501            continue;
502        }
503
504        if let Some(log_bn) = log.block_number {
505            if let Some(filter_from) = filter_from_block {
506                if log_bn < filter_from {
507                    continue;
508                }
509            }
510            if let Some(filter_to) = filter_to_block {
511                if log_bn > filter_to {
512                    continue;
513                }
514            }
515        } else {
516            if filter_from_block.is_some() || filter_to_block.is_some() {
517                continue;
518            }
519        }
520
521        let mut match_topics = true;
522        for (i, filter_topic_alternatives) in filter.topics.iter().enumerate() {
523            if filter_topic_alternatives.is_empty() {
524                continue;
525            }
526
527            let log_topic = log.topics().get(i);
528            let mut current_topic_matched = false;
529            for filter_topic in filter_topic_alternatives.iter() {
530                if log_topic == Some(filter_topic) {
531                    current_topic_matched = true;
532                    break;
533                }
534            }
535            if !current_topic_matched {
536                match_topics = false;
537                break;
538            }
539        }
540
541        if match_topics {
542            matched_logs.push(log.clone());
543        }
544    }
545    matched_logs
546}
547
548/// Helper struct for reading from the hypermap.
549#[derive(Clone, Debug, Deserialize, Serialize)]
550pub struct Hypermap {
551    pub provider: Provider,
552    address: Address,
553}
554
555impl Hypermap {
556    /// Creates a new Hypermap instance with a specified address.
557    ///
558    /// # Arguments
559    /// * `provider` - A reference to the Provider.
560    /// * `address` - The address of the Hypermap contract.
561    pub fn new(provider: Provider, address: Address) -> Self {
562        Self { provider, address }
563    }
564
565    /// Creates a new Hypermap instance with the default address and chain ID.
566    pub fn default(timeout: u64) -> Self {
567        let provider = Provider::new(HYPERMAP_CHAIN_ID, timeout);
568        Self::new(provider, Address::from_str(HYPERMAP_ADDRESS).unwrap())
569    }
570
571    /// Returns the in-use Hypermap contract address.
572    pub fn address(&self) -> &Address {
573        &self.address
574    }
575
576    /// Gets an entry from the Hypermap by its string-formatted name.
577    ///
578    /// # Parameters
579    /// - `path`: The name-path to get from the Hypermap.
580    /// # Returns
581    /// A `Result<(Address, Address, Option<Bytes>), EthError>` representing the TBA, owner,
582    /// and value if the entry exists and is a note.
583    pub fn get(&self, path: &str) -> Result<(Address, Address, Option<Bytes>), EthError> {
584        let get_call = getCall {
585            namehash: FixedBytes::<32>::from_str(&namehash(path))
586                .map_err(|_| EthError::InvalidParams)?,
587        }
588        .abi_encode();
589
590        let tx_req = TransactionRequest::default()
591            .input(TransactionInput::new(get_call.into()))
592            .to(self.address);
593
594        let res_bytes = self.provider.call(tx_req, None)?;
595
596        let res = getCall::abi_decode_returns(&res_bytes, false)
597            .map_err(|_| EthError::RpcMalformedResponse)?;
598
599        let note_data = if res.data == Bytes::default() {
600            None
601        } else {
602            Some(res.data)
603        };
604
605        Ok((res.tba, res.owner, note_data))
606    }
607
608    /// Gets an entry from the Hypermap by its hash.
609    ///
610    /// # Parameters
611    /// - `entryhash`: The entry to get from the Hypermap.
612    /// # Returns
613    /// A `Result<(Address, Address, Option<Bytes>), EthError>` representing the TBA, owner,
614    /// and value if the entry exists and is a note.
615    pub fn get_hash(&self, entryhash: &str) -> Result<(Address, Address, Option<Bytes>), EthError> {
616        let get_call = getCall {
617            namehash: FixedBytes::<32>::from_str(entryhash).map_err(|_| EthError::InvalidParams)?,
618        }
619        .abi_encode();
620
621        let tx_req = TransactionRequest::default()
622            .input(TransactionInput::new(get_call.into()))
623            .to(self.address);
624
625        let res_bytes = self.provider.call(tx_req, None)?;
626
627        let res = getCall::abi_decode_returns(&res_bytes, false)
628            .map_err(|_| EthError::RpcMalformedResponse)?;
629
630        let note_data = if res.data == Bytes::default() {
631            None
632        } else {
633            Some(res.data)
634        };
635
636        Ok((res.tba, res.owner, note_data))
637    }
638
639    /// Gets a namehash from an existing TBA address.
640    ///
641    /// # Parameters
642    /// - `tba`: The TBA to get the namehash of.
643    /// # Returns
644    /// A `Result<String, EthError>` representing the namehash of the TBA.
645    pub fn get_namehash_from_tba(&self, tba: Address) -> Result<String, EthError> {
646        let token_call = tokenCall {}.abi_encode();
647
648        let tx_req = TransactionRequest::default()
649            .input(TransactionInput::new(token_call.into()))
650            .to(tba);
651
652        let res_bytes = self.provider.call(tx_req, None)?;
653
654        let res = tokenCall::abi_decode_returns(&res_bytes, false)
655            .map_err(|_| EthError::RpcMalformedResponse)?;
656
657        let namehash: FixedBytes<32> = res.tokenId.into();
658        Ok(format!("0x{}", hex::encode(namehash)))
659    }
660
661    /// Create a filter for all mint events.
662    pub fn mint_filter(&self) -> crate::eth::Filter {
663        crate::eth::Filter::new()
664            .address(self.address)
665            .event(contract::Mint::SIGNATURE)
666    }
667
668    /// Create a filter for all note events.
669    pub fn note_filter(&self) -> crate::eth::Filter {
670        crate::eth::Filter::new()
671            .address(self.address)
672            .event(contract::Note::SIGNATURE)
673    }
674
675    /// Create a filter for all fact events.
676    pub fn fact_filter(&self) -> crate::eth::Filter {
677        crate::eth::Filter::new()
678            .address(self.address)
679            .event(contract::Fact::SIGNATURE)
680    }
681
682    /// Create a filter for a given set of specific notes. This function will
683    /// hash the note labels and use them as the topic3 filter.
684    ///
685    /// Example:
686    /// ```rust
687    /// let filter = hypermap.notes_filter(&["~note1", "~note2"]);
688    /// ```
689    pub fn notes_filter(&self, notes: &[&str]) -> crate::eth::Filter {
690        self.note_filter().topic3(
691            notes
692                .into_iter()
693                .map(|note| keccak256(note))
694                .collect::<Vec<_>>(),
695        )
696    }
697
698    /// Create a filter for a given set of specific facts. This function will
699    /// hash the fact labels and use them as the topic3 filter.
700    ///
701    /// Example:
702    /// ```rust
703    /// let filter = hypermap.facts_filter(&["!fact1", "!fact2"]);
704    /// ```
705    pub fn facts_filter(&self, facts: &[&str]) -> crate::eth::Filter {
706        self.fact_filter().topic3(
707            facts
708                .into_iter()
709                .map(|fact| keccak256(fact))
710                .collect::<Vec<_>>(),
711        )
712    }
713
714    fn get_bootstrap_log_cache_inner(
715        &self,
716        cacher_request: &CacherRequest,
717        cacher_process_address: &HyperAddress,
718        attempt: u64,
719        request_from_block_val: u64,
720        retry_delay_s: u64,
721        retry_count: Option<u64>,
722        chain: &Option<String>,
723    ) -> anyhow::Result<Option<(u64, Vec<LogCache>)>> {
724        let retry_count_str = retry_count
725            .map(|r| r.to_string())
726            .unwrap_or_else(|| "inf".to_string());
727        print_to_terminal(
728            2,
729            &format!("Attempt {attempt}/{retry_count_str} to query local hypermap-cacher"),
730        );
731
732        let response_msg = match Request::to(cacher_process_address.clone())
733            .body(serde_json::to_vec(cacher_request)?)
734            .send_and_await_response(CACHER_REQUEST_TIMEOUT_S)
735        {
736            Ok(Ok(msg)) => msg,
737            Ok(Err(e)) => {
738                print_to_terminal(
739                    1,
740                    &format!(
741                        "Error response from local cacher (attempt {}): {:?}",
742                        attempt, e
743                    ),
744                );
745                if retry_count.is_none() || attempt < retry_count.unwrap() {
746                    std::thread::sleep(std::time::Duration::from_secs(retry_delay_s));
747                    return Ok(None);
748                } else {
749                    return Err(anyhow::anyhow!(
750                        "Error response from local cacher after {retry_count_str} attempts: {e:?}"
751                    ));
752                }
753            }
754            Err(e) => {
755                print_to_terminal(
756                    1,
757                    &format!(
758                        "Failed to send request to local cacher (attempt {}): {:?}",
759                        attempt, e
760                    ),
761                );
762                if retry_count.is_none() || attempt < retry_count.unwrap() {
763                    std::thread::sleep(std::time::Duration::from_secs(retry_delay_s));
764                    return Ok(None);
765                } else {
766                    return Err(anyhow::anyhow!(
767                        "Failed to send request to local cacher after {retry_count_str} attempts: {e:?}"
768                    ));
769                }
770            }
771        };
772
773        match serde_json::from_slice::<CacherResponse>(response_msg.body())? {
774            CacherResponse::GetLogsByRange(res) => {
775                match res {
776                    Ok(GetLogsByRangeOkResponse::Latest(block)) => {
777                        return Ok(Some((block, vec![])));
778                    }
779                    Ok(GetLogsByRangeOkResponse::Logs((block, json_string_of_vec_log_cache))) => {
780                        if json_string_of_vec_log_cache.is_empty()
781                            || json_string_of_vec_log_cache == "[]"
782                        {
783                            print_to_terminal(
784                                    2,
785                                    &format!(
786                                        "Local cacher returned no log caches for the range from block {}.",
787                                        request_from_block_val,
788                                    ),
789                                );
790                            return Ok(Some((block, vec![])));
791                        }
792                        match serde_json::from_str::<Vec<LogCache>>(&json_string_of_vec_log_cache) {
793                            Ok(retrieved_caches) => {
794                                let target_chain_id = chain
795                                    .clone()
796                                    .unwrap_or_else(|| self.provider.get_chain_id().to_string());
797                                let mut filtered_caches = vec![];
798
799                                for log_cache in retrieved_caches {
800                                    if log_cache.metadata.chain_id == target_chain_id {
801                                        // Further filter: ensure the cache's own from_block isn't completely after what we need,
802                                        // and to_block isn't completely before.
803                                        let cache_from = log_cache
804                                            .metadata
805                                            .from_block
806                                            .parse::<u64>()
807                                            .unwrap_or(u64::MAX);
808                                        let cache_to =
809                                            log_cache.metadata.to_block.parse::<u64>().unwrap_or(0);
810
811                                        if cache_to >= request_from_block_val {
812                                            // Cache has some data at or after our request_from_block
813                                            filtered_caches.push(log_cache);
814                                        } else {
815                                            print_to_terminal(3, &format!("Cache from local cacher ({} to {}) does not meet request_from_block {}",
816                                                    cache_from, cache_to, request_from_block_val));
817                                        }
818                                    } else {
819                                        print_to_terminal(1,&format!("LogCache from local cacher has mismatched chain_id (expected {}, got {}). Skipping.",
820                                                target_chain_id, log_cache.metadata.chain_id));
821                                    }
822                                }
823
824                                print_to_terminal(
825                                    2,
826                                    &format!(
827                                        "Retrieved {} log caches from local hypermap-cacher.",
828                                        filtered_caches.len(),
829                                    ),
830                                );
831                                return Ok(Some((block, filtered_caches)));
832                            }
833                            Err(e) => {
834                                return Err(anyhow::anyhow!(
835                                        "Failed to deserialize Vec<LogCache> from local cacher: {:?}. JSON: {:.100}",
836                                        e, json_string_of_vec_log_cache
837                                    ));
838                            }
839                        }
840                    }
841                    Err(e_str) => {
842                        return Err(anyhow::anyhow!(
843                            "Local cacher reported error for GetLogsByRange: {}",
844                            e_str,
845                        ));
846                    }
847                }
848            }
849            CacherResponse::IsStarting => {
850                print_to_terminal(
851                        2,
852                        &format!(
853                            "Local hypermap-cacher is still starting (attempt {}/{}). Retrying in {}s...",
854                            attempt, retry_count_str, retry_delay_s
855                        ),
856                    );
857                if retry_count.is_none() || attempt < retry_count.unwrap() {
858                    std::thread::sleep(std::time::Duration::from_secs(retry_delay_s));
859                    return Ok(None);
860                } else {
861                    return Err(anyhow::anyhow!(
862                        "Local hypermap-cacher is still starting after {retry_count_str} attempts"
863                    ));
864                }
865            }
866            CacherResponse::Rejected => {
867                return Err(anyhow::anyhow!(
868                    "Local hypermap-cacher rejected our request"
869                ));
870            }
871            _ => {
872                return Err(anyhow::anyhow!(
873                    "Unexpected response type from local hypermap-cacher"
874                ));
875            }
876        }
877    }
878
879    pub fn get_bootstrap_log_cache(
880        &self,
881        from_block: Option<u64>,
882        retry_params: Option<(u64, Option<u64>)>,
883        chain: Option<String>,
884    ) -> anyhow::Result<(u64, Vec<LogCache>)> {
885        print_to_terminal(2,
886            &format!("get_bootstrap_log_cache (using local hypermap-cacher): from_block={:?}, retry_params={:?}, chain={:?}",
887            from_block, retry_params, chain)
888        );
889
890        let (retry_delay_s, retry_count) = retry_params.ok_or_else(|| {
891            anyhow::anyhow!("IsStarted check requires retry parameters (delay_s, max_tries)")
892        })?;
893
894        let cacher_process_address =
895            HyperAddress::new("our", ("hypermap-cacher", "hypermap-cacher", "sys"));
896
897        print_to_terminal(
898            2,
899            &format!(
900                "Querying local cacher with GetLogsByRange: {}",
901                cacher_process_address.to_string(),
902            ),
903        );
904
905        let request_from_block_val = from_block.unwrap_or(0);
906
907        let get_logs_by_range_payload = GetLogsByRangeRequest {
908            from_block: request_from_block_val,
909            to_block: None, // Request all logs from from_block onwards. Cacher will return what it has.
910        };
911        let cacher_request = CacherRequest::GetLogsByRange(get_logs_by_range_payload);
912
913        if let Some(retry_count) = retry_count {
914            for attempt in 1..=retry_count {
915                if let Some(return_vals) = self.get_bootstrap_log_cache_inner(
916                    &cacher_request,
917                    &cacher_process_address,
918                    attempt,
919                    request_from_block_val,
920                    retry_delay_s,
921                    Some(retry_count),
922                    &chain,
923                )? {
924                    return Ok(return_vals);
925                }
926            }
927        } else {
928            let mut attempt = 1;
929            loop {
930                if let Some(return_vals) = self.get_bootstrap_log_cache_inner(
931                    &cacher_request,
932                    &cacher_process_address,
933                    attempt,
934                    request_from_block_val,
935                    retry_delay_s,
936                    None,
937                    &chain,
938                )? {
939                    return Ok(return_vals);
940                }
941                attempt += 1;
942            }
943        }
944
945        Err(anyhow::anyhow!(
946            "Failed to get response from local hypermap-cacher after {retry_count:?} attempts"
947        ))
948    }
949
950    pub fn validate_log_cache(&self, log_cache: &LogCache) -> anyhow::Result<bool> {
951        let from_block = log_cache.metadata.from_block.parse::<u64>().map_err(|_| {
952            anyhow::anyhow!(
953                "Invalid from_block in metadata: {}",
954                log_cache.metadata.from_block
955            )
956        })?;
957        let to_block = log_cache.metadata.to_block.parse::<u64>().map_err(|_| {
958            anyhow::anyhow!(
959                "Invalid to_block in metadata: {}",
960                log_cache.metadata.to_block
961            )
962        })?;
963
964        let mut bytes_to_verify = serde_json::to_vec(&log_cache.logs)
965            .map_err(|e| anyhow::anyhow!("Failed to serialize logs for validation: {:?}", e))?;
966        bytes_to_verify.extend_from_slice(&from_block.to_be_bytes());
967        bytes_to_verify.extend_from_slice(&to_block.to_be_bytes());
968        let hashed_data = keccak256(&bytes_to_verify);
969
970        let signature_hex = log_cache.metadata.signature.trim_start_matches("0x");
971        let signature_bytes = hex::decode(signature_hex)
972            .map_err(|e| anyhow::anyhow!("Failed to decode hex signature: {:?}", e))?;
973
974        Ok(sign::net_key_verify(
975            hashed_data.to_vec(),
976            &log_cache.metadata.created_by.parse::<HyperAddress>()?,
977            signature_bytes,
978        )?)
979    }
980
981    pub fn get_bootstrap(
982        &self,
983        from_block: Option<u64>,
984        retry_params: Option<(u64, Option<u64>)>,
985        chain: Option<String>,
986    ) -> anyhow::Result<(u64, Vec<EthLog>)> {
987        print_to_terminal(
988            2,
989            &format!(
990                "get_bootstrap: from_block={:?}, retry_params={:?}, chain={:?}",
991                from_block, retry_params, chain,
992            ),
993        );
994        let (block, log_caches) = self.get_bootstrap_log_cache(from_block, retry_params, chain)?;
995
996        let mut all_valid_logs: Vec<EthLog> = Vec::new();
997        let request_from_block_val = from_block.unwrap_or(0);
998
999        for log_cache in log_caches {
1000            match self.validate_log_cache(&log_cache) {
1001                Ok(true) => {
1002                    for log in log_cache.logs {
1003                        if let Some(log_block_number) = log.block_number {
1004                            if log_block_number >= request_from_block_val {
1005                                all_valid_logs.push(log);
1006                            }
1007                        } else {
1008                            if from_block.is_none() {
1009                                all_valid_logs.push(log);
1010                            }
1011                        }
1012                    }
1013                }
1014                Ok(false) => {
1015                    print_to_terminal(
1016                        1,
1017                        &format!("LogCache validation failed for cache created by {}. Discarding {} logs.",
1018                        log_cache.metadata.created_by,
1019                        log_cache.logs.len())
1020                    );
1021                }
1022                Err(e) => {
1023                    print_to_terminal(
1024                        1,
1025                        &format!(
1026                            "Error validating LogCache from {}: {:?}. Discarding.",
1027                            log_cache.metadata.created_by, e,
1028                        ),
1029                    );
1030                }
1031            }
1032        }
1033
1034        all_valid_logs.sort_by(|a, b| {
1035            let block_cmp = a.block_number.cmp(&b.block_number);
1036            if block_cmp == std::cmp::Ordering::Equal {
1037                std::cmp::Ordering::Equal
1038            } else {
1039                block_cmp
1040            }
1041        });
1042
1043        let mut unique_logs = Vec::new();
1044        for log in all_valid_logs {
1045            if !unique_logs.contains(&log) {
1046                unique_logs.push(log);
1047            }
1048        }
1049
1050        print_to_terminal(
1051            2,
1052            &format!(
1053                "get_bootstrap: Consolidated {} unique logs.",
1054                unique_logs.len(),
1055            ),
1056        );
1057        Ok((block, unique_logs))
1058    }
1059
1060    pub fn bootstrap(
1061        &self,
1062        from_block: Option<u64>,
1063        filters: Vec<EthFilter>,
1064        retry_params: Option<(u64, Option<u64>)>,
1065        chain: Option<String>,
1066    ) -> anyhow::Result<(u64, Vec<Vec<EthLog>>)> {
1067        print_to_terminal(
1068            2,
1069            &format!(
1070                "bootstrap: from_block={:?}, num_filters={}, retry_params={:?}, chain={:?}",
1071                from_block,
1072                filters.len(),
1073                retry_params,
1074                chain,
1075            ),
1076        );
1077
1078        let (block, consolidated_logs) = self.get_bootstrap(from_block, retry_params, chain)?;
1079
1080        if consolidated_logs.is_empty() {
1081            print_to_terminal(2,"bootstrap: No logs retrieved after consolidation. Returning empty results for filters.");
1082            return Ok((block, filters.iter().map(|_| Vec::new()).collect()));
1083        }
1084
1085        let mut results_per_filter: Vec<Vec<EthLog>> = Vec::new();
1086        for filter in filters {
1087            let filtered_logs = eth_apply_filter(&consolidated_logs, &filter);
1088            results_per_filter.push(filtered_logs);
1089        }
1090
1091        print_to_terminal(
1092            2,
1093            &format!(
1094                "bootstrap: Applied {} filters to bootstrapped logs.",
1095                results_per_filter.len(),
1096            ),
1097        );
1098        Ok((block, results_per_filter))
1099    }
1100}
1101
1102impl Serialize for ManifestItem {
1103    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1104    where
1105        S: Serializer,
1106    {
1107        let mut state = serializer.serialize_struct("ManifestItem", 4)?;
1108        state.serialize_field("metadata", &self.metadata)?;
1109        state.serialize_field("is_empty", &self.is_empty)?;
1110        state.serialize_field("file_hash", &self.file_hash)?;
1111        state.serialize_field("file_name", &self.file_name)?;
1112        state.end()
1113    }
1114}
1115
1116impl<'de> Deserialize<'de> for ManifestItem {
1117    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1118    where
1119        D: Deserializer<'de>,
1120    {
1121        #[derive(Deserialize)]
1122        #[serde(field_identifier, rename_all = "snake_case")]
1123        enum Field {
1124            Metadata,
1125            IsEmpty,
1126            FileHash,
1127            FileName,
1128        }
1129
1130        struct ManifestItemVisitor;
1131
1132        impl<'de> Visitor<'de> for ManifestItemVisitor {
1133            type Value = ManifestItem;
1134
1135            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1136                formatter.write_str("struct ManifestItem")
1137            }
1138
1139            fn visit_map<V>(self, mut map: V) -> Result<ManifestItem, V::Error>
1140            where
1141                V: MapAccess<'de>,
1142            {
1143                let mut metadata = None;
1144                let mut is_empty = None;
1145                let mut file_hash = None;
1146                let mut file_name = None;
1147
1148                while let Some(key) = map.next_key()? {
1149                    match key {
1150                        Field::Metadata => {
1151                            if metadata.is_some() {
1152                                return Err(de::Error::duplicate_field("metadata"));
1153                            }
1154                            metadata = Some(map.next_value()?);
1155                        }
1156                        Field::IsEmpty => {
1157                            if is_empty.is_some() {
1158                                return Err(de::Error::duplicate_field("is_empty"));
1159                            }
1160                            is_empty = Some(map.next_value()?);
1161                        }
1162                        Field::FileHash => {
1163                            if file_hash.is_some() {
1164                                return Err(de::Error::duplicate_field("file_hash"));
1165                            }
1166                            file_hash = Some(map.next_value()?);
1167                        }
1168                        Field::FileName => {
1169                            if file_name.is_some() {
1170                                return Err(de::Error::duplicate_field("file_name"));
1171                            }
1172                            file_name = Some(map.next_value()?);
1173                        }
1174                    }
1175                }
1176
1177                let metadata = metadata.ok_or_else(|| de::Error::missing_field("metadata"))?;
1178                let is_empty = is_empty.ok_or_else(|| de::Error::missing_field("is_empty"))?;
1179                let file_hash = file_hash.ok_or_else(|| de::Error::missing_field("file_hash"))?;
1180                let file_name = file_name.ok_or_else(|| de::Error::missing_field("file_name"))?;
1181
1182                Ok(ManifestItem {
1183                    metadata,
1184                    is_empty,
1185                    file_hash,
1186                    file_name,
1187                })
1188            }
1189        }
1190
1191        deserializer.deserialize_struct(
1192            "ManifestItem",
1193            &["metadata", "is_empty", "file_hash", "file_name"],
1194            ManifestItemVisitor,
1195        )
1196    }
1197}
1198
1199impl Serialize for Manifest {
1200    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1201    where
1202        S: Serializer,
1203    {
1204        let mut state = serializer.serialize_struct("Manifest", 4)?;
1205        state.serialize_field("items", &self.items)?;
1206        state.serialize_field("manifest_filename", &self.manifest_filename)?;
1207        state.serialize_field("chain_id", &self.chain_id)?;
1208        state.serialize_field("protocol_version", &self.protocol_version)?;
1209        state.end()
1210    }
1211}
1212
1213impl<'de> Deserialize<'de> for Manifest {
1214    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1215    where
1216        D: Deserializer<'de>,
1217    {
1218        #[derive(Deserialize)]
1219        #[serde(field_identifier, rename_all = "snake_case")]
1220        enum Field {
1221            Items,
1222            ManifestFilename,
1223            ChainId,
1224            ProtocolVersion,
1225        }
1226
1227        struct ManifestVisitor;
1228
1229        impl<'de> Visitor<'de> for ManifestVisitor {
1230            type Value = Manifest;
1231
1232            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1233                formatter.write_str("struct Manifest")
1234            }
1235
1236            fn visit_map<V>(self, mut map: V) -> Result<Manifest, V::Error>
1237            where
1238                V: MapAccess<'de>,
1239            {
1240                let mut items = None;
1241                let mut manifest_filename = None;
1242                let mut chain_id = None;
1243                let mut protocol_version = None;
1244
1245                while let Some(key) = map.next_key()? {
1246                    match key {
1247                        Field::Items => {
1248                            if items.is_some() {
1249                                return Err(de::Error::duplicate_field("items"));
1250                            }
1251                            items = Some(map.next_value()?);
1252                        }
1253                        Field::ManifestFilename => {
1254                            if manifest_filename.is_some() {
1255                                return Err(de::Error::duplicate_field("manifest_filename"));
1256                            }
1257                            manifest_filename = Some(map.next_value()?);
1258                        }
1259                        Field::ChainId => {
1260                            if chain_id.is_some() {
1261                                return Err(de::Error::duplicate_field("chain_id"));
1262                            }
1263                            chain_id = Some(map.next_value()?);
1264                        }
1265                        Field::ProtocolVersion => {
1266                            if protocol_version.is_some() {
1267                                return Err(de::Error::duplicate_field("protocol_version"));
1268                            }
1269                            protocol_version = Some(map.next_value()?);
1270                        }
1271                    }
1272                }
1273
1274                let items = items.ok_or_else(|| de::Error::missing_field("items"))?;
1275                let manifest_filename = manifest_filename
1276                    .ok_or_else(|| de::Error::missing_field("manifest_filename"))?;
1277                let chain_id = chain_id.ok_or_else(|| de::Error::missing_field("chain_id"))?;
1278                let protocol_version =
1279                    protocol_version.ok_or_else(|| de::Error::missing_field("protocol_version"))?;
1280
1281                Ok(Manifest {
1282                    items,
1283                    manifest_filename,
1284                    chain_id,
1285                    protocol_version,
1286                })
1287            }
1288        }
1289
1290        deserializer.deserialize_struct(
1291            "Manifest",
1292            &["items", "manifest_filename", "chain_id", "protocol_version"],
1293            ManifestVisitor,
1294        )
1295    }
1296}
1297
1298impl Serialize for GetLogsByRangeRequest {
1299    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1300    where
1301        S: Serializer,
1302    {
1303        let mut state = serializer.serialize_struct("GetLogsByRangeRequest", 2)?;
1304        state.serialize_field("from_block", &self.from_block)?;
1305        state.serialize_field("to_block", &self.to_block)?;
1306        state.end()
1307    }
1308}
1309
1310impl<'de> Deserialize<'de> for GetLogsByRangeRequest {
1311    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1312    where
1313        D: Deserializer<'de>,
1314    {
1315        #[derive(Deserialize)]
1316        #[serde(field_identifier, rename_all = "snake_case")]
1317        enum Field {
1318            FromBlock,
1319            ToBlock,
1320        }
1321
1322        struct GetLogsByRangeRequestVisitor;
1323
1324        impl<'de> Visitor<'de> for GetLogsByRangeRequestVisitor {
1325            type Value = GetLogsByRangeRequest;
1326
1327            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1328                formatter.write_str("struct GetLogsByRangeRequest")
1329            }
1330
1331            fn visit_map<V>(self, mut map: V) -> Result<GetLogsByRangeRequest, V::Error>
1332            where
1333                V: MapAccess<'de>,
1334            {
1335                let mut from_block = None;
1336                let mut to_block = None;
1337
1338                while let Some(key) = map.next_key()? {
1339                    match key {
1340                        Field::FromBlock => {
1341                            if from_block.is_some() {
1342                                return Err(de::Error::duplicate_field("from_block"));
1343                            }
1344                            from_block = Some(map.next_value()?);
1345                        }
1346                        Field::ToBlock => {
1347                            if to_block.is_some() {
1348                                return Err(de::Error::duplicate_field("to_block"));
1349                            }
1350                            to_block = Some(map.next_value()?);
1351                        }
1352                    }
1353                }
1354
1355                let from_block =
1356                    from_block.ok_or_else(|| de::Error::missing_field("from_block"))?;
1357
1358                Ok(GetLogsByRangeRequest {
1359                    from_block,
1360                    to_block,
1361                })
1362            }
1363        }
1364
1365        deserializer.deserialize_struct(
1366            "GetLogsByRangeRequest",
1367            &["from_block", "to_block"],
1368            GetLogsByRangeRequestVisitor,
1369        )
1370    }
1371}
1372
1373impl Serialize for CacherStatus {
1374    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1375    where
1376        S: Serializer,
1377    {
1378        let mut state = serializer.serialize_struct("CacherStatus", 8)?;
1379        state.serialize_field("last_cached_block", &self.last_cached_block)?;
1380        state.serialize_field("chain_id", &self.chain_id)?;
1381        state.serialize_field("protocol_version", &self.protocol_version)?;
1382        state.serialize_field(
1383            "next_cache_attempt_in_seconds",
1384            &self.next_cache_attempt_in_seconds,
1385        )?;
1386        state.serialize_field("manifest_filename", &self.manifest_filename)?;
1387        state.serialize_field("log_files_count", &self.log_files_count)?;
1388        state.serialize_field("our_address", &self.our_address)?;
1389        state.serialize_field("is_providing", &self.is_providing)?;
1390        state.end()
1391    }
1392}
1393
1394impl<'de> Deserialize<'de> for CacherStatus {
1395    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1396    where
1397        D: Deserializer<'de>,
1398    {
1399        #[derive(Deserialize)]
1400        #[serde(field_identifier, rename_all = "snake_case")]
1401        enum Field {
1402            LastCachedBlock,
1403            ChainId,
1404            ProtocolVersion,
1405            NextCacheAttemptInSeconds,
1406            ManifestFilename,
1407            LogFilesCount,
1408            OurAddress,
1409            IsProviding,
1410        }
1411
1412        struct CacherStatusVisitor;
1413
1414        impl<'de> Visitor<'de> for CacherStatusVisitor {
1415            type Value = CacherStatus;
1416
1417            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1418                formatter.write_str("struct CacherStatus")
1419            }
1420
1421            fn visit_map<V>(self, mut map: V) -> Result<CacherStatus, V::Error>
1422            where
1423                V: MapAccess<'de>,
1424            {
1425                let mut last_cached_block = None;
1426                let mut chain_id = None;
1427                let mut protocol_version = None;
1428                let mut next_cache_attempt_in_seconds = None;
1429                let mut manifest_filename = None;
1430                let mut log_files_count = None;
1431                let mut our_address = None;
1432                let mut is_providing = None;
1433
1434                while let Some(key) = map.next_key()? {
1435                    match key {
1436                        Field::LastCachedBlock => {
1437                            if last_cached_block.is_some() {
1438                                return Err(de::Error::duplicate_field("last_cached_block"));
1439                            }
1440                            last_cached_block = Some(map.next_value()?);
1441                        }
1442                        Field::ChainId => {
1443                            if chain_id.is_some() {
1444                                return Err(de::Error::duplicate_field("chain_id"));
1445                            }
1446                            chain_id = Some(map.next_value()?);
1447                        }
1448                        Field::ProtocolVersion => {
1449                            if protocol_version.is_some() {
1450                                return Err(de::Error::duplicate_field("protocol_version"));
1451                            }
1452                            protocol_version = Some(map.next_value()?);
1453                        }
1454                        Field::NextCacheAttemptInSeconds => {
1455                            if next_cache_attempt_in_seconds.is_some() {
1456                                return Err(de::Error::duplicate_field(
1457                                    "next_cache_attempt_in_seconds",
1458                                ));
1459                            }
1460                            next_cache_attempt_in_seconds = Some(map.next_value()?);
1461                        }
1462                        Field::ManifestFilename => {
1463                            if manifest_filename.is_some() {
1464                                return Err(de::Error::duplicate_field("manifest_filename"));
1465                            }
1466                            manifest_filename = Some(map.next_value()?);
1467                        }
1468                        Field::LogFilesCount => {
1469                            if log_files_count.is_some() {
1470                                return Err(de::Error::duplicate_field("log_files_count"));
1471                            }
1472                            log_files_count = Some(map.next_value()?);
1473                        }
1474                        Field::OurAddress => {
1475                            if our_address.is_some() {
1476                                return Err(de::Error::duplicate_field("our_address"));
1477                            }
1478                            our_address = Some(map.next_value()?);
1479                        }
1480                        Field::IsProviding => {
1481                            if is_providing.is_some() {
1482                                return Err(de::Error::duplicate_field("is_providing"));
1483                            }
1484                            is_providing = Some(map.next_value()?);
1485                        }
1486                    }
1487                }
1488
1489                let last_cached_block = last_cached_block
1490                    .ok_or_else(|| de::Error::missing_field("last_cached_block"))?;
1491                let chain_id = chain_id.ok_or_else(|| de::Error::missing_field("chain_id"))?;
1492                let protocol_version =
1493                    protocol_version.ok_or_else(|| de::Error::missing_field("protocol_version"))?;
1494                let manifest_filename = manifest_filename
1495                    .ok_or_else(|| de::Error::missing_field("manifest_filename"))?;
1496                let log_files_count =
1497                    log_files_count.ok_or_else(|| de::Error::missing_field("log_files_count"))?;
1498                let our_address =
1499                    our_address.ok_or_else(|| de::Error::missing_field("our_address"))?;
1500                let is_providing =
1501                    is_providing.ok_or_else(|| de::Error::missing_field("is_providing"))?;
1502
1503                Ok(CacherStatus {
1504                    last_cached_block,
1505                    chain_id,
1506                    protocol_version,
1507                    next_cache_attempt_in_seconds,
1508                    manifest_filename,
1509                    log_files_count,
1510                    our_address,
1511                    is_providing,
1512                })
1513            }
1514        }
1515
1516        deserializer.deserialize_struct(
1517            "CacherStatus",
1518            &[
1519                "last_cached_block",
1520                "chain_id",
1521                "protocol_version",
1522                "next_cache_attempt_in_seconds",
1523                "manifest_filename",
1524                "log_files_count",
1525                "our_address",
1526                "is_providing",
1527            ],
1528            CacherStatusVisitor,
1529        )
1530    }
1531}
1532
1533impl Serialize for CacherRequest {
1534    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1535    where
1536        S: Serializer,
1537    {
1538        match self {
1539            CacherRequest::GetManifest => serializer.serialize_str("GetManifest"),
1540            CacherRequest::GetLogCacheContent(path) => {
1541                let mut map = serializer.serialize_map(Some(1))?;
1542                map.serialize_entry("GetLogCacheContent", path)?;
1543                map.end()
1544            }
1545            CacherRequest::GetStatus => serializer.serialize_str("GetStatus"),
1546            CacherRequest::GetLogsByRange(request) => {
1547                let mut map = serializer.serialize_map(Some(1))?;
1548                map.serialize_entry("GetLogsByRange", request)?;
1549                map.end()
1550            }
1551            CacherRequest::StartProviding => serializer.serialize_str("StartProviding"),
1552            CacherRequest::StopProviding => serializer.serialize_str("StopProviding"),
1553            CacherRequest::SetNodes(nodes) => {
1554                let mut map = serializer.serialize_map(Some(1))?;
1555                map.serialize_entry("SetNodes", nodes)?;
1556                map.end()
1557            }
1558            CacherRequest::Reset(nodes) => {
1559                let mut map = serializer.serialize_map(Some(1))?;
1560                map.serialize_entry("Reset", nodes)?;
1561                map.end()
1562            }
1563        }
1564    }
1565}
1566
1567impl<'de> Deserialize<'de> for CacherRequest {
1568    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1569    where
1570        D: Deserializer<'de>,
1571    {
1572        struct CacherRequestVisitor;
1573
1574        impl<'de> Visitor<'de> for CacherRequestVisitor {
1575            type Value = CacherRequest;
1576
1577            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1578                formatter.write_str("a string for unit variants or a map for other variants")
1579            }
1580
1581            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1582            where
1583                E: de::Error,
1584            {
1585                match value {
1586                    "GetManifest" => Ok(CacherRequest::GetManifest),
1587                    "GetStatus" => Ok(CacherRequest::GetStatus),
1588                    "StartProviding" => Ok(CacherRequest::StartProviding),
1589                    "StopProviding" => Ok(CacherRequest::StopProviding),
1590                    _ => Err(de::Error::unknown_variant(
1591                        value,
1592                        &[
1593                            "GetManifest",
1594                            "GetLogCacheContent",
1595                            "GetStatus",
1596                            "GetLogsByRange",
1597                            "StartProviding",
1598                            "StopProviding",
1599                            "SetNodes",
1600                            "Reset",
1601                        ],
1602                    )),
1603                }
1604            }
1605
1606            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1607            where
1608                A: MapAccess<'de>,
1609            {
1610                let (variant, value) = map
1611                    .next_entry::<String, serde_json::Value>()?
1612                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
1613
1614                // Ensure there are no extra entries
1615                if map.next_entry::<String, serde_json::Value>()?.is_some() {
1616                    return Err(de::Error::custom("unexpected extra entries in map"));
1617                }
1618
1619                match variant.as_str() {
1620                    "GetLogCacheContent" => {
1621                        let path = serde_json::from_value(value).map_err(de::Error::custom)?;
1622                        Ok(CacherRequest::GetLogCacheContent(path))
1623                    }
1624                    "GetLogsByRange" => {
1625                        let request = serde_json::from_value(value).map_err(de::Error::custom)?;
1626                        Ok(CacherRequest::GetLogsByRange(request))
1627                    }
1628                    "SetNodes" => {
1629                        let nodes = serde_json::from_value(value).map_err(de::Error::custom)?;
1630                        Ok(CacherRequest::SetNodes(nodes))
1631                    }
1632                    "Reset" => {
1633                        let nodes = serde_json::from_value(value).map_err(de::Error::custom)?;
1634                        Ok(CacherRequest::Reset(nodes))
1635                    }
1636                    _ => Err(de::Error::unknown_variant(
1637                        &variant,
1638                        &[
1639                            "GetManifest",
1640                            "GetLogCacheContent",
1641                            "GetStatus",
1642                            "GetLogsByRange",
1643                            "StartProviding",
1644                            "StopProviding",
1645                            "SetNodes",
1646                            "Reset",
1647                        ],
1648                    )),
1649                }
1650            }
1651        }
1652
1653        deserializer.deserialize_any(CacherRequestVisitor)
1654    }
1655}
1656
1657impl Serialize for CacherResponse {
1658    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1659    where
1660        S: Serializer,
1661    {
1662        match self {
1663            CacherResponse::GetManifest(manifest) => {
1664                let mut map = serializer.serialize_map(Some(1))?;
1665                map.serialize_entry("GetManifest", manifest)?;
1666                map.end()
1667            }
1668            CacherResponse::GetLogCacheContent(result) => {
1669                let mut map = serializer.serialize_map(Some(1))?;
1670                map.serialize_entry("GetLogCacheContent", result)?;
1671                map.end()
1672            }
1673            CacherResponse::GetStatus(status) => {
1674                let mut map = serializer.serialize_map(Some(1))?;
1675                map.serialize_entry("GetStatus", status)?;
1676                map.end()
1677            }
1678            CacherResponse::GetLogsByRange(result) => {
1679                let mut map = serializer.serialize_map(Some(1))?;
1680                map.serialize_entry("GetLogsByRange", result)?;
1681                map.end()
1682            }
1683            CacherResponse::StartProviding(result) => {
1684                let mut map = serializer.serialize_map(Some(1))?;
1685                map.serialize_entry("StartProviding", result)?;
1686                map.end()
1687            }
1688            CacherResponse::StopProviding(result) => {
1689                let mut map = serializer.serialize_map(Some(1))?;
1690                map.serialize_entry("StopProviding", result)?;
1691                map.end()
1692            }
1693            CacherResponse::Rejected => serializer.serialize_str("Rejected"),
1694            CacherResponse::IsStarting => serializer.serialize_str("IsStarting"),
1695            CacherResponse::SetNodes(result) => {
1696                let mut map = serializer.serialize_map(Some(1))?;
1697                map.serialize_entry("SetNodes", result)?;
1698                map.end()
1699            }
1700            CacherResponse::Reset(result) => {
1701                let mut map = serializer.serialize_map(Some(1))?;
1702                map.serialize_entry("Reset", result)?;
1703                map.end()
1704            }
1705        }
1706    }
1707}
1708
1709impl<'de> Deserialize<'de> for CacherResponse {
1710    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1711    where
1712        D: Deserializer<'de>,
1713    {
1714        struct CacherResponseVisitor;
1715
1716        impl<'de> Visitor<'de> for CacherResponseVisitor {
1717            type Value = CacherResponse;
1718
1719            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1720                formatter.write_str("a string for unit variants or a map for other variants")
1721            }
1722
1723            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1724            where
1725                E: de::Error,
1726            {
1727                match value {
1728                    "Rejected" => Ok(CacherResponse::Rejected),
1729                    "IsStarting" => Ok(CacherResponse::IsStarting),
1730                    _ => Err(de::Error::unknown_variant(
1731                        value,
1732                        &[
1733                            "GetManifest",
1734                            "GetLogCacheContent",
1735                            "GetStatus",
1736                            "GetLogsByRange",
1737                            "StartProviding",
1738                            "StopProviding",
1739                            "Rejected",
1740                            "IsStarting",
1741                            "SetNodes",
1742                            "Reset",
1743                        ],
1744                    )),
1745                }
1746            }
1747
1748            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1749            where
1750                A: MapAccess<'de>,
1751            {
1752                let (variant, value) = map
1753                    .next_entry::<String, serde_json::Value>()?
1754                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
1755
1756                // Ensure there are no extra entries
1757                if map.next_entry::<String, serde_json::Value>()?.is_some() {
1758                    return Err(de::Error::custom("unexpected extra entries in map"));
1759                }
1760
1761                match variant.as_str() {
1762                    "GetManifest" => {
1763                        let manifest = serde_json::from_value(value).map_err(de::Error::custom)?;
1764                        Ok(CacherResponse::GetManifest(manifest))
1765                    }
1766                    "GetLogCacheContent" => {
1767                        let result = serde_json::from_value(value).map_err(de::Error::custom)?;
1768                        Ok(CacherResponse::GetLogCacheContent(result))
1769                    }
1770                    "GetStatus" => {
1771                        let status = serde_json::from_value(value).map_err(de::Error::custom)?;
1772                        Ok(CacherResponse::GetStatus(status))
1773                    }
1774                    "GetLogsByRange" => {
1775                        let result = serde_json::from_value(value).map_err(de::Error::custom)?;
1776                        Ok(CacherResponse::GetLogsByRange(result))
1777                    }
1778                    "StartProviding" => {
1779                        let result = serde_json::from_value(value).map_err(de::Error::custom)?;
1780                        Ok(CacherResponse::StartProviding(result))
1781                    }
1782                    "StopProviding" => {
1783                        let result = serde_json::from_value(value).map_err(de::Error::custom)?;
1784                        Ok(CacherResponse::StopProviding(result))
1785                    }
1786                    "SetNodes" => {
1787                        let result = serde_json::from_value(value).map_err(de::Error::custom)?;
1788                        Ok(CacherResponse::SetNodes(result))
1789                    }
1790                    "Reset" => {
1791                        let result = serde_json::from_value(value).map_err(de::Error::custom)?;
1792                        Ok(CacherResponse::Reset(result))
1793                    }
1794                    _ => Err(de::Error::unknown_variant(
1795                        &variant,
1796                        &[
1797                            "GetManifest",
1798                            "GetLogCacheContent",
1799                            "GetStatus",
1800                            "GetLogsByRange",
1801                            "StartProviding",
1802                            "StopProviding",
1803                            "Rejected",
1804                            "IsStarting",
1805                            "SetNodes",
1806                            "Reset",
1807                        ],
1808                    )),
1809                }
1810            }
1811        }
1812
1813        deserializer.deserialize_any(CacherResponseVisitor)
1814    }
1815}
1816
1817impl Serialize for LogsMetadata {
1818    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1819    where
1820        S: Serializer,
1821    {
1822        let mut state = serializer.serialize_struct("LogsMetadata", 6)?;
1823        state.serialize_field("chainId", &self.chain_id)?;
1824        state.serialize_field("fromBlock", &self.from_block)?;
1825        state.serialize_field("toBlock", &self.to_block)?;
1826        state.serialize_field("timeCreated", &self.time_created)?;
1827        state.serialize_field("createdBy", &self.created_by)?;
1828        state.serialize_field("signature", &self.signature)?;
1829        state.end()
1830    }
1831}
1832
1833impl<'de> Deserialize<'de> for LogsMetadata {
1834    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1835    where
1836        D: Deserializer<'de>,
1837    {
1838        #[derive(Deserialize)]
1839        #[serde(field_identifier, rename_all = "camelCase")]
1840        enum Field {
1841            ChainId,
1842            FromBlock,
1843            ToBlock,
1844            TimeCreated,
1845            CreatedBy,
1846            Signature,
1847        }
1848
1849        struct LogsMetadataVisitor;
1850
1851        impl<'de> Visitor<'de> for LogsMetadataVisitor {
1852            type Value = LogsMetadata;
1853
1854            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1855                formatter.write_str("struct LogsMetadata")
1856            }
1857
1858            fn visit_map<V>(self, mut map: V) -> Result<LogsMetadata, V::Error>
1859            where
1860                V: MapAccess<'de>,
1861            {
1862                let mut chain_id = None;
1863                let mut from_block = None;
1864                let mut to_block = None;
1865                let mut time_created = None;
1866                let mut created_by = None;
1867                let mut signature = None;
1868
1869                while let Some(key) = map.next_key()? {
1870                    match key {
1871                        Field::ChainId => {
1872                            if chain_id.is_some() {
1873                                return Err(de::Error::duplicate_field("chainId"));
1874                            }
1875                            chain_id = Some(map.next_value()?);
1876                        }
1877                        Field::FromBlock => {
1878                            if from_block.is_some() {
1879                                return Err(de::Error::duplicate_field("fromBlock"));
1880                            }
1881                            from_block = Some(map.next_value()?);
1882                        }
1883                        Field::ToBlock => {
1884                            if to_block.is_some() {
1885                                return Err(de::Error::duplicate_field("toBlock"));
1886                            }
1887                            to_block = Some(map.next_value()?);
1888                        }
1889                        Field::TimeCreated => {
1890                            if time_created.is_some() {
1891                                return Err(de::Error::duplicate_field("timeCreated"));
1892                            }
1893                            time_created = Some(map.next_value()?);
1894                        }
1895                        Field::CreatedBy => {
1896                            if created_by.is_some() {
1897                                return Err(de::Error::duplicate_field("createdBy"));
1898                            }
1899                            created_by = Some(map.next_value()?);
1900                        }
1901                        Field::Signature => {
1902                            if signature.is_some() {
1903                                return Err(de::Error::duplicate_field("signature"));
1904                            }
1905                            signature = Some(map.next_value()?);
1906                        }
1907                    }
1908                }
1909
1910                let chain_id = chain_id.ok_or_else(|| de::Error::missing_field("chainId"))?;
1911                let from_block = from_block.ok_or_else(|| de::Error::missing_field("fromBlock"))?;
1912                let to_block = to_block.ok_or_else(|| de::Error::missing_field("toBlock"))?;
1913                let time_created =
1914                    time_created.ok_or_else(|| de::Error::missing_field("timeCreated"))?;
1915                let created_by = created_by.ok_or_else(|| de::Error::missing_field("createdBy"))?;
1916                let signature = signature.ok_or_else(|| de::Error::missing_field("signature"))?;
1917
1918                Ok(LogsMetadata {
1919                    chain_id,
1920                    from_block,
1921                    to_block,
1922                    time_created,
1923                    created_by,
1924                    signature,
1925                })
1926            }
1927        }
1928
1929        deserializer.deserialize_struct(
1930            "LogsMetadata",
1931            &[
1932                "chainId",
1933                "fromBlock",
1934                "toBlock",
1935                "timeCreated",
1936                "createdBy",
1937                "signature",
1938            ],
1939            LogsMetadataVisitor,
1940        )
1941    }
1942}
1943
1944impl Serialize for GetLogsByRangeOkResponse {
1945    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1946    where
1947        S: Serializer,
1948    {
1949        match self {
1950            GetLogsByRangeOkResponse::Logs(tuple) => {
1951                let mut map = serializer.serialize_map(Some(1))?;
1952                map.serialize_entry("Logs", tuple)?;
1953                map.end()
1954            }
1955            GetLogsByRangeOkResponse::Latest(block) => {
1956                let mut map = serializer.serialize_map(Some(1))?;
1957                map.serialize_entry("Latest", block)?;
1958                map.end()
1959            }
1960        }
1961    }
1962}
1963
1964impl<'de> Deserialize<'de> for GetLogsByRangeOkResponse {
1965    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1966    where
1967        D: Deserializer<'de>,
1968    {
1969        struct GetLogsByRangeOkResponseVisitor;
1970
1971        impl<'de> Visitor<'de> for GetLogsByRangeOkResponseVisitor {
1972            type Value = GetLogsByRangeOkResponse;
1973
1974            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1975                formatter.write_str(
1976                    "a map with a single key representing the GetLogsByRangeOkResponse variant",
1977                )
1978            }
1979
1980            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1981            where
1982                A: MapAccess<'de>,
1983            {
1984                let (variant, value) = map
1985                    .next_entry::<String, serde_json::Value>()?
1986                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
1987
1988                match variant.as_str() {
1989                    "Logs" => {
1990                        let tuple = serde_json::from_value(value).map_err(de::Error::custom)?;
1991                        Ok(GetLogsByRangeOkResponse::Logs(tuple))
1992                    }
1993                    "Latest" => {
1994                        let block = serde_json::from_value(value).map_err(de::Error::custom)?;
1995                        Ok(GetLogsByRangeOkResponse::Latest(block))
1996                    }
1997                    _ => Err(de::Error::unknown_variant(&variant, &["Logs", "Latest"])),
1998                }
1999            }
2000        }
2001
2002        deserializer.deserialize_map(GetLogsByRangeOkResponseVisitor)
2003    }
2004}