chik_protocol/
proof_of_space.rs1use 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 #[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 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}