ergo_lib/chain/transaction/
input.rs

1//! Transaction input
2
3pub mod prover_result;
4
5use ergotree_interpreter::sigma_protocol::prover::ContextExtension;
6use ergotree_interpreter::sigma_protocol::prover::ProofBytes;
7use ergotree_ir::chain::ergo_box::BoxId;
8use ergotree_ir::serialization::sigma_byte_reader::SigmaByteRead;
9use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWrite;
10use ergotree_ir::serialization::SigmaParsingError;
11use ergotree_ir::serialization::SigmaSerializable;
12use ergotree_ir::serialization::SigmaSerializeResult;
13
14#[cfg(feature = "json")]
15use crate::chain::json::context_extension::ContextExtensionSerde;
16use crate::wallet::box_selector::ErgoBoxId;
17#[cfg(feature = "json")]
18use serde::ser::SerializeStruct;
19#[cfg(feature = "json")]
20use serde::{Deserialize, Serialize};
21
22use self::prover_result::ProverResult;
23
24/// Unsigned (without proofs) transaction input
25#[derive(PartialEq, Eq, Debug, Clone)]
26#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
27#[cfg_attr(feature = "json", derive(Deserialize))]
28pub struct UnsignedInput {
29    /// id of the box to spent
30    #[cfg_attr(feature = "json", serde(rename = "boxId"))]
31    pub box_id: BoxId,
32    /// user-defined variables to be put into context
33    #[cfg_attr(
34        feature = "json",
35        serde(rename = "extension",),
36        serde(with = "crate::chain::json::context_extension::ContextExtensionSerde")
37    )]
38    pub extension: ContextExtension,
39}
40
41#[cfg(feature = "json")]
42impl Serialize for UnsignedInput {
43    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44    where
45        S: serde::Serializer,
46    {
47        let mut s = serializer.serialize_struct("UnsignedInput", 2)?;
48        s.serialize_field("boxId", &self.box_id)?;
49        s.serialize_field(
50            "extension",
51            &ContextExtensionSerde::from(self.extension.clone()),
52        )?;
53        s.end()
54    }
55}
56
57impl UnsignedInput {
58    /// Create new with empty ContextExtension
59    pub fn new(box_id: BoxId, extension: ContextExtension) -> Self {
60        UnsignedInput { box_id, extension }
61    }
62
63    /// Create new Input with empty proof (for UnsignedTransaction id calculation)
64    pub fn input_to_sign(&self) -> Input {
65        Input {
66            box_id: self.box_id,
67            spending_proof: ProverResult {
68                proof: ProofBytes::Empty,
69                extension: self.extension.clone(),
70            },
71        }
72    }
73}
74
75impl<T: ErgoBoxId> From<T> for UnsignedInput {
76    fn from(b: T) -> Self {
77        UnsignedInput::new(b.box_id(), ContextExtension::empty())
78    }
79}
80
81/// Fully signed transaction input
82#[derive(PartialEq, Eq, Debug, Clone)]
83#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
84#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
85pub struct Input {
86    /// id of the box to spent
87    #[cfg_attr(feature = "json", serde(rename = "boxId", alias = "id"))]
88    pub box_id: BoxId,
89    /// proof of spending correctness
90    #[cfg_attr(
91        feature = "json",
92        serde(
93            rename = "spendingProof",
94            deserialize_with = "ergotree_ir::chain::json::t_as_string_or_struct"
95        )
96    )]
97    pub spending_proof: ProverResult,
98}
99
100impl Input {
101    /// Create new
102    pub fn new(box_id: BoxId, spending_proof: ProverResult) -> Self {
103        Self {
104            box_id,
105            spending_proof,
106        }
107    }
108
109    /// Create Input from UnsignedInput and a proof
110    pub fn from_unsigned_input(unsigned_input: UnsignedInput, proof_bytes: ProofBytes) -> Self {
111        Self::new(
112            unsigned_input.box_id,
113            ProverResult {
114                proof: proof_bytes,
115                extension: unsigned_input.extension,
116            },
117        )
118    }
119
120    /// input with an empty proof
121    pub fn input_to_sign(&self) -> Input {
122        Input {
123            box_id: self.box_id,
124            spending_proof: ProverResult {
125                proof: ProofBytes::Empty,
126                extension: self.spending_proof.extension.clone(),
127            },
128        }
129    }
130}
131
132impl SigmaSerializable for Input {
133    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
134        self.box_id.sigma_serialize(w)?;
135        self.spending_proof.sigma_serialize(w)?;
136        Ok(())
137    }
138    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SigmaParsingError> {
139        let box_id = BoxId::sigma_parse(r)?;
140        let spending_proof = ProverResult::sigma_parse(r)?;
141        Ok(Input {
142            box_id,
143            spending_proof,
144        })
145    }
146}
147
148#[cfg(test)]
149#[allow(clippy::panic)]
150mod tests {
151    use super::*;
152    use ergotree_ir::serialization::sigma_serialize_roundtrip;
153    use proptest::prelude::*;
154
155    proptest! {
156
157        #[test]
158        fn ser_roundtrip(v in any::<Input>()) {
159            prop_assert_eq![sigma_serialize_roundtrip(&v), v];
160        }
161    }
162}