/ node / consensus / src / clp / response.rs
response.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphaos library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  //! Response handling for CLP challenges.
 20  
 21  use super::{ClpChallenge, ClpResponse, Signature, ValidatorAddress};
 22  use tracing::{debug, warn};
 23  
 24  /// Handles creation and verification of CLP responses.
 25  pub struct ResponseHandler {
 26      /// Local validator address.
 27      validator_address: ValidatorAddress,
 28  }
 29  
 30  impl ResponseHandler {
 31      /// Create a new response handler.
 32      pub fn new(validator_address: ValidatorAddress) -> Self {
 33          Self { validator_address }
 34      }
 35  
 36      /// Create a response to a challenge.
 37      ///
 38      /// The response signs (challenge_value || current_block).
 39      pub fn create_response(
 40          &self,
 41          challenge: &ClpChallenge,
 42          current_block: u64,
 43          sign_fn: impl FnOnce(&[u8]) -> Vec<u8>,
 44      ) -> Option<ClpResponse> {
 45          // Check if we're still within the response window
 46          if current_block > challenge.deadline_block {
 47              warn!(
 48                  "Cannot respond to challenge {} - deadline passed (current: {}, deadline: {})",
 49                  challenge.id, current_block, challenge.deadline_block
 50              );
 51              return None;
 52          }
 53  
 54          // Create message to sign: challenge_value || current_block
 55          let mut message = Vec::with_capacity(40);
 56          message.extend_from_slice(&challenge.value);
 57          message.extend_from_slice(&current_block.to_le_bytes());
 58  
 59          // Sign the message
 60          let signature = sign_fn(&message);
 61  
 62          debug!("Created CLP response for challenge {} at block {}", challenge.id, current_block);
 63  
 64          Some(ClpResponse {
 65              challenge_id: challenge.id,
 66              validator: self.validator_address.clone(),
 67              signature: Signature(signature),
 68              response_block: current_block,
 69          })
 70      }
 71  
 72      /// Verify a response from another validator.
 73      pub fn verify_response(
 74          &self,
 75          response: &ClpResponse,
 76          challenge: &ClpChallenge,
 77          verify_fn: impl FnOnce(&ValidatorAddress, &[u8], &Signature) -> bool,
 78      ) -> bool {
 79          // Check challenge ID matches
 80          if response.challenge_id != challenge.id {
 81              warn!("Response challenge ID mismatch");
 82              return false;
 83          }
 84  
 85          // Check response was within deadline
 86          if response.response_block > challenge.deadline_block {
 87              warn!(
 88                  "Response from {} was after deadline (response: {}, deadline: {})",
 89                  response.validator, response.response_block, challenge.deadline_block
 90              );
 91              return false;
 92          }
 93  
 94          // Reconstruct the message that was signed
 95          let mut message = Vec::with_capacity(40);
 96          message.extend_from_slice(&challenge.value);
 97          message.extend_from_slice(&response.response_block.to_le_bytes());
 98  
 99          // Verify the signature
100          if !verify_fn(&response.validator, &message, &response.signature) {
101              warn!("Invalid signature from validator {}", response.validator);
102              return false;
103          }
104  
105          debug!("Verified CLP response from {} for challenge {}", response.validator, challenge.id);
106  
107          true
108      }
109  }
110  
111  #[cfg(test)]
112  mod tests {
113      use super::{super::ChallengeId, *};
114  
115      fn dummy_sign(message: &[u8]) -> Vec<u8> {
116          // Dummy signature: just hash the message
117          let mut hasher = std::collections::hash_map::DefaultHasher::new();
118          std::hash::Hash::hash_slice(message, &mut hasher);
119          std::hash::Hasher::finish(&hasher).to_le_bytes().to_vec()
120      }
121  
122      fn dummy_verify(_addr: &ValidatorAddress, message: &[u8], sig: &Signature) -> bool {
123          // Verify dummy signature
124          let expected = dummy_sign(message);
125          sig.0 == expected
126      }
127  
128      #[test]
129      fn test_create_response() {
130          let addr = ValidatorAddress([1u8; 32]);
131          let handler = ResponseHandler::new(addr.clone());
132  
133          let challenge = ClpChallenge {
134              id: ChallengeId([0u8; 32]),
135              value: [42u8; 32],
136              issued_at_block: 100,
137              epoch: 1,
138              round: 1,
139              deadline_block: 103,
140          };
141  
142          let response = handler.create_response(&challenge, 101, dummy_sign);
143          assert!(response.is_some());
144  
145          let response = response.unwrap();
146          assert_eq!(response.challenge_id, challenge.id);
147          assert_eq!(response.validator, addr);
148          assert_eq!(response.response_block, 101);
149      }
150  
151      #[test]
152      fn test_response_after_deadline() {
153          let addr = ValidatorAddress([1u8; 32]);
154          let handler = ResponseHandler::new(addr);
155  
156          let challenge = ClpChallenge {
157              id: ChallengeId([0u8; 32]),
158              value: [42u8; 32],
159              issued_at_block: 100,
160              epoch: 1,
161              round: 1,
162              deadline_block: 103,
163          };
164  
165          // Try to respond after deadline
166          let response = handler.create_response(&challenge, 104, dummy_sign);
167          assert!(response.is_none());
168      }
169  
170      #[test]
171      fn test_verify_response() {
172          let addr = ValidatorAddress([1u8; 32]);
173          let handler = ResponseHandler::new(addr.clone());
174  
175          let challenge = ClpChallenge {
176              id: ChallengeId([0u8; 32]),
177              value: [42u8; 32],
178              issued_at_block: 100,
179              epoch: 1,
180              round: 1,
181              deadline_block: 103,
182          };
183  
184          let response = handler.create_response(&challenge, 101, dummy_sign).unwrap();
185  
186          // Verify the response
187          let valid = handler.verify_response(&response, &challenge, dummy_verify);
188          assert!(valid);
189      }
190  }