blvm-node 0.1.2

Bitcoin Commons BLVM: Minimal Bitcoin node implementation using blvm-protocol and blvm-consensus
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
//! Module system traits and interfaces
//!
//! Defines the core traits that modules and the node use to communicate.

use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;

use crate::module::ipc::protocol::{EventPayload, ModuleMessage};
use crate::{Block, BlockHeader, Hash, Transaction};

/// Module lifecycle state
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ModuleState {
    /// Module is stopped/not running
    Stopped,
    /// Module is initializing
    Initializing,
    /// Module is running normally
    Running,
    /// Module is stopping
    Stopping,
    /// Module has crashed or errored
    Error(String),
}

/// Module metadata describing module identity and capabilities
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleMetadata {
    /// Module name (unique identifier)
    pub name: String,
    /// Module version (semantic versioning)
    pub version: String,
    /// Human-readable description
    pub description: String,
    /// Module author
    pub author: String,
    /// Capabilities this module declares it can use
    pub capabilities: Vec<String>,
    /// Required dependencies (module names with versions)
    /// Hard dependencies - module cannot load without them
    pub dependencies: HashMap<String, String>,
    /// Optional dependencies (module names with versions)
    /// Soft dependencies - module can work without them
    pub optional_dependencies: HashMap<String, String>,
    /// Module entry point (binary name or path)
    pub entry_point: String,
}

/// Module information for discovery
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInfo {
    /// Module ID (unique identifier, format: {module_name}_{uuid})
    pub module_id: String,
    /// Module name (from metadata)
    pub module_name: String,
    /// Module version
    pub version: String,
    /// Module capabilities
    pub capabilities: Vec<String>,
    /// Module status
    pub status: ModuleState,
    /// API version (for compatibility checking)
    pub api_version: u32,
}

/// Module hooks for providing cached data (advisory only)
///
/// Modules can implement this trait to provide cached values for expensive operations.
/// All hooks are advisory - the node can ignore them if needed.
/// All hooks have timeouts to prevent hanging on unresponsive modules.
#[async_trait]
pub trait ModuleHooks: Send + Sync {
    /// Hook called when getting fee estimate
    /// Return Some(u64) to use cached value, None to calculate normally
    /// Timeout: 100ms (fail fast if module unresponsive)
    async fn get_fee_estimate_cached(&self, target_blocks: u32)
        -> Result<Option<u64>, ModuleError>;

    /// Hook called when getting mempool stats
    /// Return Some(MempoolStats) to use cached value, None to calculate normally
    /// Timeout: 50ms (fail fast if module unresponsive)
    async fn get_mempool_stats_cached(&self) -> Result<Option<MempoolSize>, ModuleError>;
}

/// Module trait that all modules must implement
///
/// This trait is implemented by module binaries (separate processes),
/// not directly by Rust code in the node. The IPC layer translates
/// between this trait interface and the actual module process.
#[async_trait]
pub trait Module: Send + Sync {
    /// Initialize the module with given context
    ///
    /// Called when module is first loaded. Module should validate
    /// configuration and prepare for operation.
    async fn init(&mut self, context: ModuleContext) -> Result<(), ModuleError>;

    /// Start the module
    ///
    /// Module should begin its main processing loop here.
    async fn start(&mut self) -> Result<(), ModuleError>;

    /// Stop the module (graceful shutdown)
    ///
    /// Module should clean up resources and stop processing.
    async fn stop(&mut self) -> Result<(), ModuleError>;

    /// Shutdown the module (forced shutdown)
    ///
    /// Called when node is shutting down or module is being removed.
    /// Module must terminate immediately.
    async fn shutdown(&mut self) -> Result<(), ModuleError>;

    /// Get module metadata
    fn metadata(&self) -> &ModuleMetadata;

    /// Get current module state
    fn state(&self) -> ModuleState;
}

/// Context provided to modules for communication with node
///
/// This is the interface modules use to communicate with the base node.
/// All communication goes through IPC, so this is essentially a handle
/// to the IPC connection.
#[derive(Debug, Clone)]
pub struct ModuleContext {
    /// Module ID (unique identifier for this module instance)
    pub module_id: String,
    /// IPC socket path (Unix domain socket path for communication)
    pub socket_path: String,
    /// Module data directory (where module can store its state)
    pub data_dir: String,
    /// Module configuration (key-value pairs from config file)
    pub config: HashMap<String, String>,
}

impl ModuleContext {
    /// Create a new module context
    pub fn new(
        module_id: String,
        socket_path: String,
        data_dir: String,
        config: HashMap<String, String>,
    ) -> Self {
        Self {
            module_id,
            socket_path,
            data_dir,
            config,
        }
    }

