dpp/address_funds/
witness_verification_operations.rs

1/// Operations performed during address witness verification.
2///
3/// This struct tracks the cryptographic operations performed when verifying
4/// address witnesses (P2PKH and P2SH), which are used to calculate processing fees.
5#[derive(Debug, Clone, Default, PartialEq, Eq)]
6pub struct AddressWitnessVerificationOperations {
7    /// Number of ECDSA secp256k1 signature verifications performed.
8    /// Each verification involves elliptic curve operations.
9    pub ecdsa_signature_verifications: u16,
10
11    /// Number of times we hash the signable bytes using sha256d.
12    /// For P2PKH: 1 (hash computed once for signature verification)
13    /// For P2SH multisig: 1 (hash computed once, reused for all signatures)
14    ///
15    /// Note: This is NOT per signature - with P2SH optimization, the message
16    /// hash is computed once and reused for all signature verifications.
17    pub message_hash_count: u16,
18
19    /// Number of public key hash (Hash160) verifications performed.
20    /// Hash160 = RIPEMD160(SHA256(pubkey))
21    pub pubkey_hash_verifications: u16,
22
23    /// Number of script hash (SHA256) verifications performed.
24    /// Used to verify P2SH redeem scripts.
25    pub script_hash_verifications: u16,
26
27    /// Size of the signable bytes in bytes.
28    /// Used to calculate the number of SHA256 blocks for the first SHA256.
29    /// The second SHA256 (finalization) is always 1 block (32-byte input).
30    pub signable_bytes_len: usize,
31}
32
33impl AddressWitnessVerificationOperations {
34    /// Create a new empty operations tracker
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    /// Operations for a single P2PKH witness verification
40    ///
41    /// # Arguments
42    /// * `signable_bytes_len` - Length of the signable bytes being signed
43    pub fn for_p2pkh(signable_bytes_len: usize) -> Self {
44        Self {
45            ecdsa_signature_verifications: 1,
46            message_hash_count: 1,
47            pubkey_hash_verifications: 1,
48            script_hash_verifications: 0,
49            signable_bytes_len,
50        }
51    }
52
53    /// Operations for a P2SH multisig witness verification
54    ///
55    /// # Arguments
56    /// * `signatures_verified` - Number of signatures that were actually verified
57    ///   (may be more than threshold due to signature ordering in CHECKMULTISIG)
58    /// * `signable_bytes_len` - Length of the signable bytes being signed
59    pub fn for_p2sh_multisig(signatures_verified: u16, signable_bytes_len: usize) -> Self {
60        Self {
61            ecdsa_signature_verifications: signatures_verified,
62            // Hash is computed once and reused for all signature verifications
63            message_hash_count: 1,
64            pubkey_hash_verifications: 0,
65            script_hash_verifications: 1,
66            signable_bytes_len,
67        }
68    }
69
70    /// Combine operations from multiple witness verifications
71    pub fn combine(&mut self, other: &Self) {
72        self.ecdsa_signature_verifications = self
73            .ecdsa_signature_verifications
74            .saturating_add(other.ecdsa_signature_verifications);
75        self.message_hash_count = self
76            .message_hash_count
77            .saturating_add(other.message_hash_count);
78        self.pubkey_hash_verifications = self
79            .pubkey_hash_verifications
80            .saturating_add(other.pubkey_hash_verifications);
81        self.script_hash_verifications = self
82            .script_hash_verifications
83            .saturating_add(other.script_hash_verifications);
84        // Use the max signable_bytes_len since all signatures sign the same bytes
85        self.signable_bytes_len = self.signable_bytes_len.max(other.signable_bytes_len);
86    }
87
88    /// Total number of signature verifications
89    pub fn total_signature_verifications(&self) -> u16 {
90        self.ecdsa_signature_verifications
91    }
92
93    /// Total number of hash operations
94    pub fn total_hash_operations(&self) -> u16 {
95        self.pubkey_hash_verifications
96            .saturating_add(self.script_hash_verifications)
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_p2pkh_operations() {
106        let ops = AddressWitnessVerificationOperations::for_p2pkh(100);
107        assert_eq!(ops.ecdsa_signature_verifications, 1);
108        assert_eq!(ops.pubkey_hash_verifications, 1);
109        assert_eq!(ops.script_hash_verifications, 0);
110        assert_eq!(ops.signable_bytes_len, 100);
111    }
112
113    #[test]
114    fn test_p2sh_multisig_operations() {
115        // 2-of-3 multisig where 2 signatures were verified
116        let ops = AddressWitnessVerificationOperations::for_p2sh_multisig(2, 150);
117        assert_eq!(ops.ecdsa_signature_verifications, 2);
118        assert_eq!(ops.pubkey_hash_verifications, 0);
119        assert_eq!(ops.script_hash_verifications, 1);
120        assert_eq!(ops.signable_bytes_len, 150);
121    }
122
123    #[test]
124    fn test_combine_operations() {
125        let mut ops1 = AddressWitnessVerificationOperations::for_p2pkh(100);
126        let ops2 = AddressWitnessVerificationOperations::for_p2sh_multisig(3, 150);
127
128        ops1.combine(&ops2);
129
130        assert_eq!(ops1.ecdsa_signature_verifications, 4); // 1 + 3
131        assert_eq!(ops1.pubkey_hash_verifications, 1); // 1 + 0
132        assert_eq!(ops1.script_hash_verifications, 1); // 0 + 1
133        assert_eq!(ops1.signable_bytes_len, 150); // max(100, 150)
134    }
135}