@@ -1444,27 +1444,99 @@ impl StorageManager for DiskStorageManager {
14441444 }
14451445
14461446 async fn store_masternode_state ( & mut self , state : & MasternodeState ) -> StorageResult < ( ) > {
1447- let path = self . base_path . join ( "state/masternode.json" ) ;
1448- let json = serde_json:: to_string_pretty ( state) . map_err ( |e| {
1447+ // Store the main state info as JSON (without the large engine_state)
1448+ let json_path = self . base_path . join ( "state/masternode.json" ) ;
1449+ let engine_path = self . base_path . join ( "state/masternode_engine.bin" ) ;
1450+
1451+ // Create a version without the engine state for JSON storage
1452+ let json_state = serde_json:: json!( {
1453+ "last_height" : state. last_height,
1454+ "last_update" : state. last_update,
1455+ "terminal_block_hash" : state. terminal_block_hash,
1456+ "engine_state_size" : state. engine_state. len( )
1457+ } ) ;
1458+
1459+ let json = serde_json:: to_string_pretty ( & json_state) . map_err ( |e| {
14491460 StorageError :: Serialization ( format ! ( "Failed to serialize masternode state: {}" , e) )
14501461 } ) ?;
1451-
1452- tokio:: fs:: write ( path, json) . await ?;
1462+ tokio:: fs:: write ( json_path, json) . await ?;
1463+
1464+ // Store the engine state as binary
1465+ if !state. engine_state . is_empty ( ) {
1466+ tokio:: fs:: write ( engine_path, & state. engine_state ) . await ?;
1467+ }
1468+
14531469 Ok ( ( ) )
14541470 }
14551471
14561472 async fn load_masternode_state ( & self ) -> StorageResult < Option < MasternodeState > > {
1457- let path = self . base_path . join ( "state/masternode.json" ) ;
1458- if !path. exists ( ) {
1473+ let json_path = self . base_path . join ( "state/masternode.json" ) ;
1474+ let engine_path = self . base_path . join ( "state/masternode_engine.bin" ) ;
1475+
1476+ if !json_path. exists ( ) {
14591477 return Ok ( None ) ;
14601478 }
1461-
1462- let content = tokio:: fs:: read_to_string ( path) . await ?;
1463- let state = serde_json:: from_str ( & content) . map_err ( |e| {
1464- StorageError :: Serialization ( format ! ( "Failed to deserialize masternode state: {}" , e) )
1465- } ) ?;
1466-
1467- Ok ( Some ( state) )
1479+
1480+ // Try to read the file with size limit check
1481+ let metadata = tokio:: fs:: metadata ( & json_path) . await ?;
1482+ if metadata. len ( ) > 10_000_000 { // 10MB limit for JSON file
1483+ tracing:: error!( "Masternode state JSON file is too large: {} bytes. Likely corrupted." , metadata. len( ) ) ;
1484+ // Delete the corrupted file and return None to start fresh
1485+ let _ = tokio:: fs:: remove_file ( & json_path) . await ;
1486+ let _ = tokio:: fs:: remove_file ( & engine_path) . await ;
1487+ return Ok ( None ) ;
1488+ }
1489+
1490+ let content = tokio:: fs:: read_to_string ( & json_path) . await ?;
1491+
1492+ // First try to parse as the new format (without engine_state in JSON)
1493+ if let Ok ( json_state) = serde_json:: from_str :: < serde_json:: Value > ( & content) {
1494+ if !json_state. get ( "engine_state" ) . is_some ( ) {
1495+ // New format - load from separate files
1496+ let last_height = json_state[ "last_height" ] . as_u64 ( )
1497+ . ok_or_else ( || StorageError :: Serialization ( "Missing last_height" . to_string ( ) ) ) ? as u32 ;
1498+ let last_update = json_state[ "last_update" ] . as_u64 ( )
1499+ . ok_or_else ( || StorageError :: Serialization ( "Missing last_update" . to_string ( ) ) ) ?;
1500+ let terminal_block_hash = json_state[ "terminal_block_hash" ] . as_array ( )
1501+ . and_then ( |arr| {
1502+ if arr. len ( ) == 32 {
1503+ let mut hash = [ 0u8 ; 32 ] ;
1504+ for ( i, v) in arr. iter ( ) . enumerate ( ) {
1505+ hash[ i] = v. as_u64 ( ) ? as u8 ;
1506+ }
1507+ Some ( hash)
1508+ } else {
1509+ None
1510+ }
1511+ } ) ;
1512+
1513+ // Load the engine state binary if it exists
1514+ let engine_state = if engine_path. exists ( ) {
1515+ tokio:: fs:: read ( engine_path) . await ?
1516+ } else {
1517+ Vec :: new ( )
1518+ } ;
1519+
1520+ return Ok ( Some ( MasternodeState {
1521+ last_height,
1522+ engine_state,
1523+ last_update,
1524+ terminal_block_hash,
1525+ } ) ) ;
1526+ }
1527+ }
1528+
1529+ // Fall back to old format (with engine_state in JSON) - but with size protection
1530+ match serde_json:: from_str :: < MasternodeState > ( & content) {
1531+ Ok ( state) => Ok ( Some ( state) ) ,
1532+ Err ( e) => {
1533+ tracing:: error!( "Failed to deserialize masternode state: {}. Deleting corrupted file." , e) ;
1534+ // Delete the corrupted file
1535+ let _ = tokio:: fs:: remove_file ( & json_path) . await ;
1536+ let _ = tokio:: fs:: remove_file ( & engine_path) . await ;
1537+ Ok ( None )
1538+ }
1539+ }
14681540 }
14691541
14701542 async fn store_chain_state ( & mut self , state : & ChainState ) -> StorageResult < ( ) > {
0 commit comments