use crate::bytes::{Bytes, Bytes32};
use chik_bls::G1Element;
use chik_streamable_macro::streamable;
#[streamable(no_json)]
pub struct ProofOfSpace {
challenge: Bytes32,
pool_public_key: Option<G1Element>,
pool_contract_puzzle_hash: Option<Bytes32>,
plot_public_key: G1Element,
#[cfg_attr(feature = "serde", serde(rename = "size", alias = "version_and_size"))]
version_and_size: u8,
proof: Bytes,
}
#[derive(Debug, PartialEq)]
pub enum PlotSize {
V1(u8),
V2(u8),
}
impl ProofOfSpace {
pub fn size(&self) -> PlotSize {
if (self.version_and_size & 0x80) == 0 {
PlotSize::V1(self.version_and_size)
} else {
PlotSize::V2(self.version_and_size & 0x7f)
}
}
}
#[cfg(feature = "py-bindings")]
use chik_traits::{FromJsonDict, ToJsonDict};
#[cfg(feature = "py-bindings")]
use pyo3::prelude::*;
#[cfg(feature = "py-bindings")]
#[pyclass(name = "PlotSize")]
pub struct PyPlotSize {
#[pyo3(get)]
pub size_v1: Option<u8>,
#[pyo3(get)]
pub size_v2: Option<u8>,
}
#[cfg(feature = "py-bindings")]
#[pymethods]
impl PyPlotSize {
#[staticmethod]
fn make_v1(s: u8) -> Self {
Self {
size_v1: Some(s),
size_v2: None,
}
}
#[staticmethod]
fn make_v2(s: u8) -> Self {
Self {
size_v1: None,
size_v2: Some(s),
}
}
}
#[cfg(feature = "py-bindings")]
#[pymethods]
impl ProofOfSpace {
#[pyo3(name = "size")]
fn py_size(&self) -> PyPlotSize {
match self.size() {
PlotSize::V1(s) => PyPlotSize {
size_v1: Some(s),
size_v2: None,
},
PlotSize::V2(s) => PyPlotSize {
size_v1: None,
size_v2: Some(s),
},
}
}
}
#[cfg(feature = "py-bindings")]
impl ToJsonDict for ProofOfSpace {
fn to_json_dict(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::PyObject> {
use pyo3::prelude::PyDictMethods;
let ret = pyo3::types::PyDict::new(py);
ret.set_item("challenge", self.challenge.to_json_dict(py)?)?;
ret.set_item("pool_public_key", self.pool_public_key.to_json_dict(py)?)?;
ret.set_item(
"pool_contract_puzzle_hash",
self.pool_contract_puzzle_hash.to_json_dict(py)?,
)?;
ret.set_item("plot_public_key", self.plot_public_key.to_json_dict(py)?)?;
ret.set_item("size", self.version_and_size.to_json_dict(py)?)?;
ret.set_item("proof", self.proof.to_json_dict(py)?)?;
Ok(ret.into())
}
}
#[cfg(feature = "py-bindings")]
impl FromJsonDict for ProofOfSpace {
fn from_json_dict(o: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult<Self> {
use pyo3::prelude::PyAnyMethods;
Ok(Self {
challenge: <Bytes32 as FromJsonDict>::from_json_dict(&o.get_item("challenge")?)?,
pool_public_key: <Option<G1Element> as FromJsonDict>::from_json_dict(
&o.get_item("pool_public_key")?,
)?,
pool_contract_puzzle_hash: <Option<Bytes32> as FromJsonDict>::from_json_dict(
&o.get_item("pool_contract_puzzle_hash")?,
)?,
plot_public_key: <G1Element as FromJsonDict>::from_json_dict(
&o.get_item("plot_public_key")?,
)?,
version_and_size: <u8 as FromJsonDict>::from_json_dict(&o.get_item("size")?)?,
proof: <Bytes as FromJsonDict>::from_json_dict(&o.get_item("proof")?)?,
})
}
}
#[cfg(test)]
#[allow(clippy::needless_pass_by_value)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case(0x00, PlotSize::V1(0))]
#[case(0x01, PlotSize::V1(1))]
#[case(0x08, PlotSize::V1(8))]
#[case(0x7f, PlotSize::V1(0x7f))]
#[case(0x80, PlotSize::V2(0))]
#[case(0x81, PlotSize::V2(1))]
#[case(0x80 + 28, PlotSize::V2(28))]
#[case(0x80 + 30, PlotSize::V2(30))]
#[case(0x80 + 32, PlotSize::V2(32))]
#[case(0xff, PlotSize::V2(0x7f))]
fn proof_of_space_size(#[case] size_field: u8, #[case] expect: PlotSize) {
let pos = ProofOfSpace::new(
Bytes32::from(b"abababababababababababababababab"),
None,
None,
G1Element::default(),
size_field,
Bytes::from(vec![]),
);
assert_eq!(pos.size(), expect);
}
}