Skip to main content

drive/verify/
grovedb_proof_envelope.rs

1use crate::error::proof::ProofError;
2use crate::error::Error;
3
4/// Lowest GroveDB proof envelope version a client accepts.
5///
6/// V0's item binding lets a prover return different item bytes under the
7/// same authenticated root, so a quorum signature on the root does not make a
8/// V0 payload safe. GroveDB emits V1 from grove version 3, which Platform
9/// selects from protocol version 12, so every live network serves V1.
10///
11/// This is client policy, not consensus: no node reads it, so it holds for
12/// every protocol version a client verifies with and is not a version-table
13/// entry.
14pub const MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION: u32 = 1;
15
16/// Reject a GroveDB proof envelope older than
17/// [`MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION`] before its bytes reach Drive.
18///
19/// GroveDB's envelope enum is encoded by bincode as a `u32` discriminant
20/// followed by the version-specific payload; newer discriminants pass here
21/// and fail in GroveDB's own decoder if this build does not know them.
22/// `label` names the proof being read in the error: "proof", "predecessor
23/// proof" or "forward proof".
24pub fn require_supported_grovedb_proof_envelope(
25    proof: &[u8],
26    label: &'static str,
27) -> Result<(), Error> {
28    let config = bincode::config::standard()
29        .with_big_endian()
30        .with_limit::<16>();
31    let (version, _): (u32, usize) =
32        bincode::decode_from_slice(proof, config).map_err(|error| {
33            Error::Proof(ProofError::InvalidGroveDBProofEnvelope {
34                proof: label,
35                reason: error.to_string(),
36            })
37        })?;
38
39    if version < MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION {
40        return Err(Error::Proof(
41            ProofError::UnsupportedGroveDBProofEnvelopeVersion {
42                proof: label,
43                version,
44                minimum: MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION,
45            },
46        ));
47    }
48
49    Ok(())
50}
51
52#[cfg(test)]
53mod tests {
54    use super::{require_supported_grovedb_proof_envelope, MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION};
55    use crate::error::proof::ProofError;
56    use crate::error::Error;
57
58    fn envelope(version: u32) -> Vec<u8> {
59        bincode::encode_to_vec(version, bincode::config::standard().with_big_endian())
60            .expect("encode envelope version")
61    }
62
63    #[test]
64    fn should_reject_a_v0_envelope() {
65        let error = require_supported_grovedb_proof_envelope(&envelope(0), "forward proof")
66            .expect_err("V0 must be rejected");
67
68        assert!(
69            matches!(
70                error,
71                Error::Proof(ProofError::UnsupportedGroveDBProofEnvelopeVersion {
72                    proof: "forward proof",
73                    version: 0,
74                    minimum: MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION,
75                })
76            ),
77            "unexpected error: {error}"
78        );
79        assert_eq!(
80            error.to_string(),
81            "proof: unsupported GroveDB proof envelope version 0 in the forward proof: at least version 1 is required"
82        );
83    }
84
85    #[test]
86    fn should_reject_bytes_without_an_envelope_discriminant() {
87        let error = require_supported_grovedb_proof_envelope(&[], "proof")
88            .expect_err("empty bytes carry no envelope");
89
90        assert!(
91            matches!(
92                error,
93                Error::Proof(ProofError::InvalidGroveDBProofEnvelope { proof: "proof", .. })
94            ),
95            "unexpected error: {error}"
96        );
97    }
98
99    #[test]
100    fn should_pass_envelopes_at_or_above_the_minimum() {
101        for version in [1u32, 2u32] {
102            require_supported_grovedb_proof_envelope(&envelope(version), "proof")
103                .unwrap_or_else(|error| panic!("envelope {version} must pass: {error}"));
104        }
105    }
106}