fuel_core/database/
state.rs1use fuel_core_chain_config::TableEntry;
2use fuel_core_storage::{
3 ContractsStateKey,
4 Error as StorageError,
5 StorageBatchMutate,
6 tables::ContractsState,
7};
8use fuel_core_types::fuel_types::{
9 Bytes32,
10 ContractId,
11};
12use itertools::Itertools;
13
14pub trait StateInitializer {
15 fn init_contract_state<S>(
18 &mut self,
19 contract_id: &ContractId,
20 slots: S,
21 ) -> Result<(), StorageError>
22 where
23 S: Iterator<Item = (Bytes32, Vec<u8>)>;
24
25 fn update_contract_states(
27 &mut self,
28 states: impl IntoIterator<Item = TableEntry<ContractsState>>,
29 ) -> Result<(), StorageError>;
30}
31
32impl<S> StateInitializer for S
33where
34 S: StorageBatchMutate<ContractsState, Error = StorageError>,
35{
36 fn init_contract_state<I>(
37 &mut self,
38 contract_id: &ContractId,
39 slots: I,
40 ) -> Result<(), StorageError>
41 where
42 I: Iterator<Item = (Bytes32, Vec<u8>)>,
43 {
44 let slots = slots
45 .map(|(key, value)| (ContractsStateKey::new(contract_id, &key), value))
46 .collect_vec();
47 <_ as StorageBatchMutate<ContractsState>>::init_storage(
48 self,
49 &mut slots.iter().map(|(key, value)| (key, value.as_slice())),
50 )
51 }
52
53 fn update_contract_states(
69 &mut self,
70 states: impl IntoIterator<Item = TableEntry<ContractsState>>,
71 ) -> Result<(), StorageError> {
72 states
73 .into_iter()
74 .group_by(|s| *s.key.contract_id())
75 .into_iter()
76 .try_for_each(|(contract_id, entries)| {
77 self.init_contract_state(
78 &contract_id,
79 entries
80 .into_iter()
81 .map(|e| (*e.key.state_key(), e.value.into())),
82 )
83 })?;
84
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::database::{
93 Database,
94 database_description::on_chain::OnChain,
95 };
96 use fuel_core_storage::{
97 StorageAsMut,
98 transactional::IntoTransaction,
99 };
100 use fuel_core_types::fuel_types::Bytes32;
101 use rand::Rng;
102
103 fn random_bytes32<R>(rng: &mut R) -> Bytes32
104 where
105 R: Rng + ?Sized,
106 {
107 let mut bytes = [0u8; 32];
108 rng.fill(bytes.as_mut());
109 bytes.into()
110 }
111
112 #[test]
113 fn init_contract_state_works() {
114 use rand::{
115 SeedableRng,
116 rngs::StdRng,
117 };
118
119 let rng = &mut StdRng::seed_from_u64(1234);
120 let r#gen = || Some((random_bytes32(rng), random_bytes32(rng).to_vec()));
121 let data = core::iter::from_fn(r#gen).take(5_000).collect::<Vec<_>>();
122
123 let contract_id = ContractId::from([1u8; 32]);
124 let mut init_database = Database::<OnChain>::default().into_transaction();
125
126 init_database
127 .init_contract_state(&contract_id, data.clone().into_iter())
128 .expect("Should init contract");
129
130 let mut seq_database = Database::<OnChain>::default().into_transaction();
131 for (key, value) in data.iter() {
132 seq_database
133 .storage_as_mut::<ContractsState>()
134 .insert(&ContractsStateKey::new(&contract_id, key), value)
135 .expect("Should insert a state");
136 }
137
138 for (key, value) in data.into_iter() {
139 let init_value = init_database
140 .storage::<ContractsState>()
141 .get(&ContractsStateKey::new(&contract_id, &key))
142 .expect("Should get a state from init database")
143 .unwrap()
144 .into_owned();
145 let seq_value = seq_database
146 .storage::<ContractsState>()
147 .get(&ContractsStateKey::new(&contract_id, &key))
148 .expect("Should get a state from seq database")
149 .unwrap()
150 .into_owned();
151 let value = value.into();
152 assert_eq!(init_value, value);
153 assert_eq!(seq_value, value);
154 }
155 }
156
157 mod update_contract_state {
158 use core::iter::repeat_with;
159 use fuel_core_chain_config::{
160 ContractStateConfig,
161 Randomize,
162 };
163
164 use fuel_core_storage::iter::IteratorOverTable;
165 use rand::{
166 SeedableRng,
167 rngs::StdRng,
168 };
169
170 use super::*;
171
172 #[test]
173 fn states_inserted_into_db() {
174 let mut rng = StdRng::seed_from_u64(0);
176 let state_groups = repeat_with(|| TableEntry::randomize(&mut rng))
177 .chunks(100)
178 .into_iter()
179 .map(|chunk| chunk.collect_vec())
180 .take(10)
181 .collect_vec();
182
183 let database = &mut Database::<OnChain>::default();
184
185 for group in &state_groups {
187 database
188 .update_contract_states(group.clone())
189 .expect("Should insert contract state");
190 }
191
192 let mut states_in_db: Vec<_> = database
194 .iter_all::<ContractsState>(None)
195 .collect::<Result<Vec<_>, _>>()
196 .unwrap()
197 .into_iter()
198 .map(|(key, value)| ContractStateConfig {
199 key: *key.state_key(),
200 value: value.into(),
201 })
202 .collect();
203
204 let mut original_state = state_groups
205 .into_iter()
206 .flatten()
207 .map(|entry| ContractStateConfig {
208 key: *entry.key.state_key(),
209 value: entry.value.into(),
210 })
211 .collect::<Vec<_>>();
212
213 states_in_db.sort();
214 original_state.sort();
215
216 assert_eq!(states_in_db, original_state);
217 }
218 }
219}