    /// Get a configuration value
    pub fn get_config(&self, key: &str) -> Option<&String> {
        self.config.get(key)
    }

    /// Get a configuration value with default
    pub fn get_config_or(&self, key: &str, default: &str) -> String {
        self.config
            .get(key)
            .map(|s| s.as_str())
            .unwrap_or(default)
            .to_string()
    }
}

/// Node API trait - interface for modules to query node state
///
/// This trait defines what APIs modules can call on the node.
/// Implemented by the node side, used by modules through IPC.
#[async_trait]
pub trait NodeAPI: Send + Sync {
    /// Get a block by hash
    async fn get_block(&self, hash: &Hash) -> Result<Option<Block>, ModuleError>;

    /// Get a block header by hash
    async fn get_block_header(&self, hash: &Hash) -> Result<Option<BlockHeader>, ModuleError>;

    /// Get a transaction by hash
    async fn get_transaction(&self, hash: &Hash) -> Result<Option<Transaction>, ModuleError>;

    /// Check if a transaction exists
    async fn has_transaction(&self, hash: &Hash) -> Result<bool, ModuleError>;

    /// Get current chain tip (highest block hash)
    async fn get_chain_tip(&self) -> Result<Hash, ModuleError>;

    /// Get current block height
    async fn get_block_height(&self) -> Result<u64, ModuleError>;

    /// Get UTXO by outpoint (read-only, cannot modify)
    async fn get_utxo(
        &self,
        outpoint: &crate::OutPoint,
    ) -> Result<Option<crate::UTXO>, ModuleError>;

    /// Subscribe to node events
    ///
    /// Returns a receiver that will receive event messages
    async fn subscribe_events(
        &self,
        event_types: Vec<EventType>,
    ) -> Result<tokio::sync::mpsc::Receiver<ModuleMessage>, ModuleError>;

    // === Mempool API Methods ===
    /// Get all transaction hashes in mempool
    async fn get_mempool_transactions(&self) -> Result<Vec<Hash>, ModuleError>;

    /// Get a transaction from mempool by hash
    async fn get_mempool_transaction(
        &self,
        tx_hash: &Hash,
    ) -> Result<Option<Transaction>, ModuleError>;

    /// Get mempool size information
    async fn get_mempool_size(&self) -> Result<MempoolSize, ModuleError>;

    // === Network API Methods ===
    /// Get network statistics
    async fn get_network_stats(&self) -> Result<NetworkStats, ModuleError>;

    /// Get list of connected peers
    async fn get_network_peers(&self) -> Result<Vec<PeerInfo>, ModuleError>;

    // === Chain API Methods ===
    /// Get chain information (tip, height, difficulty, etc.)
    async fn get_chain_info(&self) -> Result<ChainInfo, ModuleError>;

    /// Get block by height
    async fn get_block_by_height(&self, height: u64) -> Result<Option<Block>, ModuleError>;

    // === Lightning API Methods (for Lightning module) ===
    /// Get Lightning node connection info
    async fn get_lightning_node_url(&self) -> Result<Option<String>, ModuleError>;

    /// Get Lightning node information
    async fn get_lightning_info(&self) -> Result<Option<LightningInfo>, ModuleError>;

    // === Payment API Methods ===
    /// Get payment state by payment ID
    async fn get_payment_state(
        &self,
        payment_id: &str,
    ) -> Result<Option<PaymentState>, ModuleError>;

    // === Additional Mempool API Methods ===
    /// Check if a transaction is in the mempool
    async fn check_transaction_in_mempool(&self, tx_hash: &Hash) -> Result<bool, ModuleError>;

    /// Get fee estimate for target confirmation blocks
    /// Returns fee rate in satoshis per vbyte
    async fn get_fee_estimate(&self, target_blocks: u32) -> Result<u64, ModuleError>;

    // === Module RPC Endpoint Registration ===
    /// Register a JSON-RPC endpoint
    /// Method name must have module prefix (e.g., "lightning_*", "stratum_*")
    /// Cannot override core endpoints (whitelist enforced)
    async fn register_rpc_endpoint(
        &self,
        method: String,
        description: String,
    ) -> Result<(), ModuleError>;

    /// Unregister an RPC endpoint (on module shutdown)
    async fn unregister_rpc_endpoint(&self, method: &str) -> Result<(), ModuleError>;

    // === Timers and Scheduled Tasks ===
    /// Register a periodic timer
    /// Returns a timer ID that can be used to cancel
    /// Callback is async and can call NodeAPI methods
    async fn register_timer(
        &self,
        interval_seconds: u64,
        callback: Arc<dyn crate::module::timers::manager::TimerCallback>,
    ) -> Result<crate::module::timers::manager::TimerId, ModuleError>;

