Skip to main content

hiero_sdk/system/
freeze_transaction.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use hiero_sdk_proto::services;
4use hiero_sdk_proto::services::freeze_service_client::FreezeServiceClient;
5use time::OffsetDateTime;
6use tonic::transport::Channel;
7
8use crate::protobuf::FromProtobuf;
9use crate::transaction::{
10    AnyTransactionData,
11    ChunkInfo,
12    ToSchedulableTransactionDataProtobuf,
13    ToTransactionDataProtobuf,
14    TransactionData,
15    TransactionExecute,
16};
17use crate::{
18    BoxGrpcFuture,
19    Error,
20    FileId,
21    FreezeType,
22    ToProtobuf,
23    Transaction,
24    ValidateChecksums,
25};
26
27/// Sets the freezing period in which the platform will stop creating
28/// events and accepting transactions.
29///
30/// This is used before safely shut down the platform for maintenance.
31///
32pub type FreezeTransaction = Transaction<FreezeTransactionData>;
33
34#[derive(Debug, Clone, Default)]
35pub struct FreezeTransactionData {
36    start_time: Option<OffsetDateTime>,
37    file_id: Option<FileId>,
38    file_hash: Option<Vec<u8>>,
39    freeze_type: FreezeType,
40}
41
42impl FreezeTransaction {
43    /// Returns the start time.
44    #[must_use]
45    pub fn get_start_time(&self) -> Option<OffsetDateTime> {
46        self.data().start_time
47    }
48
49    /// Sets the start time.
50    pub fn start_time(&mut self, time: OffsetDateTime) -> &mut Self {
51        self.data_mut().start_time = Some(time);
52        self
53    }
54
55    /// Returns the freeze type.
56    #[must_use]
57    pub fn get_freeze_type(&self) -> FreezeType {
58        self.data().freeze_type
59    }
60
61    /// Sets the freeze type.
62    pub fn freeze_type(&mut self, ty: FreezeType) -> &mut Self {
63        self.data_mut().freeze_type = ty;
64        self
65    }
66
67    /// Returns the file ID.
68    #[must_use]
69    pub fn get_file_id(&self) -> Option<FileId> {
70        self.data().file_id
71    }
72
73    /// Sets the file ID.
74    pub fn file_id(&mut self, id: FileId) -> &mut Self {
75        self.data_mut().file_id = Some(id);
76        self
77    }
78
79    /// Returns the file hash.
80    #[must_use]
81    pub fn get_file_hash(&self) -> Option<&[u8]> {
82        self.data().file_hash.as_deref()
83    }
84
85    /// Sets the file hash.
86    pub fn file_hash(&mut self, hash: Vec<u8>) -> &mut Self {
87        self.data_mut().file_hash = Some(hash);
88        self
89    }
90}
91
92impl TransactionData for FreezeTransactionData {}
93
94impl TransactionExecute for FreezeTransactionData {
95    fn execute(
96        &self,
97        channel: Channel,
98        request: services::Transaction,
99    ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
100        Box::pin(async { FreezeServiceClient::new(channel).freeze(request).await })
101    }
102}
103
104impl ValidateChecksums for FreezeTransactionData {
105    fn validate_checksums(&self, ledger_id: &crate::ledger_id::RefLedgerId) -> Result<(), Error> {
106        self.file_id.validate_checksums(ledger_id)
107    }
108}
109
110impl ToTransactionDataProtobuf for FreezeTransactionData {
111    fn to_transaction_data_protobuf(
112        &self,
113        chunk_info: &ChunkInfo,
114    ) -> services::transaction_body::Data {
115        let _ = chunk_info.assert_single_transaction();
116
117        services::transaction_body::Data::Freeze(self.to_protobuf())
118    }
119}
120
121impl ToSchedulableTransactionDataProtobuf for FreezeTransactionData {
122    fn to_schedulable_transaction_data_protobuf(
123        &self,
124    ) -> services::schedulable_transaction_body::Data {
125        services::schedulable_transaction_body::Data::Freeze(self.to_protobuf())
126    }
127}
128
129impl From<FreezeTransactionData> for AnyTransactionData {
130    fn from(transaction: FreezeTransactionData) -> Self {
131        Self::Freeze(transaction)
132    }
133}
134
135impl FromProtobuf<services::FreezeTransactionBody> for FreezeTransactionData {
136    fn from_protobuf(pb: services::FreezeTransactionBody) -> crate::Result<Self> {
137        Ok(Self {
138            start_time: pb.start_time.map(Into::into),
139            file_id: Option::from_protobuf(pb.update_file)?,
140            file_hash: Some(pb.file_hash),
141            freeze_type: FreezeType::from(pb.freeze_type),
142        })
143    }
144}
145
146impl ToProtobuf for FreezeTransactionData {
147    type Protobuf = services::FreezeTransactionBody;
148
149    fn to_protobuf(&self) -> Self::Protobuf {
150        services::FreezeTransactionBody {
151            update_file: self.file_id.to_protobuf(),
152            file_hash: self.file_hash.clone().unwrap_or_default(),
153            start_time: self.start_time.map(Into::into),
154            freeze_type: self.freeze_type as _,
155            ..Default::default()
156        }
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use expect_test::expect;
163    use hex_literal::hex;
164    use hiero_sdk_proto::services;
165    use time::OffsetDateTime;
166
167    use crate::protobuf::{
168        FromProtobuf,
169        ToProtobuf,
170    };
171    use crate::system::FreezeTransactionData;
172    use crate::transaction::test_helpers::{
173        check_body,
174        transaction_body,
175        VALID_START,
176    };
177    use crate::{
178        AnyTransaction,
179        FileId,
180        FreezeTransaction,
181        FreezeType,
182    };
183
184    const FILE_ID: FileId = FileId::new(4, 5, 6);
185    const FILE_HASH: [u8; 14] = hex!("1723904587120938954702349857");
186    const START_TIME: OffsetDateTime = VALID_START;
187    const FREEZE_TYPE: FreezeType = FreezeType::FreezeAbort;
188
189    fn make_transaction() -> FreezeTransaction {
190        let mut tx = FreezeTransaction::new_for_tests();
191
192        tx.file_id(FILE_ID)
193            .file_hash(FILE_HASH.to_vec())
194            .start_time(START_TIME)
195            .freeze_type(FREEZE_TYPE)
196            .freeze()
197            .unwrap();
198
199        tx
200    }
201
202    #[test]
203    fn serialize() {
204        let tx = make_transaction();
205
206        let tx = transaction_body(tx);
207
208        let tx = check_body(tx);
209
210        expect![[r#"
211            Freeze(
212                FreezeTransactionBody {
213                    start_hour: 0,
214                    start_min: 0,
215                    end_hour: 0,
216                    end_min: 0,
217                    update_file: Some(
218                        FileId {
219                            shard_num: 4,
220                            realm_num: 5,
221                            file_num: 6,
222                        },
223                    ),
224                    file_hash: [
225                        23,
226                        35,
227                        144,
228                        69,
229                        135,
230                        18,
231                        9,
232                        56,
233                        149,
234                        71,
235                        2,
236                        52,
237                        152,
238                        87,
239                    ],
240                    start_time: Some(
241                        Timestamp {
242                            seconds: 1554158542,
243                            nanos: 0,
244                        },
245                    ),
246                    freeze_type: FreezeAbort,
247                },
248            )
249        "#]]
250        .assert_debug_eq(&tx)
251    }
252
253    #[test]
254    fn to_from_bytes() {
255        let tx = make_transaction();
256
257        let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
258
259        let tx = transaction_body(tx);
260
261        let tx2 = transaction_body(tx2);
262
263        assert_eq!(tx, tx2);
264    }
265
266    #[test]
267    fn from_proto_body() {
268        let tx = services::FreezeTransactionBody {
269            update_file: Some(FILE_ID.to_protobuf()),
270            file_hash: FILE_HASH.to_vec(),
271            start_time: Some(START_TIME.to_protobuf()),
272            freeze_type: FREEZE_TYPE as i32,
273            ..Default::default()
274        };
275
276        let tx = FreezeTransactionData::from_protobuf(tx).unwrap();
277
278        assert_eq!(tx.file_id, Some(FILE_ID));
279        assert_eq!(tx.file_hash.as_deref(), Some(FILE_HASH.as_slice()));
280        assert_eq!(tx.start_time, Some(START_TIME));
281        assert_eq!(tx.freeze_type, FREEZE_TYPE);
282    }
283
284    mod get_set {
285        use super::*;
286
287        #[test]
288        fn file_id() {
289            let mut tx = FreezeTransaction::new();
290            tx.file_id(FILE_ID);
291
292            assert_eq!(tx.get_file_id(), Some(FILE_ID));
293        }
294
295        #[test]
296        #[should_panic]
297        fn file_id_frozen_panics() {
298            make_transaction().file_id(FILE_ID);
299        }
300
301        #[test]
302        fn file_hash() {
303            let mut tx = FreezeTransaction::new();
304            tx.file_hash(FILE_HASH.to_vec());
305
306            assert_eq!(tx.get_file_hash(), Some(FILE_HASH.as_slice()));
307        }
308
309        #[test]
310        #[should_panic]
311        fn file_hash_frozen_panics() {
312            make_transaction().file_hash(FILE_HASH.to_vec());
313        }
314
315        #[test]
316        fn start_time() {
317            let mut tx = FreezeTransaction::new();
318            tx.start_time(START_TIME);
319
320            assert_eq!(tx.get_start_time(), Some(START_TIME));
321        }
322
323        #[test]
324        #[should_panic]
325        fn start_time_frozen_panics() {
326            make_transaction().start_time(START_TIME);
327        }
328
329        #[test]
330        fn freeze_type() {
331            let mut tx = FreezeTransaction::new();
332            tx.freeze_type(FREEZE_TYPE);
333
334            assert_eq!(tx.get_freeze_type(), FREEZE_TYPE);
335        }
336
337        #[test]
338        #[should_panic]
339        fn freeze_type_frozen_panics() {
340            make_transaction().freeze_type(FREEZE_TYPE);
341        }
342    }
343}