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}