    /// Cancel a registered timer
    async fn cancel_timer(
        &self,
        timer_id: crate::module::timers::manager::TimerId,
    ) -> Result<(), ModuleError>;

    /// Schedule a one-time task
    /// Callback is async and can call NodeAPI methods
    async fn schedule_task(
        &self,
        delay_seconds: u64,
        callback: Arc<dyn crate::module::timers::manager::TaskCallback>,
    ) -> Result<crate::module::timers::manager::TaskId, ModuleError>;

    // === Metrics and Telemetry ===
    /// Report a metric to the node
    async fn report_metric(
        &self,
        metric: crate::module::metrics::manager::Metric,
    ) -> Result<(), ModuleError>;

    /// Get module metrics (for RPC/metrics endpoint)
    async fn get_module_metrics(
        &self,
        module_id: &str,
    ) -> Result<Vec<crate::module::metrics::manager::Metric>, ModuleError>;

    /// Get aggregated metrics from all modules
    async fn get_all_metrics(
        &self,
    ) -> Result<
        std::collections::HashMap<String, Vec<crate::module::metrics::manager::Metric>>,
        ModuleError,
    >;

    // === Filesystem API Methods ===
    /// Read a file from the module's data directory
    /// Path must be within the module's sandboxed data directory
    async fn read_file(&self, path: String) -> Result<Vec<u8>, ModuleError>;

    /// Write data to a file in the module's data directory
    /// Path must be within the module's sandboxed data directory
    async fn write_file(&self, path: String, data: Vec<u8>) -> Result<(), ModuleError>;

    /// Delete a file from the module's data directory
    async fn delete_file(&self, path: String) -> Result<(), ModuleError>;

    /// List directory contents
    async fn list_directory(&self, path: String) -> Result<Vec<String>, ModuleError>;

    /// Create a directory in the module's data directory
    async fn create_directory(&self, path: String) -> Result<(), ModuleError>;

    /// Get file metadata (size, type, timestamps)
    async fn get_file_metadata(
        &self,
        path: String,
    ) -> Result<crate::module::ipc::protocol::FileMetadata, ModuleError>;

    /// Initialize filesystem and storage access for a module
    /// Called when a module connects to set up per-module sandbox and storage
    async fn initialize_module(
        &self,
        module_id: String,
        module_data_dir: std::path::PathBuf,
        base_data_dir: std::path::PathBuf,
    ) -> Result<(), ModuleError>;

    // === Module Discovery API Methods ===
    /// Discover all available modules
    /// Returns list of module information for all loaded modules
    async fn discover_modules(&self) -> Result<Vec<ModuleInfo>, ModuleError>;

    /// Get information about a specific module
    /// Returns None if module is not loaded
    async fn get_module_info(&self, module_id: &str) -> Result<Option<ModuleInfo>, ModuleError>;

    /// Check if a module is available (loaded and running)
    async fn is_module_available(&self, module_id: &str) -> Result<bool, ModuleError>;

    // === Module Event Publishing ===
    /// Publish an event (modules can publish to other modules)
    /// Event will be delivered to all modules subscribed to the event type
    async fn publish_event(
        &self,
        event_type: EventType,
        payload: EventPayload,
    ) -> Result<(), ModuleError>;

    // === Module-to-Module Communication ===
    /// Call an API method on another module
    ///
    /// # Arguments
    /// * `target_module_id` - ID of the target module (or None to use method routing)
    /// * `method` - API method name
    /// * `params` - Serialized parameters (typically JSON)
    ///
    /// # Returns
    /// Serialized response data
    async fn call_module(
        &self,
        target_module_id: Option<&str>,
        method: &str,
        params: Vec<u8>,
    ) -> Result<Vec<u8>, ModuleError>;

    /// Register a module API for other modules to call
    ///
    /// # Arguments
    /// * `api` - The API implementation
    async fn register_module_api(
        &self,
        api: Arc<dyn crate::module::inter_module::api::ModuleAPI>,
    ) -> Result<(), ModuleError>;

    /// Unregister a module API
    async fn unregister_module_api(&self) -> Result<(), ModuleError>;

    // === Network Integration ===
    /// Send mesh packet to a module (for network layer integration)
    /// This allows the network layer to route mesh packets to the mesh module
    async fn send_mesh_packet_to_module(
        &self,
        module_id: &str,
        packet_data: Vec<u8>,
        peer_addr: String,
    ) -> Result<(), ModuleError>;

