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(¤t_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 }