Skip to main content

snarkvm_ledger_block/transaction/
serialize.rs

1// Copyright (c) 2019-2026 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        if deserializer.is_human_readable() {
61            // Deserialize the transaction into a JSON value.
62            let mut transaction = serde_json::Value::deserialize(deserializer)?;
63            // Retrieve the transaction ID.
64            let id: N::TransactionID = DeserializeExt::take_from_value::<D>(&mut transaction, "id")
65                .map_err(|err| de::Error::custom(format!("Failed to parse transaction ID: {err}")))?;
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        } else {
109            FromBytesUncheckedDeserializer::<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), false, true, rng),
124            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, false, rng),
125            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, true, rng),
126            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, false, rng),
127            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, true, rng),
128            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, false, rng),
129            crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
130            crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0),
131        ]
132        .into_iter()
133        {
134            // Serialize
135            let expected_string = &expected.to_string();
136            let candidate_string = serde_json::to_string(&expected)?;
137
138            // Deserialize
139            assert_eq!(expected, Transaction::from_str(expected_string)?);
140            assert_eq!(expected, serde_json::from_str(&candidate_string)?);
141        }
142        Ok(())
143    }
144
145    #[test]
146    fn test_bincode() -> Result<()> {
147        let rng = &mut TestRng::default();
148
149        for expected in [
150            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, true, rng),
151            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, false, rng),
152            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, true, rng),
153            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, false, rng),
154            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, true, rng),
155            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, false, rng),
156            crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
157            crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0),
158        ]
159        .into_iter()
160        {
161            // Serialize
162            let expected_bytes = expected.to_bytes_le()?;
163            let expected_bytes_with_size_encoding = bincode::serialize(&expected)?;
164            assert_eq!(&expected_bytes[..], &expected_bytes_with_size_encoding[8..]);
165
166            // Deserialize
167            assert_eq!(expected, Transaction::read_le(&expected_bytes[..])?);
168            assert_eq!(expected, bincode::deserialize(&expected_bytes_with_size_encoding[..])?);
169        }
170        Ok(())
171    }
172}