    /// Send mesh packet to a network peer
    /// This allows modules to send mesh packets to network peers
    async fn send_mesh_packet_to_peer(
        &self,
        peer_addr: String,
        packet_data: Vec<u8>,
    ) -> Result<(), ModuleError>;

    /// Send Stratum V2 message to peer (miner)
    /// This allows the Stratum V2 module to send messages to miners
    async fn send_stratum_v2_message_to_peer(
        &self,
        peer_addr: String,
        message_data: Vec<u8>,
    ) -> Result<(), ModuleError>;

    // === Module Health & Monitoring ===
    /// Get health status of a module
    async fn get_module_health(
        &self,
        module_id: &str,
    ) -> Result<Option<crate::module::process::monitor::ModuleHealth>, ModuleError>;

    /// Get health status of all modules
    async fn get_all_module_health(
        &self,
    ) -> Result<Vec<(String, crate::module::process::monitor::ModuleHealth)>, ModuleError>;

    /// Report module health (for modules to self-report)
    async fn report_module_health(
        &self,
        health: crate::module::process::monitor::ModuleHealth,
    ) -> Result<(), ModuleError>;

    // === Mining API Methods ===
    /// Get block template (GBT equivalent)
    ///
    /// Returns a block template suitable for mining.
    /// Uses the same formally verified consensus function as RPC getblocktemplate.
    ///
    /// # Arguments
    /// * `rules` - Consensus rules to apply (e.g., ["segwit"])
    /// * `coinbase_script` - Optional coinbase script (for custom coinbase)
    /// * `coinbase_address` - Optional coinbase address (for custom coinbase)
    ///
    /// # Returns
    /// BlockTemplate with header, coinbase transaction, and selected transactions
    async fn get_block_template(
        &self,
        rules: Vec<String>,
        coinbase_script: Option<Vec<u8>>,
        coinbase_address: Option<String>,
    ) -> Result<blvm_protocol::mining::BlockTemplate, ModuleError>;

    /// Submit a solved block
    ///
    /// Submits a fully solved block to the network.
    /// The block must be valid and meet the difficulty target.
    ///
    /// # Arguments
    /// * `block` - The solved block to submit
    ///
    /// # Returns
    /// SubmitBlockResult indicating acceptance, rejection, or duplicate
    async fn submit_block(&self, block: Block) -> Result<SubmitBlockResult, ModuleError>;

    /// Merge block hashes into the node's denylist for serving full `block` messages on the network
    /// (e.g. `getdata`). Additive; callers may include selective-sync, policy/compliance, or tests.
    /// Peers receive `notfound` for these hashes instead of a full block.
    async fn merge_block_serve_denylist(&self, block_hashes: &[Hash]) -> Result<(), ModuleError>;

    /// Bounded snapshot of the block serve denylist (for status and debugging).
    async fn get_block_serve_denylist_snapshot(
        &self,
    ) -> Result<BlockServeDenylistSnapshot, ModuleError>;

    async fn clear_block_serve_denylist(&self) -> Result<(), ModuleError>;

    async fn replace_block_serve_denylist(&self, block_hashes: &[Hash]) -> Result<(), ModuleError>;

    /// Merge txids into the denylist for serving full `tx` on `getdata` (additive).
    async fn merge_tx_serve_denylist(&self, tx_hashes: &[Hash]) -> Result<(), ModuleError>;

    async fn get_tx_serve_denylist_snapshot(&self) -> Result<TxServeDenylistSnapshot, ModuleError>;

    async fn clear_tx_serve_denylist(&self) -> Result<(), ModuleError>;

    async fn replace_tx_serve_denylist(&self, tx_hashes: &[Hash]) -> Result<(), ModuleError>;

    /// Sync coordinator phase and progress (requires sync coordinator on the node API).
    async fn get_sync_status(&self) -> Result<SyncStatus, ModuleError>;

    /// Ban a peer by socket address string. `ban_duration_seconds: None` means permanent.
    async fn ban_peer(
        &self,
        peer_addr: &str,
        ban_duration_seconds: Option<u64>,
    ) -> Result<(), ModuleError>;

    /// When enabled, refuse all full-block `getdata` answers (operational maintenance).
    async fn set_block_serve_maintenance_mode(&self, enabled: bool) -> Result<(), ModuleError>;
}

