dpp/state_transition/traits/
state_transition_witness_validation.rs

1use crate::address_funds::AddressWitnessVerificationOperations;
2use crate::consensus::signature::InvalidStateTransitionSignatureError;
3use crate::serialization::Signable;
4use crate::state_transition::StateTransitionWitnessSigned;
5use crate::validation::SimpleConsensusValidationResult;
6
7/// Result of witness validation, containing both the validation result and the operations performed.
8pub struct WitnessValidationResult {
9    /// The consensus validation result (empty on success, contains errors on failure)
10    pub validation_result: SimpleConsensusValidationResult,
11    /// The operations performed during validation (for fee calculation)
12    pub operations: AddressWitnessVerificationOperations,
13}
14
15impl WitnessValidationResult {
16    /// Create a new result with the given validation result and operations
17    pub fn new(
18        validation_result: SimpleConsensusValidationResult,
19        operations: AddressWitnessVerificationOperations,
20    ) -> Self {
21        Self {
22            validation_result,
23            operations,
24        }
25    }
26
27    /// Create a new error result with no operations tracked
28    pub fn new_with_error(error: crate::consensus::ConsensusError) -> Self {
29        Self {
30            validation_result: SimpleConsensusValidationResult::new_with_error(error),
31            operations: AddressWitnessVerificationOperations::new(),
32        }
33    }
34}
35
36/// Trait for validating input witnesses against signable bytes.
37///
38/// This trait is implemented by state transitions that have inputs and input_witnesses,
39/// where each input address must have a corresponding valid witness (signature).
40pub trait StateTransitionWitnessValidation: StateTransitionWitnessSigned + Signable {
41    /// Validates that all input witnesses are valid for the given signable bytes.
42    ///
43    /// This method verifies that:
44    /// 1. The number of witnesses matches the number of inputs
45    /// 2. Each witness correctly signs for its corresponding input address
46    ///
47    /// # Arguments
48    /// * `signable_bytes` - The bytes that were signed (typically from `state_transition.signable_bytes()`)
49    ///
50    /// # Returns
51    /// * `WitnessValidationResult` - Contains validation result and operations performed
52    fn validate_witnesses(&self, signable_bytes: &[u8]) -> WitnessValidationResult {
53        let inputs = self.inputs();
54        let witnesses = self.witnesses();
55
56        // Verify witness count matches input count before zipping
57        if inputs.len() != witnesses.len() {
58            return WitnessValidationResult::new_with_error(
59                InvalidStateTransitionSignatureError::new(format!(
60                    "Number of witnesses ({}) does not match number of inputs ({})",
61                    witnesses.len(),
62                    inputs.len()
63                ))
64                .into(),
65            );
66        }
67
68        let mut total_operations = AddressWitnessVerificationOperations::new();
69
70        // Validate each witness against its corresponding input address
71        for (i, (address, witness)) in inputs.keys().zip(witnesses.iter()).enumerate() {
72            match address.verify_bytes_against_witness(witness, signable_bytes) {
73                Ok(operations) => {
74                    total_operations.combine(&operations);
75                }
76                Err(e) => {
77                    return WitnessValidationResult::new_with_error(
78                        InvalidStateTransitionSignatureError::new(format!(
79                            "Witness {} verification failed: {}",
80                            i, e
81                        ))
82                        .into(),
83                    );
84                }
85            }
86        }
87
88        WitnessValidationResult::new(SimpleConsensusValidationResult::new(), total_operations)
89    }
90}