librats 1.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -1,14 +1,12 @@
1
- #include "storage.h"
2
- #include "crc32.h"
3
- #include "librats.h"
4
- #include "fs.h"
5
- #include "logger.h"
1
+ #include "librats/storage/storage.h"
2
+ #include "librats/node/node_context.h"
3
+ #include "librats/crypto/crc32.h"
4
+ #include "librats/util/fs.h"
5
+ #include "librats/util/logger.h"
6
6
  #include <algorithm>
7
7
  #include <cstring>
8
8
  #include <cstdlib>
9
- #include <fstream>
10
- #include <sstream>
11
- #include <iomanip>
9
+ #include <cstdio>
12
10
 
13
11
  // Define logging module for this file
14
12
  #define LOG_STORAGE_INFO(message) LOG_INFO("storage", message)
@@ -18,6 +16,14 @@
18
16
 
19
17
  namespace librats {
20
18
 
19
+ namespace {
20
+
21
+ void put_u32(std::vector<uint8_t>& b, uint32_t v) {
22
+ for (int i = 3; i >= 0; --i) b.push_back(static_cast<uint8_t>((v >> (i * 8)) & 0xFF));
23
+ }
24
+
25
+ } // namespace
26
+
21
27
  //=============================================================================
22
28
  // StorageEntry Implementation
23
29
  //=============================================================================
@@ -25,27 +31,27 @@ namespace librats {
25
31
  void StorageEntry::calculate_checksum() {
26
32
  // Calculate CRC32 over key + type + data + timestamp + peer_id
27
33
  std::vector<uint8_t> buffer;
28
-
34
+
29
35
  // Add key
30
36
  buffer.insert(buffer.end(), key.begin(), key.end());
31
-
37
+
32
38
  // Add type
33
39
  buffer.push_back(static_cast<uint8_t>(type));
34
-
40
+
35
41
  // Add data
36
42
  buffer.insert(buffer.end(), data.begin(), data.end());
37
-
43
+
38
44
  // Add timestamp (8 bytes, big endian)
39
45
  for (int i = 7; i >= 0; i--) {
40
46
  buffer.push_back(static_cast<uint8_t>((timestamp_ms >> (i * 8)) & 0xFF));
41
47
  }
42
-
48
+
43
49
  // Add peer_id
44
50
  buffer.insert(buffer.end(), origin_peer_id.begin(), origin_peer_id.end());
45
-
51
+
46
52
  // Add deleted flag
47
53
  buffer.push_back(deleted ? 1 : 0);
48
-
54
+
49
55
  checksum = storage_calculate_crc32(buffer.data(), buffer.size());
50
56
  }
51
57
 
@@ -57,7 +63,7 @@ bool StorageEntry::verify_checksum() const {
57
63
 
58
64
  std::vector<uint8_t> StorageEntry::serialize() const {
59
65
  std::vector<uint8_t> buffer;
60
-
66
+
61
67
  // Format:
62
68
  // [4 bytes] total_length (excluding this field)
63
69
  // [4 bytes] key_length
@@ -70,90 +76,90 @@ std::vector<uint8_t> StorageEntry::serialize() const {
70
76
  // [4 bytes] data_length
71
77
  // [data_length bytes] data
72
78
  // [4 bytes] checksum
73
-
79
+
74
80
  // Calculate total size first
75
81
  uint32_t key_len = static_cast<uint32_t>(key.size());
76
82
  uint32_t peer_id_len = static_cast<uint32_t>(origin_peer_id.size());
77
83
  uint32_t data_len = static_cast<uint32_t>(data.size());
78
84
  uint32_t total_len = 4 + key_len + 1 + 1 + 8 + 4 + peer_id_len + 4 + data_len + 4;
79
-
85
+
80
86
  buffer.reserve(4 + total_len);
81
-
87
+
82
88
  // Total length (big endian)
83
89
  buffer.push_back((total_len >> 24) & 0xFF);
84
90
  buffer.push_back((total_len >> 16) & 0xFF);
85
91
  buffer.push_back((total_len >> 8) & 0xFF);
86
92
  buffer.push_back(total_len & 0xFF);
87
-
93
+
88
94
  // Key length (big endian)
89
95
  buffer.push_back((key_len >> 24) & 0xFF);
90
96
  buffer.push_back((key_len >> 16) & 0xFF);
91
97
  buffer.push_back((key_len >> 8) & 0xFF);
92
98
  buffer.push_back(key_len & 0xFF);
93
-
99
+
94
100
  // Key
95
101
  buffer.insert(buffer.end(), key.begin(), key.end());
96
-
102
+
97
103
  // Type
98
104
  buffer.push_back(static_cast<uint8_t>(type));
99
-
105
+
100
106
  // Deleted flag
101
107
  buffer.push_back(deleted ? 1 : 0);
102
-
108
+
103
109
  // Timestamp (big endian)
104
110
  for (int i = 7; i >= 0; i--) {
105
111
  buffer.push_back(static_cast<uint8_t>((timestamp_ms >> (i * 8)) & 0xFF));
106
112
  }
107
-
113
+
108
114
  // Peer ID length (big endian)
109
115
  buffer.push_back((peer_id_len >> 24) & 0xFF);
110
116
  buffer.push_back((peer_id_len >> 16) & 0xFF);
111
117
  buffer.push_back((peer_id_len >> 8) & 0xFF);
112
118
  buffer.push_back(peer_id_len & 0xFF);
113
-
119
+
114
120
  // Peer ID
115
121
  buffer.insert(buffer.end(), origin_peer_id.begin(), origin_peer_id.end());
116
-
122
+
117
123
  // Data length (big endian)
118
124
  buffer.push_back((data_len >> 24) & 0xFF);
119
125
  buffer.push_back((data_len >> 16) & 0xFF);
120
126
  buffer.push_back((data_len >> 8) & 0xFF);
121
127
  buffer.push_back(data_len & 0xFF);
122
-
128
+
123
129
  // Data
124
130
  buffer.insert(buffer.end(), data.begin(), data.end());
125
-
131
+
126
132
  // Checksum (big endian)
127
133
  buffer.push_back((checksum >> 24) & 0xFF);
128
134
  buffer.push_back((checksum >> 16) & 0xFF);
129
135
  buffer.push_back((checksum >> 8) & 0xFF);
130
136
  buffer.push_back(checksum & 0xFF);
131
-
137
+
132
138
  return buffer;
133
139
  }
134
140
 
135
- bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset,
141
+ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset,
136
142
  StorageEntry& entry, size_t& bytes_read) {
137
143
  bytes_read = 0;
138
-
144
+
139
145
  // Minimum size check (4 bytes for total_length)
140
146
  if (offset + 4 > buffer.size()) {
141
147
  return false;
142
148
  }
143
-
149
+
144
150
  // Read total length
145
151
  uint32_t total_len = (static_cast<uint32_t>(buffer[offset]) << 24) |
146
152
  (static_cast<uint32_t>(buffer[offset + 1]) << 16) |
147
153
  (static_cast<uint32_t>(buffer[offset + 2]) << 8) |
148
154
  static_cast<uint32_t>(buffer[offset + 3]);
149
-
155
+
150
156
  // Check if we have enough data
151
157
  if (offset + 4 + total_len > buffer.size()) {
152
158
  return false;
153
159
  }
154
-
160
+
155
161
  size_t pos = offset + 4;
156
-
162
+
157
163
  // Read key length
158
164
  if (pos + 4 > buffer.size()) return false;
159
165
  uint32_t key_len = (static_cast<uint32_t>(buffer[pos]) << 24) |
@@ -161,22 +167,22 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
161
167
  (static_cast<uint32_t>(buffer[pos + 2]) << 8) |
162
168
  static_cast<uint32_t>(buffer[pos + 3]);
163
169
  pos += 4;
164
-
170
+
165
171
  // Read key
166
172
  if (pos + key_len > buffer.size()) return false;
167
173
  entry.key = std::string(buffer.begin() + pos, buffer.begin() + pos + key_len);
168
174
  pos += key_len;
169
-
175
+
170
176
  // Read type
171
177
  if (pos + 1 > buffer.size()) return false;
172
178
  entry.type = static_cast<StorageValueType>(buffer[pos]);
173
179
  pos += 1;
174
-
180
+
175
181
  // Read deleted flag
176
182
  if (pos + 1 > buffer.size()) return false;
177
183
  entry.deleted = buffer[pos] != 0;
178
184
  pos += 1;
179
-
185
+
180
186
  // Read timestamp
181
187
  if (pos + 8 > buffer.size()) return false;
182
188
  entry.timestamp_ms = 0;
@@ -184,7 +190,7 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
184
190
  entry.timestamp_ms = (entry.timestamp_ms << 8) | buffer[pos + i];
185
191
  }
186
192
  pos += 8;
187
-
193
+
188
194
  // Read peer ID length
189
195
  if (pos + 4 > buffer.size()) return false;
190
196
  uint32_t peer_id_len = (static_cast<uint32_t>(buffer[pos]) << 24) |
@@ -192,12 +198,12 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
192
198
  (static_cast<uint32_t>(buffer[pos + 2]) << 8) |
193
199
  static_cast<uint32_t>(buffer[pos + 3]);
194
200
  pos += 4;
195
-
201
+
196
202
  // Read peer ID
197
203
  if (pos + peer_id_len > buffer.size()) return false;
198
204
  entry.origin_peer_id = std::string(buffer.begin() + pos, buffer.begin() + pos + peer_id_len);
199
205
  pos += peer_id_len;
200
-
206
+
201
207
  // Read data length
202
208
  if (pos + 4 > buffer.size()) return false;
203
209
  uint32_t data_len = (static_cast<uint32_t>(buffer[pos]) << 24) |
@@ -205,12 +211,12 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
205
211
  (static_cast<uint32_t>(buffer[pos + 2]) << 8) |
206
212
  static_cast<uint32_t>(buffer[pos + 3]);
207
213
  pos += 4;
208
-
214
+
209
215
  // Read data
210
216
  if (pos + data_len > buffer.size()) return false;
211
217
  entry.data = std::vector<uint8_t>(buffer.begin() + pos, buffer.begin() + pos + data_len);
212
218
  pos += data_len;
213
-
219
+
214
220
  // Read checksum
215
221
  if (pos + 4 > buffer.size()) return false;
216
222
  entry.checksum = (static_cast<uint32_t>(buffer[pos]) << 24) |
@@ -218,7 +224,7 @@ bool StorageEntry::deserialize(const std::vector<uint8_t>& buffer, size_t offset
218
224
  (static_cast<uint32_t>(buffer[pos + 2]) << 8) |
219
225
  static_cast<uint32_t>(buffer[pos + 3]);
220
226
  pos += 4;
221
-
227
+
222
228
  bytes_read = pos - offset;
223
229
  return true;
224
230
  }
@@ -228,7 +234,7 @@ bool StorageEntry::wins_over(const StorageEntry& other) const {
228
234
  if (timestamp_ms != other.timestamp_ms) {
229
235
  return timestamp_ms > other.timestamp_ms;
230
236
  }
231
-
237
+
232
238
  // Tie-breaker: lexicographic comparison of peer IDs
233
239
  return origin_peer_id > other.origin_peer_id;
234
240
  }
@@ -261,18 +267,17 @@ StorageValueType string_to_storage_value_type(const std::string& str) {
261
267
  // StorageManager Implementation
262
268
  //=============================================================================
263
269
 
264
- StorageManager::StorageManager(RatsClient& client, const StorageConfig& config)
265
- : client_(client),
266
- config_(config),
270
+ StorageManager::StorageManager(const StorageConfig& config)
271
+ : config_(config),
267
272
  sync_status_(StorageSyncStatus::NOT_STARTED),
268
273
  initial_sync_complete_(false),
269
274
  running_(true),
270
275
  dirty_(false) {
271
-
276
+
272
277
  // Initialize statistics
273
278
  stats_ = StorageStatistics();
274
279
  stats_.sync_status = StorageSyncStatus::NOT_STARTED;
275
-
280
+
276
281
  initialize();
277
282
  }
278
283
 
@@ -281,81 +286,57 @@ StorageManager::~StorageManager() {
281
286
  }
282
287
 
283
288
  void StorageManager::initialize() {
284
- // Ensure data directory exists
289
+ // Ensure data directory exists and load any existing data from disk.
285
290
  if (config_.persist_to_disk) {
286
291
  create_directories(config_.data_directory.c_str());
287
- }
288
-
289
- // Load existing data from disk
290
- if (config_.persist_to_disk) {
291
292
  load();
292
- }
293
-
294
- // Subscribe to GossipSub topic for real-time updates
295
- if (config_.enable_sync && client_.is_gossipsub_available()) {
296
- client_.subscribe_to_topic(STORAGE_GOSSIP_TOPIC);
297
-
298
- // Set up message handler for storage updates
299
- client_.on_topic_message(STORAGE_GOSSIP_TOPIC,
300
- [this](const std::string& peer_id, const std::string& topic, const std::string& message) {
301
- handle_gossip_message(peer_id, topic, message);
302
- });
303
- }
304
-
305
- // Register message handlers for sync protocol
306
- if (config_.enable_sync) {
307
- client_.on(MSG_TYPE_SYNC_REQUEST, [this](const std::string& peer_id, const nlohmann::json& data) {
308
- handle_sync_request(peer_id, data);
309
- });
310
-
311
- client_.on(MSG_TYPE_SYNC_RESPONSE, [this](const std::string& peer_id, const nlohmann::json& data) {
312
- handle_sync_response(peer_id, data);
313
- });
314
- }
315
-
316
- // Start persistence thread
317
- if (config_.persist_to_disk) {
318
293
  persistence_thread_ = std::thread(&StorageManager::persistence_thread_loop, this);
319
294
  }
320
-
295
+
321
296
  LOG_STORAGE_INFO("StorageManager initialized with data directory: " << config_.data_directory);
322
297
  }
323
298
 
324
299
  void StorageManager::shutdown() {
300
+ // Idempotent: safe to call from both stop() and the destructor.
301
+ if (!running_.exchange(false)) return;
302
+
325
303
  LOG_STORAGE_INFO("StorageManager shutting down...");
326
-
327
- running_.store(false);
328
-
304
+
329
305
  // Wake up persistence thread
330
306
  {
331
307
  std::lock_guard<std::mutex> lock(persistence_mutex_);
332
308
  persistence_cv_.notify_all();
333
309
  }
334
-
310
+
335
311
  // Join persistence thread
336
312
  if (persistence_thread_.joinable()) {
337
313
  persistence_thread_.join();
338
314
  }
339
-
315
+
340
316
  // Final save
341
- if (config_.persist_to_disk && dirty_) {
317
+ bool needs_save;
318
+ {
319
+ std::lock_guard<std::mutex> lock(persistence_mutex_);
320
+ needs_save = dirty_;
321
+ }
322
+ if (config_.persist_to_disk && needs_save) {
342
323
  save();
343
324
  }
344
-
325
+
345
326
  LOG_STORAGE_INFO("StorageManager shut down");
346
327
  }
347
328
 
348
329
  void StorageManager::persistence_thread_loop() {
349
330
  const auto save_interval = std::chrono::seconds(5);
350
-
331
+
351
332
  while (running_.load()) {
352
333
  std::unique_lock<std::mutex> lock(persistence_mutex_);
353
- persistence_cv_.wait_for(lock, save_interval, [this] {
354
- return !running_.load() || dirty_;
334
+ persistence_cv_.wait_for(lock, save_interval, [this] {
335
+ return !running_.load() || dirty_;
355
336
  });
356
-
337
+
357
338
  if (!running_.load()) break;
358
-
339
+
359
340
  if (dirty_) {
360
341
  lock.unlock();
361
342
  save();
@@ -363,10 +344,34 @@ void StorageManager::persistence_thread_loop() {
363
344
  }
364
345
  }
365
346
 
347
+ //=============================================================================
348
+ // Subsystem
349
+ //=============================================================================
350
+
351
+ void StorageManager::attach(NodeContext& ctx) {
352
+ network_ = &ctx.network;
353
+
354
+ if (!config_.enable_sync) return;
355
+
356
+ network_->on(MessageType::Storage,
357
+ [this](const Peer& peer, ByteView payload) { on_storage_message(peer.id(), payload); });
358
+ network_->on_peer_connected(
359
+ [this](const Peer& peer) { on_peer_connected(peer.id()); });
360
+ }
361
+
362
+ void StorageManager::start() {
363
+ // The persistence thread is already running (started in the constructor) and
364
+ // sync is driven by peer events registered in attach(); nothing to spin up here.
365
+ }
366
+
367
+ void StorageManager::stop() {
368
+ shutdown();
369
+ }
370
+
366
371
  void StorageManager::set_config(const StorageConfig& config) {
367
372
  std::lock_guard<std::mutex> lock(storage_mutex_);
368
373
  config_ = config;
369
-
374
+
370
375
  if (config_.persist_to_disk) {
371
376
  create_directories(config_.data_directory.c_str());
372
377
  }
@@ -396,12 +401,12 @@ bool StorageManager::put(const std::string& key, const std::vector<uint8_t>& val
396
401
  return put_internal(key, StorageValueType::BINARY, value);
397
402
  }
398
403
 
399
- bool StorageManager::put_json(const std::string& key, const nlohmann::json& value) {
404
+ bool StorageManager::put_json(const std::string& key, const librats::Json& value) {
400
405
  std::string json_str = value.dump();
401
406
  return put_internal(key, StorageValueType::JSON, serialize_value(json_str));
402
407
  }
403
408
 
404
- bool StorageManager::put_internal(const std::string& key, StorageValueType type,
409
+ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
405
410
  const std::vector<uint8_t>& data,
406
411
  uint64_t timestamp_ms,
407
412
  const std::string& origin_peer_id,
@@ -410,22 +415,22 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
410
415
  LOG_STORAGE_ERROR("Cannot put with empty key");
411
416
  return false;
412
417
  }
413
-
418
+
414
419
  if (data.size() > config_.max_value_size) {
415
420
  LOG_STORAGE_ERROR("Value size " << data.size() << " exceeds maximum " << config_.max_value_size);
416
421
  return false;
417
422
  }
418
-
423
+
419
424
  // Use current time if not provided
420
425
  if (timestamp_ms == 0) {
421
426
  timestamp_ms = get_current_timestamp_ms();
422
427
  }
423
-
428
+
424
429
  // Use our peer ID if not provided
425
430
  std::string peer_id = origin_peer_id.empty() ? get_our_peer_id() : origin_peer_id;
426
-
431
+
427
432
  StorageEntry new_entry(key, type, data, timestamp_ms, peer_id);
428
-
433
+
429
434
  StorageChangeEvent event;
430
435
  event.operation = StorageOperation::OP_PUT;
431
436
  event.key = key;
@@ -434,10 +439,10 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
434
439
  event.timestamp_ms = timestamp_ms;
435
440
  event.origin_peer_id = peer_id;
436
441
  event.is_remote = !origin_peer_id.empty() && origin_peer_id != get_our_peer_id();
437
-
442
+
438
443
  {
439
444
  std::lock_guard<std::mutex> lock(storage_mutex_);
440
-
445
+
441
446
  auto it = entries_.find(key);
442
447
  if (it != entries_.end()) {
443
448
  // Check LWW - only update if new entry wins
@@ -445,24 +450,24 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
445
450
  LOG_STORAGE_DEBUG("Rejected put for key '" << key << "' - existing entry is newer");
446
451
  return false;
447
452
  }
448
-
453
+
449
454
  event.old_data = it->second.data;
450
455
  it->second = new_entry;
451
456
  } else {
452
457
  entries_[key] = new_entry;
453
458
  }
454
459
  }
455
-
460
+
456
461
  mark_dirty();
457
-
462
+
458
463
  // Broadcast to peers if this is a local change
459
464
  if (broadcast && config_.enable_sync) {
460
- broadcast_put(new_entry);
465
+ broadcast_entry(new_entry);
461
466
  }
462
-
467
+
463
468
  // Notify change callback
464
469
  notify_change(event);
465
-
470
+
466
471
  LOG_STORAGE_DEBUG("Put key '" << key << "' with type " << storage_value_type_to_string(type));
467
472
  return true;
468
473
  }
@@ -473,104 +478,93 @@ bool StorageManager::put_internal(const std::string& key, StorageValueType type,
473
478
 
474
479
  std::optional<std::string> StorageManager::get_string(const std::string& key) const {
475
480
  std::lock_guard<std::mutex> lock(storage_mutex_);
476
-
481
+
477
482
  auto it = entries_.find(key);
478
483
  if (it == entries_.end() || it->second.deleted) {
479
484
  return std::nullopt;
480
485
  }
481
-
486
+
482
487
  if (it->second.type != StorageValueType::STRING) {
483
488
  return std::nullopt;
484
489
  }
485
-
490
+
486
491
  return deserialize_string(it->second.data);
487
492
  }
488
493
 
489
494
  std::optional<int64_t> StorageManager::get_int(const std::string& key) const {
490
495
  std::lock_guard<std::mutex> lock(storage_mutex_);
491
-
496
+
492
497
  auto it = entries_.find(key);
493
498
  if (it == entries_.end() || it->second.deleted) {
494
499
  return std::nullopt;
495
500
  }
496
-
501
+
497
502
  if (it->second.type != StorageValueType::INT64) {
498
503
  return std::nullopt;
499
504
  }
500
-
505
+
501
506
  return deserialize_int64(it->second.data);
502
507
  }
503
508
 
504
509
  std::optional<double> StorageManager::get_double(const std::string& key) const {
505
510
  std::lock_guard<std::mutex> lock(storage_mutex_);
506
-
511
+
507
512
  auto it = entries_.find(key);
508
513
  if (it == entries_.end() || it->second.deleted) {
509
514
  return std::nullopt;
510
515
  }
511
-
516
+
512
517
  if (it->second.type != StorageValueType::DOUBLE) {
513
518
  return std::nullopt;
514
519
  }
515
-
520
+
516
521
  return deserialize_double(it->second.data);
517
522
  }
518
523
 
519
524
  std::optional<std::vector<uint8_t>> StorageManager::get_binary(const std::string& key) const {
520
525
  std::lock_guard<std::mutex> lock(storage_mutex_);
521
-
526
+
522
527
  auto it = entries_.find(key);
523
528
  if (it == entries_.end() || it->second.deleted) {
524
529
  return std::nullopt;
525
530
  }
526
-
531
+
527
532
  if (it->second.type != StorageValueType::BINARY) {
528
533
  return std::nullopt;
529
534
  }
530
-
535
+
531
536
  return it->second.data;
532
537
  }
533
538
 
534
- std::optional<nlohmann::json> StorageManager::get_json(const std::string& key) const {
539
+ std::optional<librats::Json> StorageManager::get_json(const std::string& key) const {
535
540
  std::lock_guard<std::mutex> lock(storage_mutex_);
536
-
541
+
537
542
  auto it = entries_.find(key);
538
543
  if (it == entries_.end() || it->second.deleted) {
539
544
  return std::nullopt;
540
545
  }
541
-
546
+
542
547
  if (it->second.type != StorageValueType::JSON) {
543
548
  return std::nullopt;
544
549
  }
545
-
550
+
546
551
  try {
547
552
  std::string json_str = deserialize_string(it->second.data);
548
- return nlohmann::json::parse(json_str);
553
+ return librats::Json::parse(json_str);
549
554
  } catch (const std::exception& e) {
550
555
  LOG_STORAGE_ERROR("Failed to parse JSON for key '" << key << "': " << e.what());
551
556
  return std::nullopt;
552
557
  }
553
558
  }
554
559
 
555
- const StorageEntry* StorageManager::get_entry(const std::string& key) const {
556
- std::lock_guard<std::mutex> lock(storage_mutex_);
557
-
558
- auto it = entries_.find(key);
559
- if (it == entries_.end() || it->second.deleted) {
560
- return nullptr;
561
- }
562
-
563
- return &it->second;
564
- }
565
-
566
560
  std::optional<StorageValueType> StorageManager::get_type(const std::string& key) const {
567
561
  std::lock_guard<std::mutex> lock(storage_mutex_);
568
-
562
+
569
563
  auto it = entries_.find(key);
570
564
  if (it == entries_.end() || it->second.deleted) {
571
565
  return std::nullopt;
572
566
  }
573
-
567
+
574
568
  return it->second.type;
575
569
  }
576
570
 
@@ -581,98 +575,100 @@ std::optional<StorageValueType> StorageManager::get_type(const std::string& key)
581
575
  bool StorageManager::remove(const std::string& key) {
582
576
  uint64_t timestamp_ms = get_current_timestamp_ms();
583
577
  std::string our_peer_id = get_our_peer_id();
584
-
578
+
585
579
  StorageChangeEvent event;
586
580
  event.operation = StorageOperation::OP_DELETE;
587
581
  event.key = key;
588
582
  event.timestamp_ms = timestamp_ms;
589
583
  event.origin_peer_id = our_peer_id;
590
584
  event.is_remote = false;
591
-
585
+
586
+ StorageEntry tombstone;
592
587
  {
593
588
  std::lock_guard<std::mutex> lock(storage_mutex_);
594
-
589
+
595
590
  auto it = entries_.find(key);
596
591
  if (it == entries_.end()) {
597
592
  return false;
598
593
  }
599
-
594
+
600
595
  if (it->second.deleted) {
601
596
  return false; // Already deleted
602
597
  }
603
-
598
+
604
599
  event.old_data = it->second.data;
605
-
600
+
606
601
  // Mark as deleted (tombstone)
607
602
  it->second.deleted = true;
608
603
  it->second.timestamp_ms = timestamp_ms;
609
604
  it->second.origin_peer_id = our_peer_id;
610
605
  it->second.data.clear();
611
606
  it->second.calculate_checksum();
607
+ tombstone = it->second;
612
608
  }
613
-
609
+
614
610
  mark_dirty();
615
-
616
- // Broadcast delete to peers
611
+
612
+ // Broadcast the tombstone to peers
617
613
  if (config_.enable_sync) {
618
- broadcast_delete(key, timestamp_ms);
614
+ broadcast_entry(tombstone);
619
615
  }
620
-
616
+
621
617
  // Notify change callback
622
618
  notify_change(event);
623
-
619
+
624
620
  LOG_STORAGE_DEBUG("Deleted key '" << key << "'");
625
621
  return true;
626
622
  }
627
623
 
628
624
  bool StorageManager::has(const std::string& key) const {
629
625
  std::lock_guard<std::mutex> lock(storage_mutex_);
630
-
626
+
631
627
  auto it = entries_.find(key);
632
628
  return it != entries_.end() && !it->second.deleted;
633
629
  }
634
630
 
635
631
  std::vector<std::string> StorageManager::keys() const {
636
632
  std::lock_guard<std::mutex> lock(storage_mutex_);
637
-
633
+
638
634
  std::vector<std::string> result;
639
635
  result.reserve(entries_.size());
640
-
636
+
641
637
  for (const auto& pair : entries_) {
642
638
  if (!pair.second.deleted) {
643
639
  result.push_back(pair.first);
644
640
  }
645
641
  }
646
-
642
+
647
643
  return result;
648
644
  }
649
645
 
650
646
  std::vector<std::string> StorageManager::keys_with_prefix(const std::string& prefix) const {
651
647
  std::lock_guard<std::mutex> lock(storage_mutex_);
652
-
648
+
653
649
  std::vector<std::string> result;
654
-
650
+
655
651
  for (const auto& pair : entries_) {
656
- if (!pair.second.deleted &&
652
+ if (!pair.second.deleted &&
657
653
  pair.first.size() >= prefix.size() &&
658
654
  pair.first.compare(0, prefix.size(), prefix) == 0) {
659
655
  result.push_back(pair.first);
660
656
  }
661
657
  }
662
-
658
+
663
659
  return result;
664
660
  }
665
661
 
666
662
  size_t StorageManager::size() const {
667
663
  std::lock_guard<std::mutex> lock(storage_mutex_);
668
-
664
+
669
665
  size_t count = 0;
670
666
  for (const auto& pair : entries_) {
671
667
  if (!pair.second.deleted) {
672
668
  count++;
673
669
  }
674
670
  }
675
-
671
+
676
672
  return count;
677
673
  }
678
674
 
@@ -681,24 +677,22 @@ bool StorageManager::empty() const {
681
677
  }
682
678
 
683
679
  void StorageManager::clear() {
684
- uint64_t timestamp_ms = get_current_timestamp_ms();
685
-
686
680
  std::vector<std::string> keys_to_delete;
687
-
681
+
688
682
  {
689
683
  std::lock_guard<std::mutex> lock(storage_mutex_);
690
-
684
+
691
685
  for (auto& pair : entries_) {
692
686
  if (!pair.second.deleted) {
693
687
  keys_to_delete.push_back(pair.first);
694
688
  }
695
689
  }
696
690
  }
697
-
691
+
698
692
  for (const auto& key : keys_to_delete) {
699
693
  remove(key);
700
694
  }
701
-
695
+
702
696
  LOG_STORAGE_INFO("Cleared all entries");
703
697
  }
704
698
 
@@ -710,16 +704,21 @@ bool StorageManager::save() {
710
704
  if (!config_.persist_to_disk) {
711
705
  return true;
712
706
  }
713
-
707
+
714
708
  std::lock_guard<std::mutex> lock(storage_mutex_);
715
-
709
+
716
710
  bool result = write_data_file();
717
-
711
+
718
712
  if (result) {
719
- dirty_ = false;
713
+ // dirty_ belongs to persistence_mutex_, not storage_mutex_ — the persistence
714
+ // thread reads it under that lock. Same order as load() (storage → persistence).
715
+ {
716
+ std::lock_guard<std::mutex> dirty_lock(persistence_mutex_);
717
+ dirty_ = false;
718
+ }
720
719
  LOG_STORAGE_DEBUG("Saved " << entries_.size() << " entries to disk");
721
720
  }
722
-
721
+
723
722
  return result;
724
723
  }
725
724
 
@@ -727,29 +726,29 @@ bool StorageManager::load() {
727
726
  if (!config_.persist_to_disk) {
728
727
  return true;
729
728
  }
730
-
729
+
731
730
  std::lock_guard<std::mutex> lock(storage_mutex_);
732
-
731
+
733
732
  std::string data_path = get_data_file_path();
734
733
  if (!file_exists(data_path.c_str())) {
735
734
  LOG_STORAGE_DEBUG("No existing data file found at " << data_path);
736
735
  return true; // Not an error, just no data yet
737
736
  }
738
-
737
+
739
738
  bool result = read_data_file();
740
-
739
+
741
740
  if (result) {
742
741
  LOG_STORAGE_INFO("Loaded " << entries_.size() << " entries from disk");
743
742
  }
744
-
743
+
745
744
  return result;
746
745
  }
747
746
 
748
747
  size_t StorageManager::compact() {
749
748
  std::lock_guard<std::mutex> lock(storage_mutex_);
750
-
749
+
751
750
  size_t removed = 0;
752
-
751
+
753
752
  for (auto it = entries_.begin(); it != entries_.end();) {
754
753
  if (it->second.deleted) {
755
754
  it = entries_.erase(it);
@@ -758,12 +757,12 @@ size_t StorageManager::compact() {
758
757
  ++it;
759
758
  }
760
759
  }
761
-
760
+
762
761
  if (removed > 0) {
763
762
  mark_dirty();
764
763
  LOG_STORAGE_INFO("Compacted storage, removed " << removed << " tombstones");
765
764
  }
766
-
765
+
767
766
  return removed;
768
767
  }
769
768
 
@@ -772,29 +771,27 @@ size_t StorageManager::compact() {
772
771
  //=============================================================================
773
772
 
774
773
  bool StorageManager::request_sync() {
775
- if (!config_.enable_sync) {
774
+ if (!config_.enable_sync || !network_) {
776
775
  return false;
777
776
  }
778
-
777
+
779
778
  // Get a connected peer to sync from
780
- auto peers = client_.get_validated_peers();
779
+ auto peers = network_->connected_peers();
781
780
  if (peers.empty()) {
782
781
  LOG_STORAGE_WARN("No peers available for sync");
783
782
  return false;
784
783
  }
785
-
786
- // Pick first available peer
787
- std::string peer_id = peers[0].peer_id;
788
-
784
+
785
+ const PeerId& peer_id = peers[0];
786
+
789
787
  {
790
788
  std::lock_guard<std::mutex> lock(sync_mutex_);
791
789
  sync_status_ = StorageSyncStatus::IN_PROGRESS;
792
- sync_peer_id_ = peer_id;
793
790
  }
794
-
791
+
795
792
  send_sync_request(peer_id);
796
-
797
- LOG_STORAGE_INFO("Requested sync from peer " << peer_id);
793
+
794
+ LOG_STORAGE_INFO("Requested sync from peer " << peer_id.short_hex());
798
795
  return true;
799
796
  }
800
797
 
@@ -826,17 +823,17 @@ void StorageManager::set_sync_complete_callback(StorageSyncCompleteCallback call
826
823
 
827
824
  StorageStatistics StorageManager::get_statistics() const {
828
825
  std::lock_guard<std::mutex> lock(stats_mutex_);
829
-
826
+
830
827
  StorageStatistics result = stats_;
831
-
828
+
832
829
  // Calculate current counts
833
830
  {
834
831
  std::lock_guard<std::mutex> storage_lock(storage_mutex_);
835
-
832
+
836
833
  result.total_entries = 0;
837
834
  result.deleted_entries = 0;
838
835
  result.total_data_bytes = 0;
839
-
836
+
840
837
  for (const auto& pair : entries_) {
841
838
  if (pair.second.deleted) {
842
839
  result.deleted_entries++;
@@ -846,20 +843,20 @@ StorageStatistics StorageManager::get_statistics() const {
846
843
  }
847
844
  }
848
845
  }
849
-
846
+
850
847
  {
851
848
  std::lock_guard<std::mutex> sync_lock(sync_mutex_);
852
849
  result.sync_status = sync_status_;
853
850
  result.last_sync_time = last_sync_time_;
854
851
  }
855
-
852
+
856
853
  return result;
857
854
  }
858
855
 
859
- nlohmann::json StorageManager::get_statistics_json() const {
856
+ librats::Json StorageManager::get_statistics_json() const {
860
857
  StorageStatistics stats = get_statistics();
861
-
862
- nlohmann::json result;
858
+
859
+ librats::Json result;
863
860
  result["total_entries"] = stats.total_entries;
864
861
  result["deleted_entries"] = stats.deleted_entries;
865
862
  result["total_data_bytes"] = stats.total_data_bytes;
@@ -868,231 +865,110 @@ nlohmann::json StorageManager::get_statistics_json() const {
868
865
  result["entries_sent"] = stats.entries_sent;
869
866
  result["sync_requests_received"] = stats.sync_requests_received;
870
867
  result["sync_requests_sent"] = stats.sync_requests_sent;
871
-
868
+
872
869
  switch (stats.sync_status) {
873
870
  case StorageSyncStatus::NOT_STARTED: result["sync_status"] = "not_started"; break;
874
871
  case StorageSyncStatus::IN_PROGRESS: result["sync_status"] = "in_progress"; break;
875
872
  case StorageSyncStatus::COMPLETED: result["sync_status"] = "completed"; break;
876
873
  case StorageSyncStatus::FAILED: result["sync_status"] = "failed"; break;
877
874
  }
878
-
875
+
879
876
  return result;
880
877
  }
881
878
 
882
879
  //=============================================================================
883
- // Network Message Handlers
880
+ // Network Message Handlers (run on a reactor thread)
884
881
  //=============================================================================
885
882
 
886
- void StorageManager::handle_gossip_message(const std::string& peer_id, const std::string& topic,
887
- const std::string& message) {
888
- if (topic != STORAGE_GOSSIP_TOPIC) {
889
- return;
890
- }
891
-
892
- try {
893
- nlohmann::json msg = nlohmann::json::parse(message);
894
-
895
- std::string msg_type = msg.value("type", "");
896
-
897
- if (msg_type == "put") {
898
- // Decode entry from message
899
- std::string key = msg["key"];
900
- std::string type_str = msg["value_type"];
901
- std::string data_base64 = msg["data"];
902
- uint64_t timestamp_ms = msg["timestamp"];
903
- std::string origin_peer = msg["origin_peer"];
904
-
905
- // Decode base64 data
906
- std::vector<uint8_t> data;
907
- // Simple base64 decode (we'll implement inline)
908
- static const std::string base64_chars =
909
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
910
-
911
- std::vector<int> T(256, -1);
912
- for (int i = 0; i < 64; i++) T[static_cast<unsigned char>(base64_chars[i])] = i;
913
-
914
- int val = 0, valb = -8;
915
- for (unsigned char c : data_base64) {
916
- if (T[c] == -1) break;
917
- val = (val << 6) + T[c];
918
- valb += 6;
919
- if (valb >= 0) {
920
- data.push_back((val >> valb) & 0xFF);
921
- valb -= 8;
922
- }
923
- }
924
-
925
- StorageValueType value_type = string_to_storage_value_type(type_str);
926
-
927
- // Apply with LWW resolution
928
- put_internal(key, value_type, data, timestamp_ms, origin_peer, false);
929
-
930
- {
931
- std::lock_guard<std::mutex> lock(stats_mutex_);
932
- stats_.entries_synced++;
933
- }
934
-
935
- } else if (msg_type == "delete") {
936
- std::string key = msg["key"];
937
- uint64_t timestamp_ms = msg["timestamp"];
938
- std::string origin_peer = msg["origin_peer"];
939
-
940
- // Apply delete with LWW
941
- {
942
- std::lock_guard<std::mutex> lock(storage_mutex_);
943
-
944
- auto it = entries_.find(key);
945
- if (it != entries_.end()) {
946
- // Check if this delete is newer
947
- if (timestamp_ms > it->second.timestamp_ms ||
948
- (timestamp_ms == it->second.timestamp_ms && origin_peer > it->second.origin_peer_id)) {
949
- it->second.deleted = true;
950
- it->second.timestamp_ms = timestamp_ms;
951
- it->second.origin_peer_id = origin_peer;
952
- it->second.data.clear();
953
- it->second.calculate_checksum();
954
- mark_dirty();
955
- }
956
- }
957
- }
958
-
883
+ void StorageManager::on_storage_message(const PeerId& from, ByteView payload) {
884
+ if (payload.empty()) return;
885
+
886
+ const uint8_t* p = payload.data();
887
+ const size_t n = payload.size();
888
+ const uint8_t op = p[0];
889
+
890
+ if (op == OP_ENTRY) {
891
+ // Deserialize a single entry from the payload (after the opcode byte).
892
+ std::vector<uint8_t> buf(p + 1, p + n);
893
+ StorageEntry entry;
894
+ size_t bytes_read = 0;
895
+ if (!StorageEntry::deserialize(buf, 0, entry, bytes_read)) {
896
+ LOG_STORAGE_WARN("Malformed storage entry from " << from.short_hex());
897
+ return;
898
+ }
899
+ if (!entry.verify_checksum()) {
900
+ LOG_STORAGE_WARN("Checksum mismatch on entry '" << entry.key << "' from " << from.short_hex());
901
+ return;
902
+ }
903
+
904
+ StorageChangeEvent event;
905
+ if (apply_remote_entry(entry, &event)) {
959
906
  {
960
907
  std::lock_guard<std::mutex> lock(stats_mutex_);
961
908
  stats_.entries_synced++;
962
909
  }
910
+ mark_dirty();
911
+ notify_change(event);
912
+ // Re-flood to other peers; LWW makes a duplicate lose, so this stops.
913
+ forward_entry(entry, from);
963
914
  }
964
-
965
- } catch (const std::exception& e) {
966
- LOG_STORAGE_ERROR("Failed to handle gossip message: " << e.what());
967
- }
968
- }
969
-
970
- void StorageManager::handle_sync_request(const std::string& peer_id, const nlohmann::json& data) {
971
- LOG_STORAGE_INFO("Received sync request from peer " << peer_id);
972
-
973
- {
974
- std::lock_guard<std::mutex> lock(stats_mutex_);
975
- stats_.sync_requests_received++;
976
- }
977
-
978
- // Send full database snapshot
979
- send_sync_response(peer_id);
980
- }
981
-
982
- void StorageManager::handle_sync_response(const std::string& peer_id, const nlohmann::json& data) {
983
- LOG_STORAGE_INFO("Received sync response from peer " << peer_id);
984
-
985
- try {
986
- if (!data.contains("entries") || !data["entries"].is_array()) {
987
- LOG_STORAGE_ERROR("Invalid sync response format");
988
-
989
- {
990
- std::lock_guard<std::mutex> lock(sync_mutex_);
991
- sync_status_ = StorageSyncStatus::FAILED;
992
- }
993
-
994
- if (sync_complete_callback_) {
995
- sync_complete_callback_(false, "Invalid sync response format");
996
- }
997
- return;
915
+ } else if (op == OP_SYNC_REQUEST) {
916
+ {
917
+ std::lock_guard<std::mutex> lock(stats_mutex_);
918
+ stats_.sync_requests_received++;
998
919
  }
999
-
920
+ send_sync_response(from);
921
+ } else if (op == OP_SYNC_RESPONSE) {
922
+ // [3][count:u32][entry]*
923
+ if (n < 5) return;
924
+ uint32_t count = (static_cast<uint32_t>(p[1]) << 24) |
925
+ (static_cast<uint32_t>(p[2]) << 16) |
926
+ (static_cast<uint32_t>(p[3]) << 8) |
927
+ static_cast<uint32_t>(p[4]);
928
+ std::vector<uint8_t> buf(p + 5, p + n);
929
+ size_t offset = 0;
1000
930
  int applied = 0;
1001
-
1002
- for (const auto& entry_json : data["entries"]) {
1003
- std::string key = entry_json["key"];
1004
- std::string type_str = entry_json["value_type"];
1005
- std::string data_base64 = entry_json["data"];
1006
- uint64_t timestamp_ms = entry_json["timestamp"];
1007
- std::string origin_peer = entry_json["origin_peer"];
1008
- bool deleted = entry_json.value("deleted", false);
1009
-
1010
- // Decode base64 data
1011
- std::vector<uint8_t> decoded_data;
1012
- static const std::string base64_chars =
1013
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1014
-
1015
- std::vector<int> T(256, -1);
1016
- for (int i = 0; i < 64; i++) T[static_cast<unsigned char>(base64_chars[i])] = i;
1017
-
1018
- int val = 0, valb = -8;
1019
- for (unsigned char c : data_base64) {
1020
- if (T[c] == -1) break;
1021
- val = (val << 6) + T[c];
1022
- valb += 6;
1023
- if (valb >= 0) {
1024
- decoded_data.push_back((val >> valb) & 0xFF);
1025
- valb -= 8;
1026
- }
1027
- }
1028
-
1029
- StorageValueType value_type = string_to_storage_value_type(type_str);
1030
-
1031
- // Create entry
931
+ for (uint32_t i = 0; i < count && offset < buf.size(); i++) {
1032
932
  StorageEntry entry;
1033
- entry.key = key;
1034
- entry.type = value_type;
1035
- entry.data = decoded_data;
1036
- entry.timestamp_ms = timestamp_ms;
1037
- entry.origin_peer_id = origin_peer;
1038
- entry.deleted = deleted;
1039
- entry.calculate_checksum();
1040
-
1041
- if (apply_remote_entry(entry)) {
933
+ size_t bytes_read = 0;
934
+ if (!StorageEntry::deserialize(buf, offset, entry, bytes_read)) break;
935
+ offset += bytes_read;
936
+ if (!entry.verify_checksum()) continue;
937
+
938
+ StorageChangeEvent event;
939
+ if (apply_remote_entry(entry, &event)) {
1042
940
  applied++;
941
+ notify_change(event);
1043
942
  }
1044
943
  }
1045
-
944
+
1046
945
  {
1047
946
  std::lock_guard<std::mutex> lock(sync_mutex_);
1048
947
  sync_status_ = StorageSyncStatus::COMPLETED;
1049
948
  initial_sync_complete_ = true;
1050
949
  last_sync_time_ = std::chrono::steady_clock::now();
1051
950
  }
1052
-
1053
951
  {
1054
952
  std::lock_guard<std::mutex> lock(stats_mutex_);
1055
953
  stats_.entries_synced += applied;
1056
954
  }
1057
-
1058
- mark_dirty();
1059
-
1060
- LOG_STORAGE_INFO("Sync completed, applied " << applied << " entries from peer " << peer_id);
1061
-
1062
- if (sync_complete_callback_) {
1063
- sync_complete_callback_(true, "");
1064
- }
1065
-
1066
- } catch (const std::exception& e) {
1067
- LOG_STORAGE_ERROR("Failed to handle sync response: " << e.what());
1068
-
1069
- {
1070
- std::lock_guard<std::mutex> lock(sync_mutex_);
1071
- sync_status_ = StorageSyncStatus::FAILED;
1072
- }
1073
-
1074
- if (sync_complete_callback_) {
1075
- sync_complete_callback_(false, e.what());
1076
- }
955
+ if (applied > 0) mark_dirty();
956
+
957
+ LOG_STORAGE_INFO("Sync from " << from.short_hex() << " applied " << applied << " entries");
958
+ if (sync_complete_callback_) sync_complete_callback_(true, "");
1077
959
  }
1078
960
  }
1079
961
 
1080
- void StorageManager::on_peer_connected(const std::string& peer_id) {
1081
- if (!config_.enable_sync) {
1082
- return;
1083
- }
1084
-
1085
- // If we haven't synced yet, request sync from this peer
962
+ void StorageManager::on_peer_connected(const PeerId& peer_id) {
963
+ if (!config_.enable_sync) return;
964
+
965
+ // Anti-entropy: ask the new peer for a full snapshot. Both ends do this on
966
+ // connect, so the two databases converge via LWW.
1086
967
  {
1087
968
  std::lock_guard<std::mutex> lock(sync_mutex_);
1088
- if (!initial_sync_complete_ && sync_status_ != StorageSyncStatus::IN_PROGRESS) {
969
+ if (sync_status_ == StorageSyncStatus::NOT_STARTED)
1089
970
  sync_status_ = StorageSyncStatus::IN_PROGRESS;
1090
- sync_peer_id_ = peer_id;
1091
- } else {
1092
- return; // Already synced or in progress
1093
- }
1094
971
  }
1095
-
1096
972
  send_sync_request(peer_id);
1097
973
  }
1098
974
 
@@ -1124,7 +1000,7 @@ std::vector<uint8_t> StorageManager::serialize_value(const std::string& value) c
1124
1000
 
1125
1001
  int64_t StorageManager::deserialize_int64(const std::vector<uint8_t>& data) const {
1126
1002
  if (data.size() < 8) return 0;
1127
-
1003
+
1128
1004
  int64_t value = 0;
1129
1005
  for (int i = 0; i < 8; i++) {
1130
1006
  value = (value << 8) | data[i];
@@ -1134,12 +1010,12 @@ int64_t StorageManager::deserialize_int64(const std::vector<uint8_t>& data) cons
1134
1010
 
1135
1011
  double StorageManager::deserialize_double(const std::vector<uint8_t>& data) const {
1136
1012
  if (data.size() < 8) return 0.0;
1137
-
1013
+
1138
1014
  uint64_t bits = 0;
1139
1015
  for (int i = 0; i < 8; i++) {
1140
1016
  bits = (bits << 8) | data[i];
1141
1017
  }
1142
-
1018
+
1143
1019
  double value;
1144
1020
  std::memcpy(&value, &bits, sizeof(double));
1145
1021
  return value;
@@ -1153,133 +1029,103 @@ std::string StorageManager::deserialize_string(const std::vector<uint8_t>& data)
1153
1029
  // Private Methods - Network Operations
1154
1030
  //=============================================================================
1155
1031
 
1156
- void StorageManager::broadcast_put(const StorageEntry& entry) {
1157
- if (!client_.is_gossipsub_available()) {
1158
- return;
1159
- }
1160
-
1161
- // Base64 encode data
1162
- static const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1163
- std::string data_base64;
1164
-
1165
- int val = 0, valb = -6;
1166
- for (uint8_t c : entry.data) {
1167
- val = (val << 8) + c;
1168
- valb += 8;
1169
- while (valb >= 0) {
1170
- data_base64.push_back(base64_chars[(val >> valb) & 0x3F]);
1171
- valb -= 6;
1172
- }
1173
- }
1174
- if (valb > -6) data_base64.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
1175
- while (data_base64.size() % 4) data_base64.push_back('=');
1176
-
1177
- nlohmann::json msg;
1178
- msg["type"] = "put";
1179
- msg["key"] = entry.key;
1180
- msg["value_type"] = storage_value_type_to_string(entry.type);
1181
- msg["data"] = data_base64;
1182
- msg["timestamp"] = entry.timestamp_ms;
1183
- msg["origin_peer"] = entry.origin_peer_id;
1184
-
1185
- client_.publish_to_topic(STORAGE_GOSSIP_TOPIC, msg.dump());
1186
-
1032
+ void StorageManager::broadcast_entry(const StorageEntry& entry) {
1033
+ if (!network_) return;
1034
+
1035
+ std::vector<uint8_t> msg;
1036
+ msg.push_back(OP_ENTRY);
1037
+ std::vector<uint8_t> serialized = entry.serialize();
1038
+ msg.insert(msg.end(), serialized.begin(), serialized.end());
1039
+
1040
+ network_->broadcast(MessageType::Storage, ByteView(msg));
1041
+
1187
1042
  {
1188
1043
  std::lock_guard<std::mutex> lock(stats_mutex_);
1189
1044
  stats_.entries_sent++;
1190
1045
  }
1191
1046
  }
1192
1047
 
1193
- void StorageManager::broadcast_delete(const std::string& key, uint64_t timestamp_ms) {
1194
- if (!client_.is_gossipsub_available()) {
1195
- return;
1196
- }
1197
-
1198
- nlohmann::json msg;
1199
- msg["type"] = "delete";
1200
- msg["key"] = key;
1201
- msg["timestamp"] = timestamp_ms;
1202
- msg["origin_peer"] = get_our_peer_id();
1203
-
1204
- client_.publish_to_topic(STORAGE_GOSSIP_TOPIC, msg.dump());
1205
-
1206
- {
1207
- std::lock_guard<std::mutex> lock(stats_mutex_);
1208
- stats_.entries_sent++;
1048
+ void StorageManager::forward_entry(const StorageEntry& entry, const PeerId& except) {
1049
+ if (!network_) return;
1050
+
1051
+ std::vector<uint8_t> msg;
1052
+ msg.push_back(OP_ENTRY);
1053
+ std::vector<uint8_t> serialized = entry.serialize();
1054
+ msg.insert(msg.end(), serialized.begin(), serialized.end());
1055
+ ByteView view(msg);
1056
+
1057
+ for (const PeerId& peer : network_->connected_peers()) {
1058
+ if (peer == except) continue;
1059
+ network_->send(peer, MessageType::Storage, view);
1209
1060
  }
1210
1061
  }
1211
1062
 
1212
- void StorageManager::send_sync_request(const std::string& peer_id) {
1213
- nlohmann::json request;
1214
- request["request_id"] = get_current_timestamp_ms();
1215
-
1216
- client_.send(peer_id, MSG_TYPE_SYNC_REQUEST, request);
1217
-
1063
+ void StorageManager::send_sync_request(const PeerId& peer_id) {
1064
+ if (!network_) return;
1065
+
1066
+ std::vector<uint8_t> msg{OP_SYNC_REQUEST};
1067
+ network_->send(peer_id, MessageType::Storage, ByteView(msg));
1068
+
1218
1069
  {
1219
1070
  std::lock_guard<std::mutex> lock(stats_mutex_);
1220
1071
  stats_.sync_requests_sent++;
1221
1072
  }
1222
-
1223
- LOG_STORAGE_DEBUG("Sent sync request to peer " << peer_id);
1073
+
1074
+ LOG_STORAGE_DEBUG("Sent sync request to peer " << peer_id.short_hex());
1224
1075
  }
1225
1076
 
1226
- void StorageManager::send_sync_response(const std::string& peer_id) {
1227
- nlohmann::json response;
1228
- nlohmann::json entries_json = nlohmann::json::array();
1229
-
1077
+ void StorageManager::send_sync_response(const PeerId& peer_id) {
1078
+ if (!network_) return;
1079
+
1080
+ std::vector<uint8_t> msg;
1081
+ msg.push_back(OP_SYNC_RESPONSE);
1082
+
1083
+ uint32_t count = 0;
1084
+ std::vector<uint8_t> entries_blob;
1230
1085
  {
1231
1086
  std::lock_guard<std::mutex> lock(storage_mutex_);
1232
-
1233
1087
  for (const auto& pair : entries_) {
1234
- const StorageEntry& entry = pair.second;
1235
-
1236
- // Base64 encode data
1237
- static const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1238
- std::string data_base64;
1239
-
1240
- int val = 0, valb = -6;
1241
- for (uint8_t c : entry.data) {
1242
- val = (val << 8) + c;
1243
- valb += 8;
1244
- while (valb >= 0) {
1245
- data_base64.push_back(base64_chars[(val >> valb) & 0x3F]);
1246
- valb -= 6;
1247
- }
1248
- }
1249
- if (valb > -6) data_base64.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
1250
- while (data_base64.size() % 4) data_base64.push_back('=');
1251
-
1252
- nlohmann::json entry_json;
1253
- entry_json["key"] = entry.key;
1254
- entry_json["value_type"] = storage_value_type_to_string(entry.type);
1255
- entry_json["data"] = data_base64;
1256
- entry_json["timestamp"] = entry.timestamp_ms;
1257
- entry_json["origin_peer"] = entry.origin_peer_id;
1258
- entry_json["deleted"] = entry.deleted;
1259
-
1260
- entries_json.push_back(entry_json);
1088
+ std::vector<uint8_t> serialized = pair.second.serialize();
1089
+ entries_blob.insert(entries_blob.end(), serialized.begin(), serialized.end());
1090
+ count++;
1261
1091
  }
1262
1092
  }
1263
-
1264
- response["entries"] = entries_json;
1265
-
1266
- client_.send(peer_id, MSG_TYPE_SYNC_RESPONSE, response);
1267
-
1268
- LOG_STORAGE_DEBUG("Sent sync response to peer " << peer_id << " with " << entries_json.size() << " entries");
1093
+
1094
+ put_u32(msg, count);
1095
+ msg.insert(msg.end(), entries_blob.begin(), entries_blob.end());
1096
+
1097
+ network_->send(peer_id, MessageType::Storage, ByteView(msg));
1098
+
1099
+ LOG_STORAGE_DEBUG("Sent sync response to peer " << peer_id.short_hex() << " with " << count << " entries");
1269
1100
  }
1270
1101
 
1271
- bool StorageManager::apply_remote_entry(const StorageEntry& entry) {
1272
- std::lock_guard<std::mutex> lock(storage_mutex_);
1273
-
1274
- auto it = entries_.find(entry.key);
1275
- if (it != entries_.end()) {
1276
- // Check LWW
1277
- if (!entry.wins_over(it->second)) {
1278
- return false; // Our entry is newer, don't apply
1102
+ bool StorageManager::apply_remote_entry(const StorageEntry& entry, StorageChangeEvent* out_event) {
1103
+ StorageChangeEvent event;
1104
+ event.operation = entry.deleted ? StorageOperation::OP_DELETE : StorageOperation::OP_PUT;
1105
+ event.key = entry.key;
1106
+ event.type = entry.type;
1107
+ event.new_data = entry.data;
1108
+ event.timestamp_ms = entry.timestamp_ms;
1109
+ event.origin_peer_id = entry.origin_peer_id;
1110
+ event.is_remote = true;
1111
+
1112
+ {
1113
+ std::lock_guard<std::mutex> lock(storage_mutex_);
1114
+
1115
+ auto it = entries_.find(entry.key);
1116
+ if (it != entries_.end()) {
1117
+ // Check LWW
1118
+ if (!entry.wins_over(it->second)) {
1119
+ return false; // Our entry is newer or identical, don't apply
1120
+ }
1121
+ event.old_data = it->second.data;
1122
+ it->second = entry;
1123
+ } else {
1124
+ entries_[entry.key] = entry;
1279
1125
  }
1280
1126
  }
1281
-
1282
- entries_[entry.key] = entry;
1127
+
1128
+ if (out_event) *out_event = event;
1283
1129
  return true;
1284
1130
  }
1285
1131
 
@@ -1298,14 +1144,14 @@ std::string StorageManager::get_index_file_path() const {
1298
1144
  bool StorageManager::write_data_file() {
1299
1145
  std::string data_path = get_data_file_path();
1300
1146
  std::string temp_path = data_path + ".tmp";
1301
-
1147
+
1302
1148
  try {
1303
1149
  FILE* file = fopen(temp_path.c_str(), "wb");
1304
1150
  if (!file) {
1305
1151
  LOG_STORAGE_ERROR("Failed to open temp file for writing: " << temp_path);
1306
1152
  return false;
1307
1153
  }
1308
-
1154
+
1309
1155
  // Write file header
1310
1156
  // Magic: "RATS" (4 bytes)
1311
1157
  // Version: 1 (4 bytes)
@@ -1313,9 +1159,9 @@ bool StorageManager::write_data_file() {
1313
1159
  const char* magic = "RATS";
1314
1160
  uint32_t version = 1;
1315
1161
  uint32_t entry_count = static_cast<uint32_t>(entries_.size());
1316
-
1162
+
1317
1163
  fwrite(magic, 1, 4, file);
1318
-
1164
+
1319
1165
  uint8_t version_bytes[4] = {
1320
1166
  static_cast<uint8_t>((version >> 24) & 0xFF),
1321
1167
  static_cast<uint8_t>((version >> 16) & 0xFF),
@@ -1323,7 +1169,7 @@ bool StorageManager::write_data_file() {
1323
1169
  static_cast<uint8_t>(version & 0xFF)
1324
1170
  };
1325
1171
  fwrite(version_bytes, 1, 4, file);
1326
-
1172
+
1327
1173
  uint8_t count_bytes[4] = {
1328
1174
  static_cast<uint8_t>((entry_count >> 24) & 0xFF),
1329
1175
  static_cast<uint8_t>((entry_count >> 16) & 0xFF),
@@ -1331,29 +1177,29 @@ bool StorageManager::write_data_file() {
1331
1177
  static_cast<uint8_t>(entry_count & 0xFF)
1332
1178
  };
1333
1179
  fwrite(count_bytes, 1, 4, file);
1334
-
1180
+
1335
1181
  // Write each entry
1336
1182
  for (const auto& pair : entries_) {
1337
1183
  std::vector<uint8_t> serialized = pair.second.serialize();
1338
1184
  fwrite(serialized.data(), 1, serialized.size(), file);
1339
1185
  }
1340
-
1186
+
1341
1187
  fclose(file);
1342
-
1188
+
1343
1189
  // On Windows, rename fails if destination exists, so delete it first
1344
1190
  if (file_exists(data_path.c_str())) {
1345
1191
  delete_file(data_path.c_str());
1346
1192
  }
1347
-
1193
+
1348
1194
  // Atomically rename temp file to final
1349
1195
  if (!rename_file(temp_path.c_str(), data_path.c_str())) {
1350
1196
  LOG_STORAGE_ERROR("Failed to rename temp file to final: " << temp_path << " -> " << data_path);
1351
1197
  delete_file(temp_path.c_str());
1352
1198
  return false;
1353
1199
  }
1354
-
1200
+
1355
1201
  return true;
1356
-
1202
+
1357
1203
  } catch (const std::exception& e) {
1358
1204
  LOG_STORAGE_ERROR("Exception while writing data file: " << e.what());
1359
1205
  delete_file(temp_path.c_str());
@@ -1363,7 +1209,7 @@ bool StorageManager::write_data_file() {
1363
1209
 
1364
1210
  bool StorageManager::read_data_file() {
1365
1211
  std::string data_path = get_data_file_path();
1366
-
1212
+
1367
1213
  try {
1368
1214
  size_t file_size;
1369
1215
  void* file_data = read_file_binary(data_path.c_str(), &file_size);
@@ -1371,66 +1217,66 @@ bool StorageManager::read_data_file() {
1371
1217
  LOG_STORAGE_ERROR("Failed to read data file: " << data_path);
1372
1218
  return false;
1373
1219
  }
1374
-
1375
- std::vector<uint8_t> buffer(static_cast<uint8_t*>(file_data),
1220
+
1221
+ std::vector<uint8_t> buffer(static_cast<uint8_t*>(file_data),
1376
1222
  static_cast<uint8_t*>(file_data) + file_size);
1377
1223
  free_file_buffer(file_data);
1378
-
1224
+
1379
1225
  // Read header
1380
1226
  if (buffer.size() < 12) {
1381
1227
  LOG_STORAGE_ERROR("Data file too small: " << buffer.size());
1382
1228
  return false;
1383
1229
  }
1384
-
1230
+
1385
1231
  // Check magic
1386
1232
  if (buffer[0] != 'R' || buffer[1] != 'A' || buffer[2] != 'T' || buffer[3] != 'S') {
1387
1233
  LOG_STORAGE_ERROR("Invalid magic in data file");
1388
1234
  return false;
1389
1235
  }
1390
-
1236
+
1391
1237
  // Read version
1392
1238
  uint32_t version = (static_cast<uint32_t>(buffer[4]) << 24) |
1393
1239
  (static_cast<uint32_t>(buffer[5]) << 16) |
1394
1240
  (static_cast<uint32_t>(buffer[6]) << 8) |
1395
1241
  static_cast<uint32_t>(buffer[7]);
1396
-
1242
+
1397
1243
  if (version != 1) {
1398
1244
  LOG_STORAGE_ERROR("Unsupported data file version: " << version);
1399
1245
  return false;
1400
1246
  }
1401
-
1247
+
1402
1248
  // Read entry count
1403
1249
  uint32_t entry_count = (static_cast<uint32_t>(buffer[8]) << 24) |
1404
1250
  (static_cast<uint32_t>(buffer[9]) << 16) |
1405
1251
  (static_cast<uint32_t>(buffer[10]) << 8) |
1406
1252
  static_cast<uint32_t>(buffer[11]);
1407
-
1253
+
1408
1254
  // Read entries
1409
1255
  entries_.clear();
1410
1256
  size_t offset = 12;
1411
-
1257
+
1412
1258
  for (uint32_t i = 0; i < entry_count && offset < buffer.size(); i++) {
1413
1259
  StorageEntry entry;
1414
1260
  size_t bytes_read = 0;
1415
-
1261
+
1416
1262
  if (!StorageEntry::deserialize(buffer, offset, entry, bytes_read)) {
1417
1263
  LOG_STORAGE_ERROR("Failed to deserialize entry " << i << " at offset " << offset);
1418
1264
  return false;
1419
1265
  }
1420
-
1266
+
1421
1267
  // Verify checksum
1422
1268
  if (!entry.verify_checksum()) {
1423
1269
  LOG_STORAGE_WARN("Checksum mismatch for entry '" << entry.key << "', skipping");
1424
1270
  offset += bytes_read;
1425
1271
  continue;
1426
1272
  }
1427
-
1273
+
1428
1274
  entries_[entry.key] = entry;
1429
1275
  offset += bytes_read;
1430
1276
  }
1431
-
1277
+
1432
1278
  return true;
1433
-
1279
+
1434
1280
  } catch (const std::exception& e) {
1435
1281
  LOG_STORAGE_ERROR("Exception while reading data file: " << e.what());
1436
1282
  return false;
@@ -1448,7 +1294,8 @@ uint64_t StorageManager::get_current_timestamp_ms() const {
1448
1294
  }
1449
1295
 
1450
1296
  std::string StorageManager::get_our_peer_id() const {
1451
- return client_.get_our_peer_id();
1297
+ if (!network_) return "";
1298
+ return network_->local_id().to_hex();
1452
1299
  }
1453
1300
 
1454
1301
  void StorageManager::notify_change(const StorageChangeEvent& event) {