/// Event types that modules can subscribe to
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EventType {
    // === Core Blockchain Events ===
    /// New block connected to chain
    NewBlock,
    /// New transaction in mempool
    NewTransaction,
    /// Block disconnected (chain reorg)
    BlockDisconnected,
    /// Chain reorganization occurred
    ChainReorg,

    // === Payment Events (for Lightning module) ===
    /// Payment request created
    PaymentRequestCreated,
    /// Payment settled (confirmed on-chain)
    PaymentSettled,
    /// Payment failed
    PaymentFailed,
    /// Lightning payment verified
    PaymentVerified,
    /// Payment route discovered
    PaymentRouteFound,
    /// Payment routing failed
    PaymentRouteFailed,
    /// Lightning channel opened
    ChannelOpened,
    /// Lightning channel closed
    ChannelClosed,

    // === Mining Events (for Stratum V2 module) ===
    /// Block mined successfully
    BlockMined,
    /// Block template updated
    BlockTemplateUpdated,
    /// Mining difficulty changed
    MiningDifficultyChanged,
    /// Mining job created
    MiningJobCreated,

    // === Mesh Networking Events ===
    /// Mesh packet received from network
    MeshPacketReceived,
    // === Stratum V2 Events ===
    /// Stratum V2 message received from network
    StratumV2MessageReceived,
    /// Mining share submitted
    ShareSubmitted,
    /// Merge mining reward received
    MergeMiningReward,
    /// Mining pool connected
    MiningPoolConnected,
    /// Mining pool disconnected
    MiningPoolDisconnected,

    // === Governance Events (for Governance module) ===
    /// Governance proposal created
    GovernanceProposalCreated,
    /// Vote cast on proposal
    GovernanceProposalVoted,
    /// Proposal merged
    GovernanceProposalMerged,
    /// Webhook sent
    WebhookSent,
    /// Webhook delivery failed
    WebhookFailed,
    /// Governance fork detected
    GovernanceForkDetected,

    // === Network Events (for Mesh module) ===
    /// Peer connected
    PeerConnected,
    /// Peer disconnected
    PeerDisconnected,
    /// Peer banned
    PeerBanned,
    /// Peer unbanned
    PeerUnbanned,
    /// Network message received
    MessageReceived,
    /// Network message sent
    MessageSent,
    /// Broadcast operation started
    BroadcastStarted,
    /// Broadcast operation completed
    BroadcastCompleted,
    /// Route discovered
    RouteDiscovered,
    /// Route failed
    RouteFailed,
    /// Connection attempt (success/failure)
    ConnectionAttempt,
    /// New peer address discovered
    AddressDiscovered,
    /// Peer address expired
    AddressExpired,
    /// Network partition detected
    NetworkPartition,
    /// Network partition reconnected
    NetworkReconnected,
    /// DoS attack detected
    DoSAttackDetected,
    /// Rate limit exceeded
    RateLimitExceeded,

    // === Consensus Events ===
    /// Block validation started
    BlockValidationStarted,
    /// Block validation completed (success/failure)
    BlockValidationCompleted,
    /// Script verification started
    ScriptVerificationStarted,
    /// Script verification completed
    ScriptVerificationCompleted,
    /// UTXO validation started
    UTXOValidationStarted,
    /// UTXO validation completed
    UTXOValidationCompleted,
    /// Network difficulty adjusted
    DifficultyAdjusted,
    /// Soft fork activated (SegWit, Taproot, CTV, etc.)
    SoftForkActivated,
    /// Soft fork locked in (BIP9)
    SoftForkLockedIn,
    /// Consensus rule violation detected
    ConsensusRuleViolation,

    // === Sync Events ===
    /// Headers sync started
    HeadersSyncStarted,
    /// Headers sync progress update
    HeadersSyncProgress,
    /// Headers sync completed
    HeadersSyncCompleted,
    /// Block sync started (IBD)
    BlockSyncStarted,
    /// Block sync progress update
    BlockSyncProgress,
    /// Block sync completed
    BlockSyncCompleted,
    /// Sync state changed (Initial → Headers → Blocks → Synced)
    SyncStateChanged,

    // === Mempool Events ===
    /// Transaction added to mempool
    MempoolTransactionAdded,
    /// Transaction removed from mempool
    MempoolTransactionRemoved,
    /// Mempool size threshold exceeded
    MempoolThresholdExceeded,
    /// Fee rate changed (derived from mempool)
    FeeRateChanged,
    /// Mempool cleared
    MempoolCleared,

    // === Storage Events ===
    /// Storage read operation
    StorageRead,
    /// Storage write operation
    StorageWrite,
    /// Storage query operation
    StorageQuery,
    /// Database backup started
    DatabaseBackupStarted,
    /// Database backup completed
    DatabaseBackupCompleted,

    // === Module Lifecycle Events ===
    /// Module loaded successfully
    ModuleLoaded,
    /// Module unloaded
    ModuleUnloaded,
    /// Module reloaded
    ModuleReloaded,
    /// Module started
    ModuleStarted,
    /// Module stopped
    ModuleStopped,
    /// Module crashed
    ModuleCrashed,
    /// Module health status changed
    ModuleHealthChanged,
    /// Module state changed
    ModuleStateChanged,

    // === Configuration Events ===
    /// Node configuration loaded/changed
    /// Modules can subscribe to this to react to config changes
    ConfigLoaded,

    // === Node Lifecycle Events ===
    /// Node is shutting down (modules should clean up gracefully)
    NodeShutdown,
    /// Node shutdown completed
    NodeShutdownCompleted,
    /// Node startup completed (all components initialized)
    NodeStartupCompleted,

    // === Maintenance Events ===
    /// Maintenance operation started (modules can prepare)
    MaintenanceStarted,
    /// Maintenance operation completed
    MaintenanceCompleted,
    /// Data maintenance requested (unified cleanup/flush event)
    /// Modules should clean up old data and/or flush pending writes
    /// Urgency level indicates how urgent the operation is
    DataMaintenance,
    /// Health check performed (modules can report their health)
    HealthCheck,

    // === Resource Management Events ===
    /// Disk space is low (modules should clean up data)
    DiskSpaceLow,
    /// Resource limit warning (modules should reduce usage)
    ResourceLimitWarning,

    // === Dandelion++ Events ===
    /// Transaction entered stem phase
    DandelionStemStarted,
    /// Transaction advanced in stem phase
    DandelionStemAdvanced,
    /// Transaction transitioned to fluff phase
    DandelionFluffed,
    /// Stem path expired
    DandelionStemPathExpired,

    // === Compact Blocks Events ===
    /// Compact block received
    CompactBlockReceived,
    /// Block reconstruction started
    BlockReconstructionStarted,
    /// Block reconstruction completed
    BlockReconstructionCompleted,

    // === FIBRE Events ===
    /// Block encoded for FIBRE
    FibreBlockEncoded,
    /// Block sent via FIBRE
    FibreBlockSent,
    /// FIBRE peer registered
    FibrePeerRegistered,

    // === Package Relay Events ===
    /// Transaction package received
    PackageReceived,
    /// Transaction package rejected
    PackageRejected,

    // === UTXO Commitments Events ===
    /// UTXO commitment received
    UtxoCommitmentReceived,
    /// UTXO commitment verified
    UtxoCommitmentVerified,

    // === Ban List Sharing Events ===
    /// Ban list shared with peer
    BanListShared,
    /// Ban list received from peer
    BanListReceived,

    // === Extended module / mining events ===
    /// Selective sync policy applied (subscribe, refresh, etc.)
    SelectiveSyncPolicyApplied,
    /// Mining action executed (miningos trigger_action)
    ActionExecuted,
    /// Module purchase completed (marketplace)
    ModulePurchaseCompleted,
    /// Stratum V2 miner connected
    StratumClientConnected,
    /// Stratum V2 miner disconnected
    StratumClientDisconnected,
    /// Block filtered during IBD (selective sync, prune, etc.)
    IBDBlockFiltered,

    // === Module Registry Events ===
    /// Module discovered
    ModuleDiscovered,
    /// Module installed
    ModuleInstalled,
    /// Module updated
    ModuleUpdated,
    /// Module removed
    ModuleRemoved,
}

