Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Implement ProverResult JSON serialization



Discussion / hints:

Fix patch:
diff --git a/sigma-tree/src/chain/input.rs b/sigma-tree/src/chain/input.rs
index 2bf9c1b87..490a9a1cf 100644
--- a/sigma-tree/src/chain/input.rs
+++ b/sigma-tree/src/chain/input.rs
@@ -1,7 +1,7 @@
 //! Transaction input
 use std::io;
 
-use super::{box_id::BoxId, prover_result::ProverResult};
+use super::{box_id::BoxId, prover_result::ProverResult, ProofBytes};
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
     SigmaSerializable,
@@ -28,7 +28,7 @@ impl Input {
         Input {
             box_id: self.box_id.clone(),
             spending_proof: ProverResult {
-                proof: vec![],
+                proof: ProofBytes::Empty,
                 extension: self.spending_proof.extension.clone(),
             },
         }
diff --git a/sigma-tree/src/chain/prover_result.rs b/sigma-tree/src/chain/prover_result.rs
index c05f4617f..006632858 100644
--- a/sigma-tree/src/chain/prover_result.rs
+++ b/sigma-tree/src/chain/prover_result.rs
@@ -2,6 +2,7 @@
 use std::io;
 
 use super::context_extension::ContextExtension;
+use super::{Base16DecodedBytes, Base16EncodedBytes};
 use crate::serialization::{
     sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
     SigmaSerializable,
@@ -9,13 +10,70 @@ use crate::serialization::{
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
 
+/// Serialized proof generated by ['Prover']
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
+#[cfg_attr(
+    feature = "with-serde",
+    serde(into = "Base16EncodedBytes", try_from = "Base16DecodedBytes")
+)]
+#[derive(PartialEq, Eq, Hash, Debug, Clone)]
+pub enum ProofBytes {
+    /// Empty proof
+    Empty,
+    /// Non-empty proof
+    Some(Vec<u8>),
+}
+
+impl Into<Base16EncodedBytes> for ProofBytes {
+    fn into(self) -> Base16EncodedBytes {
+        match self {
+            ProofBytes::Empty => Base16EncodedBytes::new(vec![].as_slice()),
+            ProofBytes::Some(bytes) => Base16EncodedBytes::new(&bytes),
+        }
+    }
+}
+
+impl From<Base16DecodedBytes> for ProofBytes {
+    fn from(v: Base16DecodedBytes) -> Self {
+        if v.0.is_empty() {
+            ProofBytes::Empty
+        } else {
+            ProofBytes::Some(v.0)
+        }
+    }
+}
+
+impl SigmaSerializable for ProofBytes {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
+        match self {
+            ProofBytes::Empty => w.put_u16(0)?,
+            ProofBytes::Some(bytes) => {
+                w.put_u16(bytes.len() as u16)?;
+                w.write_all(&bytes)?;
+            }
+        }
+        Ok(())
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        let proof_len = r.get_u16()?;
+        Ok(if proof_len == 0 {
+            ProofBytes::Empty
+        } else {
+            let mut bytes = vec![0; proof_len as usize];
+            r.read_exact(&mut bytes)?;
+            ProofBytes::Some(bytes)
+        })
+    }
+}
+
 /// Proof of correctness of tx spending
 #[derive(Debug, PartialEq, Eq, Clone)]
 #[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
 pub struct ProverResult {
     /// proof that satisfies final sigma proposition
     #[cfg_attr(feature = "with-serde", serde(rename = "proofBytes"))]
-    pub proof: Vec<u8>,
+    pub proof: ProofBytes,
     /// user-defined variables to be put into context
     #[cfg_attr(feature = "with-serde", serde(rename = "extension"))]
     pub extension: ContextExtension,
@@ -23,15 +81,12 @@ pub struct ProverResult {
 
 impl SigmaSerializable for ProverResult {
     fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
-        w.put_u16(self.proof.len() as u16)?;
-        w.write_all(&self.proof)?;
+        self.proof.sigma_serialize(w)?;
         self.extension.sigma_serialize(w)?;
         Ok(())
     }
     fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
-        let proof_len = r.get_u16()?;
-        let mut proof = vec![0; proof_len as usize];
-        r.read_exact(&mut proof)?;
+        let proof = ProofBytes::sigma_parse(r)?;
         let extension = ContextExtension::sigma_parse(r)?;
         Ok(ProverResult { proof, extension })
     }
@@ -43,16 +98,30 @@ mod tests {
     use crate::serialization::sigma_serialize_roundtrip;
     use proptest::{collection::vec, prelude::*};
 
+    impl Arbitrary for ProofBytes {
+        type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            prop_oneof![
+                Just(ProofBytes::Empty),
+                (vec(any::<u8>(), 32..100))
+                    .prop_map(ProofBytes::Some)
+                    .boxed()
+            ]
+            .boxed()
+        }
+    }
+
     impl Arbitrary for ProverResult {
         type Parameters = ();
+        type Strategy = BoxedStrategy<Self>;
 
         fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
-            (vec(any::<u8>(), 0..100), any::<ContextExtension>())
+            (any::<ProofBytes>(), any::<ContextExtension>())
                 .prop_map(|(proof, extension)| Self { proof, extension })
                 .boxed()
         }
-
-        type Strategy = BoxedStrategy<Self>;
     }
     proptest! {
 
diff --git a/sigma-tree/src/chain/transaction.rs b/sigma-tree/src/chain/transaction.rs
index c32deda1d..2225438b3 100644
--- a/sigma-tree/src/chain/transaction.rs
+++ b/sigma-tree/src/chain/transaction.rs
@@ -300,7 +300,7 @@ mod tests {
         {
           "boxId": "9126af0675056b80d1fda7af9bf658464dbfa0b128afca7bf7dae18c27fe8456",
           "spendingProof": {
-            "proofBytes": [],
+            "proofBytes": "",
             "extension": {}
           }
         }
diff --git a/sigma-tree/src/sigma_protocol/prover.rs b/sigma-tree/src/sigma_protocol/prover.rs
index 945abd0ff..a1a367710 100644
--- a/sigma-tree/src/sigma_protocol/prover.rs
+++ b/sigma-tree/src/sigma_protocol/prover.rs
@@ -288,6 +288,7 @@ mod tests {
     use super::*;
     use crate::{
         ast::{Constant, ConstantVal, Expr},
+        chain::ProofBytes,
         sigma_protocol::DlogProverInput,
         types::SType,
     };
@@ -304,7 +305,7 @@ mod tests {
         let prover = TestProver { secrets: vec![] };
         let res = prover.prove(&bool_true_tree, &Env::empty(), message.as_slice());
         assert!(res.is_ok());
-        assert!(res.unwrap().proof.is_empty());
+        assert_eq!(res.unwrap().proof, ProofBytes::Empty);
     }
 
     #[test]
@@ -336,6 +337,6 @@ mod tests {
         };
         let res = prover.prove(&tree, &Env::empty(), message.as_slice());
         assert!(res.is_ok());
-        assert!(!res.unwrap().proof.is_empty());
+        assert_ne!(res.unwrap().proof, ProofBytes::Empty);
     }
 }
diff --git a/sigma-tree/src/sigma_protocol/sig_serializer.rs b/sigma-tree/src/sigma_protocol/sig_serializer.rs
index 7af544313..3d917d79a 100644
--- a/sigma-tree/src/sigma_protocol/sig_serializer.rs
+++ b/sigma-tree/src/sigma_protocol/sig_serializer.rs
@@ -6,13 +6,14 @@ use super::{
     Challenge, GroupSizedBytes, SigmaBoolean, SigmaProofOfKnowledgeTree, UncheckedSigmaTree,
     UncheckedTree,
 };
+use crate::chain::ProofBytes;
 use k256::Scalar;
 use std::convert::{TryFrom, TryInto};
 
 /// Serialize proof tree signatures
-pub fn serialize_sig(tree: UncheckedTree) -> Vec<u8> {
+pub fn serialize_sig(tree: UncheckedTree) -> ProofBytes {
     match tree {
-        UncheckedTree::NoProof => vec![],
+        UncheckedTree::NoProof => ProofBytes::Empty,
         UncheckedTree::UncheckedSigmaTree(UncheckedSigmaTree::UncheckedLeaf(
             UncheckedLeaf::UncheckedSchnorr(us),
         )) => {
@@ -20,7 +21,7 @@ pub fn serialize_sig(tree: UncheckedTree) -> Vec<u8> {
             res.append(&mut us.challenge.into());
             let mut sm_bytes = us.second_message.z.to_bytes();
             res.append(&mut sm_bytes.as_mut_slice().to_vec());
-            res
+            ProofBytes::Some(res)
         }
         _ => todo!(),
     }
@@ -33,11 +34,9 @@ pub fn serialize_sig(tree: UncheckedTree) -> Vec<u8> {
  */
 pub fn parse_sig_compute_challenges(
     exp: SigmaBoolean,
-    proof_bytes: Vec<u8>,
+    proof: &ProofBytes,
 ) -> Result<UncheckedTree, SigParsingError> {
-    if proof_bytes.is_empty() {
-        Err(SigParsingError::InvalidProofSize)
-    } else {
+    if let ProofBytes::Some(proof_bytes) = proof {
         // Verifier Step 2: Let e_0 be the challenge in the node here (e_0 is called "challenge" in the code)
         let chal_len = super::SOUNDNESS_BYTES;
         let challenge = if let Some(bytes) = proof_bytes.get(..chal_len) {
@@ -64,6 +63,8 @@ pub fn parse_sig_compute_challenges(
             }
             _ => todo!(),
         }
+    } else {
+        Err(SigParsingError::InvalidProofSize)
     }
 }
 
diff --git a/sigma-tree/src/sigma_protocol/verifier.rs b/sigma-tree/src/sigma_protocol/verifier.rs
index d48a0c8d9..5cd82aef0 100644
--- a/sigma-tree/src/sigma_protocol/verifier.rs
+++ b/sigma-tree/src/sigma_protocol/verifier.rs
@@ -8,6 +8,7 @@ use super::{
     SigmaBoolean, UncheckedSigmaTree, UncheckedTree,
 };
 use crate::{
+    chain::ProofBytes,
     eval::{Env, EvalError, Evaluator},
     ErgoTree, ErgoTreeParsingError,
 };
@@ -52,7 +53,7 @@ pub trait Verifier: Evaluator {
         &self,
         tree: &ErgoTree,
         env: &Env,
-        proof: &[u8],
+        proof: &ProofBytes,
         message: &[u8],
     ) -> Result<VerificationResult, VerifierError> {
         let expr = tree.proposition()?;
@@ -61,7 +62,7 @@ pub trait Verifier: Evaluator {
             SigmaBoolean::TrivialProp(b) => b,
             sb => {
                 // Perform Verifier Steps 1-3
-                match parse_sig_compute_challenges(sb, proof.to_vec()) {
+                match parse_sig_compute_challenges(sb, proof) {
                     Err(_) => false,
                     Ok(UncheckedTree::UncheckedSigmaTree(sp)) => {
                         // Perform Verifier Step 4
@@ -146,7 +147,7 @@ mod tests {
             let proof = res.unwrap().proof;
 
             let verifier = TestVerifier;
-            let ver_res = verifier.verify(&tree, &Env::empty(), proof.as_slice(), message.as_slice());
+            let ver_res = verifier.verify(&tree, &Env::empty(), &proof, message.as_slice());
             prop_assert_eq!(ver_res.unwrap().result, true);
         }
     }
@@ -154,8 +155,7 @@ mod tests {
     #[test]
     #[cfg(feature = "with-serde")]
     fn test_proof_from_mainnet() {
-        use crate::chain::{AddressEncoder, Base16DecodedBytes, NetworkPrefix, Transaction};
-        use std::convert::TryFrom;
+        use crate::chain::{AddressEncoder, NetworkPrefix, Transaction};
         let tx_json = r#"
          {
       "id": "0e6acf3f18b95bdc5bb1b060baa1eafe53bd89fb08b0e86d6cc00fbdd9e43189",
@@ -163,14 +163,14 @@ mod tests {
         {
           "boxId": "f353ae1b2027e40ea318e7a2673ea4bbaa281b7acee518a0994c5cbdefb05f55",
           "spendingProof": {
-            "proofBytes":[],
+            "proofBytes":"",
             "extension": {}
           }
         },
         {
           "boxId": "56111b039b86f71004b768d2e8b4579f1d79e28e7a617fd5add57a5239498c26",
           "spendingProof": {
-            "proofBytes":[],
+            "proofBytes": "6542a8b8914b103dcbc36d77da3bd58e42ca35755a5190b507764b0bae330b924ce86acfa1b5f9bfc8216c3c4628738e8274d902bea06b48",
             "extension": {}
           }
         }
@@ -232,7 +232,7 @@ mod tests {
 
         let ergo_tree = decoded_addr.script().unwrap();
 
-        let spending_proof_input1 = Base16DecodedBytes::try_from("6542a8b8914b103dcbc36d77da3bd58e42ca35755a5190b507764b0bae330b924ce86acfa1b5f9bfc8216c3c4628738e8274d902bea06b48".to_string()).unwrap();
+        // let spending_proof_input1 = Base16DecodedBytes::try_from("6542a8b8914b103dcbc36d77da3bd58e42ca35755a5190b507764b0bae330b924ce86acfa1b5f9bfc8216c3c4628738e8274d902bea06b48".to_string()).unwrap();
         let tx: Transaction = serde_json::from_str(tx_json).unwrap();
         let tx_id_str: String = tx.id().into();
         assert_eq!(
@@ -244,7 +244,7 @@ mod tests {
         let ver_res = verifier.verify(
             &ergo_tree,
             &Env::empty(),
-            spending_proof_input1.0.as_slice(),
+            &tx.inputs.get(1).unwrap().spending_proof.proof,
             message.as_slice(),
         );
         assert_eq!(ver_res.unwrap().result, true);


Test patch: