snarkvm_ledger_block/transaction/
bytes.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> FromBytes for Transaction<N> {
19    /// Reads the transaction from the buffer.
20    #[inline]
21    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
22        // Read the version.
23        let version = u8::read_le(&mut reader)?;
24        // Ensure the version is valid.
25        if version != 1 {
26            return Err(error("Invalid transaction version"));
27        }
28
29        // Read the variant.
30        let variant = u8::read_le(&mut reader)?;
31        // Match the variant.
32        let (id, transaction) = match variant {
33            0 => {
34                // Read the ID.
35                let id = N::TransactionID::read_le(&mut reader)?;
36                // Read the owner.
37                let owner = ProgramOwner::read_le(&mut reader)?;
38                // Read the deployment.
39                let deployment = Deployment::read_le(&mut reader)?;
40                // Read the fee.
41                let fee = Fee::read_le(&mut reader)?;
42
43                // Initialize the transaction.
44                let transaction = Self::from_deployment(owner, deployment, fee).map_err(|e| error(e.to_string()))?;
45                // Return the ID and the transaction.
46                (id, transaction)
47            }
48            1 => {
49                // Read the ID.
50                let id = N::TransactionID::read_le(&mut reader)?;
51                // Read the execution.
52                let execution = Execution::read_le(&mut reader)?;
53
54                // Read the fee variant.
55                let fee_variant = u8::read_le(&mut reader)?;
56                // Read the fee.
57                let fee = match fee_variant {
58                    0u8 => None,
59                    1u8 => Some(Fee::read_le(&mut reader)?),
60                    _ => return Err(error("Invalid fee variant")),
61                };
62
63                // Initialize the transaction.
64                let transaction = Self::from_execution(execution, fee).map_err(|e| error(e.to_string()))?;
65                // Return the ID and the transaction.
66                (id, transaction)
67            }
68            2 => {
69                // Read the ID.
70                let id = N::TransactionID::read_le(&mut reader)?;
71                // Read the fee.
72                let fee = Fee::read_le(&mut reader)?;
73
74                // Initialize the transaction.
75                let transaction = Self::from_fee(fee).map_err(|e| error(e.to_string()))?;
76                // Return the ID and the transaction.
77                (id, transaction)
78            }
79            3.. => return Err(error("Invalid transaction variant")),
80        };
81
82        // Ensure the transaction ID matches.
83        match transaction.id() == id {
84            // Return the transaction.
85            true => Ok(transaction),
86            false => Err(error("Transaction ID mismatch")),
87        }
88    }
89}
90
91impl<N: Network> ToBytes for Transaction<N> {
92    /// Writes the transaction to the buffer.
93    #[inline]
94    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
95        // Write the version.
96        1u8.write_le(&mut writer)?;
97
98        // Write the transaction.
99        // Note: We purposefully do not write out the deployment or execution ID,
100        // and instead recompute it when reconstructing the transaction, to ensure there was no malleability.
101        match self {
102            Self::Deploy(id, _, owner, deployment, fee) => {
103                // Write the variant.
104                0u8.write_le(&mut writer)?;
105                // Write the ID.
106                id.write_le(&mut writer)?;
107                // Write the owner.
108                owner.write_le(&mut writer)?;
109                // Write the deployment.
110                deployment.write_le(&mut writer)?;
111                // Write the fee.
112                fee.write_le(&mut writer)
113            }
114            Self::Execute(id, _, execution, fee) => {
115                // Write the variant.
116                1u8.write_le(&mut writer)?;
117                // Write the ID.
118                id.write_le(&mut writer)?;
119                // Write the execution.
120                execution.write_le(&mut writer)?;
121                // Write the fee.
122                match fee {
123                    None => 0u8.write_le(&mut writer),
124                    Some(fee) => {
125                        1u8.write_le(&mut writer)?;
126                        fee.write_le(&mut writer)
127                    }
128                }
129            }
130            Self::Fee(id, fee) => {
131                // Write the variant.
132                2u8.write_le(&mut writer)?;
133                // Write the ID.
134                id.write_le(&mut writer)?;
135                // Write the fee.
136                fee.write_le(&mut writer)
137            }
138        }
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_bytes() -> Result<()> {
148        let rng = &mut TestRng::default();
149
150        for expected in [
151            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), true, rng),
152            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, rng),
153            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, rng),
154            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, rng),
155            crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
156            crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0),
157        ]
158        .into_iter()
159        {
160            // Check the byte representation.
161            let expected_bytes = expected.to_bytes_le()?;
162            assert_eq!(expected, Transaction::read_le(&expected_bytes[..])?);
163        }
164        Ok(())
165    }
166}