/// Module system errors
#[derive(Debug, Error)]
pub enum ModuleError {
    #[error("IPC communication error: {0}")]
    IpcError(String),

    #[error("Module initialization failed: {0}")]
    InitializationError(String),

    #[error("Module operation failed: {0}")]
    OperationError(String),

    #[error("Permission denied: {0}")]
    PermissionDenied(String),

    #[error("Module not found: {0}")]
    ModuleNotFound(String),

    #[error("Module dependency missing: {0}")]
    DependencyMissing(String),

    #[error("Invalid module manifest: {0}")]
    InvalidManifest(String),

    #[error("Module version incompatible: {0}")]
    VersionIncompatible(String),

    #[error("Module crashed: {0}")]
    ModuleCrashed(String),

    #[error("Serialization error: {0}")]
    SerializationError(String),

    #[error("Rate limit exceeded: {0}")]
    RateLimitExceeded(String),

    #[error("Timeout waiting for module response")]
    Timeout,

    #[error("Resource limit exceeded: {0}")]
    ResourceLimitExceeded(String),

    #[error("Cryptographic error: {0}")]
    CryptoError(String),

    // Module extension errors (CLI, RPC, migration, config handlers)
    #[error("Config error: {0}")]
    Config(String),

    #[error("RPC error: {0}")]
    Rpc(String),

