1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//! Merkle proofs used in Tendermint networks
//!
//! Rewritten to Rust from:
//! https://github.com/tendermint/tendermint/blob/main/crypto/merkle/proof.go
//!
//! Helper functions copied from:
//! https://github.com/informalsystems/tendermint-rs/blob/main/tendermint/src/merkle.rs

use std::cmp::Ordering;

use anyhow::{anyhow, Result};
use rustc_hex::ToHex;
use sha2::{Digest, Sha256};

use tendermint::merkle::{Hash, HASH_SIZE};

/// Maximum number of aunts that can be included in a Proof.
/// This corresponds to a tree of size 2^100, which should be sufficient for all conceivable purposes.
/// This maximum helps prevent Denial-of-Service attacks by limiting the size of the proofs.
pub const MAX_AUNTS: usize = 100;

/// Proof represents a Merkle proof.
///
/// NOTE: The convention for proofs is to include leaf hashes but to
/// exclude the root hash.
/// This convention is implemented across IAVL range proofs as well.
/// Keep this consistent unless there's a very good reason to change
/// everything.  This also affects the generalized proof system as
/// well.
#[derive(Debug, Default, cbor::Encode, cbor::Decode)]
pub struct Proof {
    pub total: i64,       // Total number of items.
    pub index: i64,       // Index of item to prove.
    pub leaf_hash: Hash,  // Hash of item value.
    pub aunts: Vec<Hash>, // Hashes from leaf's sibling to a root's child.
}

impl Proof {
    /// Verify that the Proof proves the root hash.
    /// Check index/total manually if needed.
    pub fn verify(&self, root_hash: Hash, leaf: Hash) -> Result<()> {
        if self.total < 0 {
            return Err(anyhow!("proof total must be positive"));
        }
        if self.index < 0 {
            return Err(anyhow!("proof index cannot be negative"));
        }
        if self.aunts.len() > MAX_AUNTS {
            return Err(anyhow!(
                "expected no more than {} aunts, got {}",
                MAX_AUNTS,
                self.aunts.len()
            ));
        }

        let leaf_hash = leaf_hash(&leaf);
        match self.leaf_hash.cmp(&leaf_hash) {
            Ordering::Equal => (),
            _ => {
                return Err(anyhow!(
                    "invalid leaf hash: wanted {} got {}",
                    leaf_hash.to_hex::<String>(),
                    self.leaf_hash.to_hex::<String>(),
                ));
            }
        }

        let computed_hash = self.compute_root_hash().ok_or_else(|| {
            anyhow!(
                "invalid root hash: wanted {} got None",
                root_hash.to_hex::<String>()
            )
        })?;
        match computed_hash.cmp(&root_hash) {
            Ordering::Equal => (),
            _ => {
                return Err(anyhow!(
                    "invalid root hash: wanted {} got {}",
                    root_hash.to_hex::<String>(),
                    computed_hash.to_hex::<String>(),
                ));
            }
        }

        Ok(())
    }

    /// Compute the root hash given a leaf hash. Does not verify the result.
    pub fn compute_root_hash(&self) -> Option<Hash> {
        Self::compute_hash_from_aunts(self.index, self.total, self.leaf_hash, &self.aunts)
    }

    /// Use the leaf_hash and inner_hashes to get the root merkle hash.
    /// If the length of the inner_hashes slice isn't exactly correct, the result is None.
    /// Recursive impl.
    fn compute_hash_from_aunts(
        index: i64,
        total: i64,
        leaf_hash: Hash,
        inner_hashes: &[Hash],
    ) -> Option<Hash> {
        if index >= total || index < 0 || total <= 0 {
            return None;
        }
        match total {
            0 => unreachable!("cannot call compute_hash_from_aunts() with 0 total"), // Handled above.
            1 => {
                if !inner_hashes.is_empty() {
                    return None;
                }
                Some(leaf_hash)
            }
            _ => {
                if inner_hashes.is_empty() {
                    return None;
                }
                let last_idx = inner_hashes.len() - 1;
                let last_hash = inner_hashes[last_idx];
                let inner_hashes = &inner_hashes[..last_idx];

                let num_left = get_split_point(total as usize) as i64;
                if index < num_left {
                    if let Some(left_hash) =
                        Self::compute_hash_from_aunts(index, num_left, leaf_hash, inner_hashes)
                    {
                        return Some(inner_hash(&left_hash, &last_hash));
                    }
                    return None;
                }
                if let Some(right_hash) = Self::compute_hash_from_aunts(
                    index - num_left,
                    total - num_left,
                    leaf_hash,
                    inner_hashes,
                ) {
                    return Some(inner_hash(&last_hash, &right_hash));
                }
                None
            }
        }
    }
}

/// returns the largest power of 2 less than length
fn get_split_point(length: usize) -> usize {
    match length {
        0 => panic!("tree is empty!"),
        1 => panic!("tree has only one element!"),
        2 => 1,
        _ => length.next_power_of_two() / 2,
    }
}

/// tmhash(0x00 || leaf)
fn leaf_hash(bytes: &[u8]) -> Hash {
    // make a new array starting with 0 and copy in the bytes
    let mut leaf_bytes = Vec::with_capacity(bytes.len() + 1);
    leaf_bytes.push(0x00);
    leaf_bytes.extend_from_slice(bytes);

    // hash it !
    let digest = Sha256::digest(&leaf_bytes);

    // copy the GenericArray out
    let mut hash_bytes = [0u8; HASH_SIZE];
    hash_bytes.copy_from_slice(&digest);
    hash_bytes
}

/// tmhash(0x01 || left || right)
fn inner_hash(left: &[u8], right: &[u8]) -> Hash {
    // make a new array starting with 0x1 and copy in the bytes
    let mut inner_bytes = Vec::with_capacity(left.len() + right.len() + 1);
    inner_bytes.push(0x01);
    inner_bytes.extend_from_slice(left);
    inner_bytes.extend_from_slice(right);

    // hash it !
    let digest = Sha256::digest(&inner_bytes);

    // copy the GenericArray out
    let mut hash_bytes = [0u8; HASH_SIZE];
    hash_bytes.copy_from_slice(&digest);
    hash_bytes
}