protocol_proposal.rs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{collections::HashSet, sync::Arc};
  19. use async_trait::async_trait;
  20. use log::{debug, error};
  21. use smol::{channel::Sender, lock::RwLock};
  22. use tinyjson::JsonValue;
  23. use darkfi::{
  24. impl_p2p_message,
  25. net::{
  26. metering::MeteringConfiguration,
  27. protocol::protocol_generic::{
  28. ProtocolGenericAction, ProtocolGenericHandler, ProtocolGenericHandlerPtr,
  29. },
  30. session::SESSION_DEFAULT,
  31. Message, P2pPtr,
  32. },
  33. rpc::jsonrpc::JsonSubscriber,
  34. system::{ExecutorPtr, StoppableTask, StoppableTaskPtr},
  35. util::{encoding::base64, time::NanoTimestamp},
  36. validator::{consensus::Proposal, ValidatorPtr},
  37. Error, Result,
  38. };
  39. use darkfi_serial::{serialize_async, SerialDecodable, SerialEncodable};
  40. use crate::task::handle_unknown_proposals;
  41. /// Auxiliary [`Proposal`] wrapper structure used for messaging.
  42. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
  43. pub struct ProposalMessage(pub Proposal);
  44. // TODO: Fine tune
  45. // Since messages are asynchronous we will define loose rules to prevent spamming.
  46. // Each message score will be 1, with a threshold of 50 and expiry time of 5.
  47. // We are not limiting `Proposal` size.
  48. impl_p2p_message!(
  49. ProposalMessage,
  50. "proposal",
  51. 0,
  52. 1,
  53. MeteringConfiguration {
  54. threshold: 50,
  55. sleep_step: 500,
  56. expiry_time: NanoTimestamp::from_secs(5),
  57. }
  58. );
  59. /// Atomic pointer to the `ProtocolProposal` handler.
  60. pub type ProtocolProposalHandlerPtr = Arc<ProtocolProposalHandler>;
  61. /// Handler managing [`Proposal`] messages, over a generic P2P protocol.
  62. pub struct ProtocolProposalHandler {
  63. /// The generic handler for [`Proposal`] messages.
  64. proposals_handler: ProtocolGenericHandlerPtr<ProposalMessage, ProposalMessage>,
  65. /// Unknown proposals queue to be checked for reorg.
  66. unknown_proposals: Arc<RwLock<HashSet<[u8; 32]>>>,
  67. /// Handler background task to process unknown proposals queue.
  68. unknown_proposals_handler: StoppableTaskPtr,
  69. }
  70. impl ProtocolProposalHandler {
  71. /// Initialize a generic prototocol handler for [`Proposal`] messages
  72. /// and registers it to the provided P2P network, using the default session flag.
  73. pub async fn init(p2p: &P2pPtr) -> ProtocolProposalHandlerPtr {
  74. debug!(
  75. target: "darkfid::proto::protocol_proposal::init",
  76. "Adding ProtocolProposal to the protocol registry"
  77. );
  78. let proposals_handler =
  79. ProtocolGenericHandler::new(p2p, "ProtocolProposal", SESSION_DEFAULT).await;
  80. let unknown_proposals = Arc::new(RwLock::new(HashSet::new()));
  81. let unknown_proposals_handler = StoppableTask::new();
  82. Arc::new(Self { proposals_handler, unknown_proposals, unknown_proposals_handler })
  83. }
  84. /// Start the `ProtocolProposal` background task.
  85. pub async fn start(
  86. &self,
  87. executor: &ExecutorPtr,
  88. validator: &ValidatorPtr,
  89. p2p: &P2pPtr,
  90. proposals_sub: JsonSubscriber,
  91. blocks_sub: JsonSubscriber,
  92. ) -> Result<()> {
  93. debug!(
  94. target: "darkfid::proto::protocol_proposal::start",
  95. "Starting ProtocolProposal handler task..."
  96. );
  97. // Generate the message queue smol channel
  98. let (sender, receiver) = smol::channel::unbounded::<(Proposal, u32)>();
  99. // Start the unkown proposals handler task
  100. self.unknown_proposals_handler.clone().start(
  101. handle_unknown_proposals(receiver, self.unknown_proposals.clone(), validator.clone(), p2p.clone(), proposals_sub.clone(), blocks_sub),
  102. |res| async move {
  103. match res {
  104. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  105. Err(e) => error!(target: "darkfid::proto::protocol_proposal::start", "Failed starting unknown proposals handler task: {e}"),
  106. }
  107. },
  108. Error::DetachedTaskStopped,
  109. executor.clone(),
  110. );
  111. // Start the proposals handler task
  112. self.proposals_handler.task.clone().start(
  113. handle_receive_proposal(self.proposals_handler.clone(), sender, self.unknown_proposals.clone(), validator.clone(), proposals_sub),
  114. |res| async move {
  115. match res {
  116. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  117. Err(e) => error!(target: "darkfid::proto::protocol_proposal::start", "Failed starting ProtocolProposal handler task: {e}"),
  118. }
  119. },
  120. Error::DetachedTaskStopped,
  121. executor.clone(),
  122. );
  123. debug!(
  124. target: "darkfid::proto::protocol_proposal::start",
  125. "ProtocolProposal handler task started!"
  126. );
  127. Ok(())
  128. }
  129. /// Stop the `ProtocolProposal` background tasks.
  130. pub async fn stop(&self) {
  131. debug!(target: "darkfid::proto::protocol_proposal::stop", "Terminating ProtocolProposal handler task...");
  132. self.unknown_proposals_handler.stop().await;
  133. self.proposals_handler.task.stop().await;
  134. let mut unknown_proposals = self.unknown_proposals.write().await;
  135. *unknown_proposals = HashSet::new();
  136. drop(unknown_proposals);
  137. debug!(target: "darkfid::proto::protocol_proposal::stop", "ProtocolProposal handler task terminated!");
  138. }
  139. }
  140. /// Background handler function for ProtocolProposal.
  141. async fn handle_receive_proposal(
  142. handler: ProtocolGenericHandlerPtr<ProposalMessage, ProposalMessage>,
  143. sender: Sender<(Proposal, u32)>,
  144. unknown_proposals: Arc<RwLock<HashSet<[u8; 32]>>>,
  145. validator: ValidatorPtr,
  146. proposals_sub: JsonSubscriber,
  147. ) -> Result<()> {
  148. debug!(target: "darkfid::proto::protocol_proposal::handle_receive_proposal", "START");
  149. loop {
  150. // Wait for a new proposal message
  151. let (channel, proposal) = match handler.receiver.recv().await {
  152. Ok(r) => r,
  153. Err(e) => {
  154. debug!(
  155. target: "darkfid::proto::protocol_proposal::handle_receive_proposal",
  156. "recv fail: {e}"
  157. );
  158. continue
  159. }
  160. };
  161. // Check if node has finished syncing its blockchain
  162. if !*validator.synced.read().await {
  163. debug!(
  164. target: "darkfid::proto::protocol_proposal::handle_receive_proposal",
  165. "Node still syncing blockchain, skipping..."
  166. );
  167. handler.send_action(channel, ProtocolGenericAction::Skip).await;
  168. continue
  169. }
  170. // Append proposal
  171. match validator.append_proposal(&proposal.0).await {
  172. Ok(()) => {
  173. // Signal handler to broadcast the valid proposal to rest nodes
  174. handler.send_action(channel, ProtocolGenericAction::Broadcast).await;
  175. // Notify proposals subscriber
  176. let enc_prop = JsonValue::String(base64::encode(&serialize_async(&proposal).await));
  177. proposals_sub.notify(vec![enc_prop].into()).await;
  178. continue
  179. }
  180. Err(e) => {
  181. debug!(
  182. target: "darkfid::proto::protocol_proposal::handle_receive_proposal",
  183. "append_proposal fail: {e}",
  184. );
  185. handler.send_action(channel, ProtocolGenericAction::Skip).await;
  186. match e {
  187. Error::ExtendedChainIndexNotFound => { /* Do nothing */ }
  188. _ => continue,
  189. }
  190. }
  191. };
  192. // Check if we already have the unknown proposal record in our
  193. // queue.
  194. let mut lock = unknown_proposals.write().await;
  195. if lock.contains(proposal.0.hash.inner()) {
  196. debug!(
  197. target: "darkfid::proto::protocol_proposal::handle_receive_proposal",
  198. "Proposal {} is already in our unknown proposals queue.",
  199. proposal.0.hash,
  200. );
  201. drop(lock);
  202. continue
  203. }
  204. // Insert new record in our queue
  205. lock.insert(proposal.0.hash.0);
  206. drop(lock);
  207. // Notify the unknown proposals handler task
  208. if let Err(e) = sender.send((proposal.0, channel)).await {
  209. debug!(
  210. target: "darkfid::proto::protocol_proposal::handle_receive_proposal",
  211. "Channel {channel} send fail: {e}"
  212. );
  213. };
  214. }
  215. }