    #[error("Migration error: {0}")]
    Migration(String),

    #[error("CLI error: {0}")]
    Cli(String),

    #[error("{0}")]
    Other(String),
}

impl From<serde_json::Error> for ModuleError {
    fn from(e: serde_json::Error) -> Self {
        ModuleError::SerializationError(e.to_string())
    }
}

impl From<bincode::Error> for ModuleError {
    fn from(e: bincode::Error) -> Self {
        ModuleError::SerializationError(e.to_string())
    }
}

impl From<anyhow::Error> for ModuleError {
    fn from(e: anyhow::Error) -> Self {
        ModuleError::OperationError(e.to_string())
    }
}

impl ModuleError {
    /// Wrap an error with a context message. Use instead of
    /// `ModuleError::OperationError(format!("{msg}: {e}"))` for consistent formatting.
    pub fn op_err<E: std::fmt::Display>(msg: &str, e: E) -> Self {
        ModuleError::OperationError(format!("{msg}: {e}"))
    }
}

impl From<String> for ModuleError {
    fn from(s: String) -> Self {
        ModuleError::Other(s)
    }
}

impl From<&str> for ModuleError {
    fn from(s: &str) -> Self {
        ModuleError::Other(s.to_string())
    }
}

/// Module API error message constants. Use these with `ModuleError::OperationError(s.into())`
/// or `.to_string()` so wording stays consistent and i18n-friendly.
pub mod module_error_msg {
    pub const CHAIN_NOT_INITIALIZED: &str = "Chain not initialized";
    pub const CHAIN_NOT_YET_INITIALIZED: &str = "Chain not yet initialized";
    pub const EVENT_MANAGER_NOT_AVAILABLE: &str = "Event manager not available";
    pub const IPC_SERVER_NOT_AVAILABLE: &str = "IPC server not available";
    pub const MEMPOOL_MANAGER_NOT_AVAILABLE: &str = "Mempool manager not available";
    pub const METRICS_MANAGER_NOT_AVAILABLE: &str = "Metrics manager not available";
    pub const MODULE_API_REGISTRY_NOT_AVAILABLE: &str = "Module API registry not available";
    pub const MODULE_ID_NOT_SET: &str = "Module ID not set";
    pub const MODULE_ID_NOT_SET_FOR_METRICS: &str = "Module ID not set for metrics reporting";
    pub const MODULE_ID_NOT_SET_FOR_TASK: &str = "Module ID not set for task scheduling";
    pub const MODULE_ID_NOT_SET_FOR_TIMER: &str = "Module ID not set for timer registration";
    pub const MODULE_MANAGER_NOT_AVAILABLE: &str = "Module manager not available";
    pub const MODULE_REGISTRY_NOT_AVAILABLE: &str = "Module registry not available";
    pub const MODULE_ROUTER_NOT_AVAILABLE: &str = "Module router not available";
    pub const NETWORK_MANAGER_NOT_AVAILABLE: &str = "Network manager not available";
    pub const NO_CHAIN_TIP: &str = "No chain tip";
    pub const PAYMENT_STATE_MACHINE_NOT_AVAILABLE: &str = "Payment state machine not available";
    pub const RPC_SERVER_NOT_AVAILABLE: &str = "RPC server not available";
    pub const TIMER_MANAGER_NOT_AVAILABLE: &str = "Timer manager not available";

    // IPC / invocation
    pub const INVOCATION_RESPONSE_CHANNEL_CLOSED: &str = "Invocation response channel closed";
    pub const MODULE_CONNECTION_CLOSED: &str = "Module connection closed";
    pub const MODULE_DID_NOT_RESPOND_CLI_60S: &str =
        "Module did not respond to CLI invocation within 60 seconds";
    pub const MODULE_DID_NOT_RESPOND_RPC_60S: &str =
        "Module did not respond to RPC invocation within 60 seconds";
    pub const MODULE_RETURNED_SUCCESS_BUT_NO_PAYLOAD: &str =
        "Module returned success but no payload";
    pub const MODULE_RETURNED_WRONG_PAYLOAD_TYPE_RPC: &str =
        "Module returned wrong payload type for RPC";
    pub const NO_MODULE_WITH_CLI_NAME_LOADED: &str = "No module with CLI name '{}' is loaded";
    pub const TASK_SCHEDULING_REQUIRES_CALLBACK_IPC: &str =
        "Task scheduling requires callback which cannot be serialized over IPC. Use module-side task management.";
    pub const TIMER_CANCELLATION_NOT_SUPPORTED_IPC: &str =
        "Timer cancellation not supported over IPC. Use module-side timer management.";
    pub const TIMER_REGISTRATION_REQUIRES_CALLBACK_IPC: &str =
        "Timer registration requires callback which cannot be serialized over IPC. Use module-side timer management.";

