unknown_proposal.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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 log::{debug, error, warn};
  19. use tinyjson::JsonValue;
  20. use darkfi::{
  21. net::P2pPtr,
  22. rpc::jsonrpc::JsonSubscriber,
  23. util::encoding::base64,
  24. validator::{consensus::Proposal, ValidatorPtr},
  25. Error, Result,
  26. };
  27. use darkfi_serial::serialize_async;
  28. use crate::proto::{ForkSyncRequest, ForkSyncResponse, ProposalMessage};
  29. /// Background task to handle unknown proposals.
  30. pub async fn handle_unknown_proposal(
  31. validator: ValidatorPtr,
  32. p2p: P2pPtr,
  33. subscriber: JsonSubscriber,
  34. channel: u32,
  35. proposal: Proposal,
  36. ) -> Result<()> {
  37. // If proposal fork chain was not found, we ask our peer for its sequence
  38. debug!(target: "darkfid::task::handle_unknown_proposal", "Asking peer for fork sequence");
  39. let Some(channel) = p2p.get_channel(channel) else {
  40. error!(target: "darkfid::task::handle_unknown_proposal", "Channel {channel} wasn't found.");
  41. return Ok(())
  42. };
  43. // Communication setup
  44. let Ok(response_sub) = channel.subscribe_msg::<ForkSyncResponse>().await else {
  45. error!(target: "darkfid::task::handle_unknown_proposal", "Failure during `ForkSyncResponse` communication setup with peer: {channel:?}");
  46. return Ok(())
  47. };
  48. // Grab last known block to create the request and execute it
  49. let last = match validator.blockchain.last() {
  50. Ok(l) => l,
  51. Err(e) => {
  52. debug!(target: "darkfid::task::handle_unknown_proposal", "Blockchain last retriaval failed: {e}");
  53. return Ok(())
  54. }
  55. };
  56. let request = ForkSyncRequest { tip: last.1, fork_tip: Some(proposal.hash) };
  57. if let Err(e) = channel.send(&request).await {
  58. debug!(target: "darkfid::task::handle_unknown_proposal", "Channel send failed: {e}");
  59. return Ok(())
  60. };
  61. // Node waits for response
  62. let response = match response_sub
  63. .receive_with_timeout(p2p.settings().read().await.outbound_connect_timeout)
  64. .await
  65. {
  66. Ok(r) => r,
  67. Err(e) => {
  68. debug!(target: "darkfid::task::handle_unknown_proposal", "Asking peer for fork sequence failed: {e}");
  69. return Ok(())
  70. }
  71. };
  72. debug!(target: "darkfid::task::handle_unknown_proposal", "Peer response: {response:?}");
  73. // Verify and store retrieved proposals
  74. debug!(target: "darkfid::task::handle_unknown_proposal", "Processing received proposals");
  75. // Response should not be empty
  76. if response.proposals.is_empty() {
  77. warn!(target: "darkfid::task::handle_unknown_proposal", "Peer responded with empty sequence, node might be out of sync!");
  78. return Ok(())
  79. }
  80. // Sequence length must correspond to requested height
  81. if response.proposals.len() as u32 != proposal.block.header.height - last.0 {
  82. debug!(target: "darkfid::task::handle_unknown_proposal", "Response sequence length is erroneous");
  83. return Ok(())
  84. }
  85. // First proposal must extend canonical
  86. if response.proposals[0].block.header.previous != last.1 {
  87. debug!(target: "darkfid::task::handle_unknown_proposal", "Response sequence doesn't extend canonical");
  88. return Ok(())
  89. }
  90. // Last proposal must be the same as the one requested
  91. if response.proposals.last().unwrap().hash != proposal.hash {
  92. debug!(target: "darkfid::task::handle_unknown_proposal", "Response sequence doesn't correspond to requested tip");
  93. return Ok(())
  94. }
  95. // Process response proposals
  96. for proposal in &response.proposals {
  97. // Append proposal
  98. match validator.append_proposal(proposal).await {
  99. Ok(()) => { /* Do nothing */ }
  100. // Skip already existing proposals
  101. Err(Error::ProposalAlreadyExists) => continue,
  102. Err(e) => {
  103. error!(
  104. target: "darkfid::task::handle_unknown_proposal",
  105. "Error while appending response proposal: {e}"
  106. );
  107. break;
  108. }
  109. };
  110. // Broadcast proposal to rest nodes
  111. let message = ProposalMessage(proposal.clone());
  112. p2p.broadcast_with_exclude(&message, &[channel.address().clone()]).await;
  113. // Notify subscriber
  114. let enc_prop = JsonValue::String(base64::encode(&serialize_async(proposal).await));
  115. subscriber.notify(vec![enc_prop].into()).await;
  116. }
  117. Ok(())
  118. }