snarkvm_ledger_block/transaction/
serialize.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18impl<N: Network> Serialize for Transaction<N> {
19    /// Serializes the transaction to a JSON-string or buffer.
20    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
21        // Note: We purposefully do not write out the deployment or execution ID,
22        // and instead recompute it when reconstructing the transaction, to ensure there was no malleability.
23        match serializer.is_human_readable() {
24            true => match self {
25                Self::Deploy(id, _, owner, deployment, fee) => {
26                    let mut transaction = serializer.serialize_struct("Transaction", 5)?;
27                    transaction.serialize_field("type", "deploy")?;
28                    transaction.serialize_field("id", &id)?;
29                    transaction.serialize_field("owner", &owner)?;
30                    transaction.serialize_field("deployment", &deployment)?;
31                    transaction.serialize_field("fee", &fee)?;
32                    transaction.end()
33                }
34                Self::Execute(id, _, execution, fee) => {
35                    let mut transaction = serializer.serialize_struct("Transaction", 3 + fee.is_some() as usize)?;
36                    transaction.serialize_field("type", "execute")?;
37                    transaction.serialize_field("id", &id)?;
38                    transaction.serialize_field("execution", &execution)?;
39                    if let Some(fee) = fee {
40                        transaction.serialize_field("fee", &fee)?;
41                    }
42                    transaction.end()
43                }
44                Self::Fee(id, fee) => {
45                    let mut transaction = serializer.serialize_struct("Transaction", 3)?;
46                    transaction.serialize_field("type", "fee")?;
47                    transaction.serialize_field("id", &id)?;
48                    transaction.serialize_field("fee", &fee)?;
49                    transaction.end()
50                }
51            },
52            false => ToBytesSerializer::serialize_with_size_encoding(self, serializer),
53        }
54    }
55}
56
57impl<'de, N: Network> Deserialize<'de> for Transaction<N> {
58    /// Deserializes the transaction from a JSON-string or buffer.
59    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
60        match deserializer.is_human_readable() {
61            true => {
62                // Deserialize the transaction into a JSON value.
63                let mut transaction = serde_json::Value::deserialize(deserializer)?;
64                // Retrieve the transaction ID.
65                let id: N::TransactionID = DeserializeExt::take_from_value::<D>(&mut transaction, "id")?;
66
67                // Recover the transaction.
68                let transaction = match transaction
69                    .get("type")
70                    .ok_or_else(|| de::Error::custom("The \"type\" field is missing"))?
71                    .as_str()
72                {
73                    Some("deploy") => {
74                        // Retrieve the owner.
75                        let owner = DeserializeExt::take_from_value::<D>(&mut transaction, "owner")?;
76                        // Retrieve the deployment.
77                        let deployment = DeserializeExt::take_from_value::<D>(&mut transaction, "deployment")?;
78                        // Retrieve the fee.
79                        let fee = DeserializeExt::take_from_value::<D>(&mut transaction, "fee")?;
80                        // Construct the transaction.
81                        Transaction::from_deployment(owner, deployment, fee).map_err(de::Error::custom)?
82                    }
83                    Some("execute") => {
84                        // Retrieve the execution.
85                        let execution = DeserializeExt::take_from_value::<D>(&mut transaction, "execution")?;
86                        // Retrieve the fee, if it exists.
87                        let fee = serde_json::from_value(
88                            transaction.get_mut("fee").unwrap_or(&mut serde_json::Value::Null).take(),
89                        )
90                        .map_err(de::Error::custom)?;
91                        // Construct the transaction.
92                        Transaction::from_execution(execution, fee).map_err(de::Error::custom)?
93                    }
94                    Some("fee") => {
95                        // Retrieve the fee.
96                        let fee = DeserializeExt::take_from_value::<D>(&mut transaction, "fee")?;
97                        // Construct the transaction.
98                        Transaction::from_fee(fee).map_err(de::Error::custom)?
99                    }
100                    _ => return Err(de::Error::custom("Invalid transaction type")),
101                };
102
103                // Ensure the transaction ID matches.
104                match id == transaction.id() {
105                    true => Ok(transaction),
106                    false => Err(de::Error::custom(error("Mismatching transaction ID, possible data corruption"))),
107                }
108            }
109            false => FromBytesDeserializer::<Self>::deserialize_with_size_encoding(deserializer, "transaction"),
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_serde_json() -> Result<()> {
120        let rng = &mut TestRng::default();
121
122        for expected in [
123            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), true, rng),
124            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, rng),
125            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, rng),
126            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, rng),
127            crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
128            crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0),
129        ]
130        .into_iter()
131        {
132            // Serialize
133            let expected_string = &expected.to_string();
134            let candidate_string = serde_json::to_string(&expected)?;
135
136            // Deserialize
137            assert_eq!(expected, Transaction::from_str(expected_string)?);
138            assert_eq!(expected, serde_json::from_str(&candidate_string)?);
139        }
140        Ok(())
141    }
142
143    #[test]
144    fn test_bincode() -> Result<()> {
145        let rng = &mut TestRng::default();
146
147        for expected in [
148            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), true, rng),
149            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, rng),
150            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, rng),
151            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, rng),
152            crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
153            crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0),
154        ]
155        .into_iter()
156        {
157            // Serialize
158            let expected_bytes = expected.to_bytes_le()?;
159            let expected_bytes_with_size_encoding = bincode::serialize(&expected)?;
160            assert_eq!(&expected_bytes[..], &expected_bytes_with_size_encoding[8..]);
161
162            // Deserialize
163            assert_eq!(expected, Transaction::read_le(&expected_bytes[..])?);
164            assert_eq!(expected, bincode::deserialize(&expected_bytes_with_size_encoding[..])?);
165        }
166        Ok(())
167    }
168}