    // Registry / module fetch
    pub const BINARY_HASH_MUST_BE_32_BYTES: &str = "Binary hash must be 32 bytes";
    pub const EXPECTED_MODULE_MESSAGE: &str = "Expected Module message";
    pub const HASH_MUST_BE_32_BYTES: &str = "Hash must be 32 bytes";
    pub const IROH_TRANSPORT_NOT_SUPPORTED_MODULE_FETCHING: &str =
        "Iroh transport not yet supported for module fetching";
    pub const MANIFEST_HASH_MUST_BE_32_BYTES: &str = "Manifest hash must be 32 bytes";
    pub const MISSING_BINARY_HASH_FIELD: &str = "Missing 'binary_hash' field";
    pub const MISSING_HASH_FIELD: &str = "Missing 'hash' field";
    pub const MISSING_MANIFEST_FIELD: &str = "Missing 'manifest' field";
    pub const MISSING_MANIFEST_HASH_FIELD: &str = "Missing 'manifest_hash' field";
    pub const MISSING_NAME_FIELD: &str = "Missing 'name' field";
    pub const MISSING_VERSION_FIELD: &str = "Missing 'version' field";
    pub const NETWORK_MANAGER_NOT_SET_P2P_FETCHING: &str =
        "Network manager not set for P2P fetching";
    pub const REQUEST_ID_MISMATCH: &str = "Request ID mismatch";
    pub const RESPONSE_CHANNEL_CLOSED: &str = "Response channel closed";
}

// === API Response Types ===

/// Mempool size information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MempoolSize {
    /// Number of transactions in mempool
    pub transaction_count: usize,
    /// Total size in bytes
    pub size_bytes: usize,
    /// Total fee in sats
    pub total_fee_sats: u64,
}

/// Network statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkStats {
    /// Number of connected peers
    pub peer_count: usize,
    /// Network hash rate (hashes per second)
    pub hash_rate: f64,
    /// Bytes sent
    pub bytes_sent: u64,
    /// Bytes received
    pub bytes_received: u64,
}

/// Peer information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeerInfo {
    /// Peer address
    pub addr: String,
    /// Transport type
    pub transport_type: String,
    /// Services flags
    pub services: u64,
    /// Protocol version
    pub version: u32,
    /// Connected since (timestamp)
    pub connected_since: u64,
}

/// Chain information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainInfo {
    /// Current chain tip hash
    pub tip_hash: Hash,
    /// Current block height
    pub height: u64,
    /// Current difficulty
    pub difficulty: u32,
    /// Chain work
    pub chain_work: u64,
    /// Is synced
    pub is_synced: bool,
}

/// Bounded snapshot of block hashes on the full-block serve denylist.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockServeDenylistSnapshot {
    pub total_count: u64,
    /// True when `hashes` is shorter than `total_count` due to the snapshot cap.
    pub truncated: bool,
    pub hashes: Vec<Hash>,
}

/// Bounded snapshot of txids on the full-transaction serve denylist.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TxServeDenylistSnapshot {
    pub total_count: u64,
    pub truncated: bool,
    pub hashes: Vec<Hash>,
}

/// Sync coordinator view for modules (mirrors internal [`crate::node::sync::SyncState`] labels).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SyncStatus {
    pub phase: String,
    pub progress: f64,
    pub is_synced: bool,
    pub error_message: Option<String>,
}

/// Maximum hashes returned in one denylist snapshot over IPC (each hash is 32 bytes).
pub const SERVE_DENYLIST_SNAPSHOT_MAX_HASHES: usize = 4096;

/// Lightning node information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LightningInfo {
    /// Lightning node URL
    pub node_url: String,
    /// Node public key
    pub node_pubkey: Vec<u8>,
    /// Number of channels
    pub channel_count: usize,
    /// Total channel capacity (sats)
    pub total_capacity_sats: u64,
}

/// Payment state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentState {
    /// Payment ID
    pub payment_id: String,
    /// Payment status
    pub status: String, // "pending", "confirmed", "failed"
    /// Amount in sats
    pub amount_sats: u64,
    /// Transaction hash (if confirmed)
    pub tx_hash: Option<Hash>,
    /// Confirmations (if confirmed)
    pub confirmations: Option<u32>,
}

/// Result of block submission
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SubmitBlockResult {
    /// Block was accepted
    Accepted,
    /// Block was rejected with reason
    Rejected(String),
    /// Block was already submitted (duplicate)
    Duplicate,
}