chik_protocol/
proof_of_space.rs

1use crate::bytes::{Bytes, Bytes32};
2use chik_bls::G1Element;
3use chik_streamable_macro::streamable;
4
5#[streamable(no_json)]
6pub struct ProofOfSpace {
7    challenge: Bytes32,
8    pool_public_key: Option<G1Element>,
9    pool_contract_puzzle_hash: Option<Bytes32>,
10    plot_public_key: G1Element,
11    // this field was renamed when adding support for v2 plots since the top
12    // bit now means whether it's v1 or v2. To stay backwards compabible with
13    // JSON serialization, we still serialize this as its original name
14    #[cfg_attr(feature = "serde", serde(rename = "size", alias = "version_and_size"))]
15    version_and_size: u8,
16    proof: Bytes,
17}
18
19#[derive(Debug, PartialEq)]
20pub enum PlotSize {
21    V1(u8),
22    V2(u8),
23}
24
25impl ProofOfSpace {
26    pub fn size(&self) -> PlotSize {
27        if (self.version_and_size & 0x80) == 0 {
28            PlotSize::V1(self.version_and_size)
29        } else {
30            PlotSize::V2(self.version_and_size & 0x7f)
31        }
32    }
33}
34
35#[cfg(feature = "py-bindings")]
36use chik_traits::{FromJsonDict, ToJsonDict};
37#[cfg(feature = "py-bindings")]
38use pyo3::prelude::*;
39
40#[cfg(feature = "py-bindings")]
41#[pyclass(name = "PlotSize")]
42pub struct PyPlotSize {
43    #[pyo3(get)]
44    pub size_v1: Option<u8>,
45    #[pyo3(get)]
46    pub size_v2: Option<u8>,
47}
48
49#[cfg(feature = "py-bindings")]
50#[pymethods]
51impl ProofOfSpace {
52    fn size_v1(&self) -> Option<u8> {
53        match self.size() {
54            PlotSize::V1(s) => Some(s),
55            PlotSize::V2(_) => None,
56        }
57    }
58
59    fn size_v2(&self) -> Option<u8> {
60        match self.size() {
61            PlotSize::V1(_) => None,
62            PlotSize::V2(s) => Some(s),
63        }
64    }
65
66    #[pyo3(name = "size")]
67    fn py_size(&self) -> PyPlotSize {
68        PyPlotSize {
69            size_v1: self.size_v1(),
70            size_v2: self.size_v2(),
71        }
72    }
73}
74
75#[cfg(feature = "py-bindings")]
76impl ToJsonDict for ProofOfSpace {
77    fn to_json_dict(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::PyObject> {
78        use pyo3::prelude::PyDictMethods;
79        let ret = pyo3::types::PyDict::new(py);
80
81        ret.set_item("challenge", self.challenge.to_json_dict(py)?)?;
82        ret.set_item("pool_public_key", self.pool_public_key.to_json_dict(py)?)?;
83        ret.set_item(
84            "pool_contract_puzzle_hash",
85            self.pool_contract_puzzle_hash.to_json_dict(py)?,
86        )?;
87        ret.set_item("plot_public_key", self.plot_public_key.to_json_dict(py)?)?;
88
89        // "size" was the original name of this field. We keep it to remain backwards compatible
90        ret.set_item("size", self.version_and_size.to_json_dict(py)?)?;
91        ret.set_item("proof", self.proof.to_json_dict(py)?)?;
92
93        Ok(ret.into())
94    }
95}
96
97#[cfg(feature = "py-bindings")]
98impl FromJsonDict for ProofOfSpace {
99    fn from_json_dict(o: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult<Self> {
100        use pyo3::prelude::PyAnyMethods;
101        Ok(Self {
102            challenge: <Bytes32 as FromJsonDict>::from_json_dict(&o.get_item("challenge")?)?,
103            pool_public_key: <Option<G1Element> as FromJsonDict>::from_json_dict(
104                &o.get_item("pool_public_key")?,
105            )?,
106            pool_contract_puzzle_hash: <Option<Bytes32> as FromJsonDict>::from_json_dict(
107                &o.get_item("pool_contract_puzzle_hash")?,
108            )?,
109            plot_public_key: <G1Element as FromJsonDict>::from_json_dict(
110                &o.get_item("plot_public_key")?,
111            )?,
112            version_and_size: <u8 as FromJsonDict>::from_json_dict(&o.get_item("size")?)?,
113            proof: <Bytes as FromJsonDict>::from_json_dict(&o.get_item("proof")?)?,
114        })
115    }
116}
117
118#[cfg(test)]
119#[allow(clippy::needless_pass_by_value)]
120mod tests {
121    use super::*;
122    use rstest::rstest;
123
124    #[rstest]
125    #[case(0x00, PlotSize::V1(0))]
126    #[case(0x01, PlotSize::V1(1))]
127    #[case(0x08, PlotSize::V1(8))]
128    #[case(0x7f, PlotSize::V1(0x7f))]
129    #[case(0x80, PlotSize::V2(0))]
130    #[case(0x81, PlotSize::V2(1))]
131    #[case(0x80 + 28, PlotSize::V2(28))]
132    #[case(0x80 + 30, PlotSize::V2(30))]
133    #[case(0x80 + 32, PlotSize::V2(32))]
134    #[case(0xff, PlotSize::V2(0x7f))]
135    fn proof_of_space_size(#[case] size_field: u8, #[case] expect: PlotSize) {
136        let pos = ProofOfSpace::new(
137            Bytes32::from(b"abababababababababababababababab"),
138            None,
139            None,
140            G1Element::default(),
141            size_field,
142            Bytes::from(vec![]),
143        );
144
145        assert_eq!(pos.size(), expect);
146    }
147}