doublecrypt_core/
transaction.rs1use rand::RngCore;
2
3use crate::allocator::SlotAllocator;
4use crate::block_store::BlockStore;
5use crate::codec::{write_encrypted_object, ObjectCodec, PostcardCodec};
6use crate::crypto::CryptoEngine;
7use crate::error::FsResult;
8use crate::model::*;
9
10pub struct TransactionManager {
19 generation: u64,
21 next_is_b: bool,
23}
24
25impl TransactionManager {
26 pub fn new() -> Self {
27 Self {
28 generation: 0,
29 next_is_b: false,
30 }
31 }
32
33 pub fn from_recovered(generation: u64, last_was_b: bool) -> Self {
35 Self {
36 generation,
37 next_is_b: !last_was_b,
38 }
39 }
40
41 pub fn generation(&self) -> u64 {
43 self.generation
44 }
45
46 pub fn commit(
59 &mut self,
60 store: &dyn BlockStore,
61 crypto: &dyn CryptoEngine,
62 codec: &PostcardCodec,
63 allocator: &dyn SlotAllocator,
64 superblock: &Superblock,
65 ) -> FsResult<u64> {
66 self.generation += 1;
67
68 let sb_block = allocator.allocate()?;
70
71 write_encrypted_object(
73 store,
74 crypto,
75 codec,
76 sb_block,
77 ObjectKind::Superblock,
78 superblock,
79 )?;
80
81 let sb_bytes = codec.serialize_object(superblock)?;
83 let checksum = blake3::hash(&sb_bytes);
84
85 let root_ptr = RootPointer {
86 generation: self.generation,
87 superblock_ref: ObjectRef::new(sb_block),
88 checksum: *checksum.as_bytes(),
89 };
90
91 let rp_bytes = codec.serialize_object(&root_ptr)?;
93 let block_size = store.block_size();
94 let mut block = vec![0u8; block_size];
95 rand::thread_rng().fill_bytes(&mut block);
96 let len = rp_bytes.len() as u32;
97 block[..4].copy_from_slice(&len.to_le_bytes());
98 block[4..4 + rp_bytes.len()].copy_from_slice(&rp_bytes);
99
100 let slot = if self.next_is_b {
101 BLOCK_ROOT_POINTER_B
102 } else {
103 BLOCK_ROOT_POINTER_A
104 };
105 store.write_block(slot, &block)?;
106
107 self.next_is_b = !self.next_is_b;
108 Ok(sb_block)
109 }
110
111 pub fn read_root_pointer(
113 store: &dyn BlockStore,
114 codec: &PostcardCodec,
115 slot: u64,
116 ) -> FsResult<Option<RootPointer>> {
117 let block = store.read_block(slot)?;
118 if block.len() < 4 {
119 return Ok(None);
120 }
121 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
122 if len == 0 || 4 + len > block.len() {
123 return Ok(None);
124 }
125 let rp_bytes = &block[4..4 + len];
126 match codec.deserialize_object::<RootPointer>(rp_bytes) {
127 Ok(rp) => Ok(Some(rp)),
128 Err(_) => Ok(None),
129 }
130 }
131
132 pub fn recover_latest(
135 store: &dyn BlockStore,
136 codec: &PostcardCodec,
137 ) -> FsResult<Option<(RootPointer, bool)>> {
138 let rp_a = Self::read_root_pointer(store, codec, BLOCK_ROOT_POINTER_A)?;
139 let rp_b = Self::read_root_pointer(store, codec, BLOCK_ROOT_POINTER_B)?;
140
141 match (rp_a, rp_b) {
142 (Some(a), Some(b)) => {
143 if b.generation >= a.generation {
144 Ok(Some((b, true)))
145 } else {
146 Ok(Some((a, false)))
147 }
148 }
149 (Some(a), None) => Ok(Some((a, false))),
150 (None, Some(b)) => Ok(Some((b, true))),
151 (None, None) => Ok(None),
152 }
153 }
154}