1use crate::{
4 address::Address,
5 amount::{Amount, AmountDeserializer, AmountSerializer},
6 error::ModelsError,
7 error::ModelsResult as Result,
8 prehash::{PreHashMap, PreHashSet},
9};
10use massa_serialization::{Deserializer, SerializeError, Serializer};
11use nom::{
12 error::{context, ContextError, ParseError},
13 IResult, Parser,
14};
15use serde::{Deserialize, Serialize};
16use std::{collections::hash_map, ops::Bound::Included};
17
18#[derive(Debug, Default, Deserialize, Clone, Copy, Serialize)]
20pub struct LedgerData {
21 pub balance: Amount,
23}
24
25#[derive(Default)]
27pub struct LedgerDataSerializer {
28 amount_serializer: AmountSerializer,
29}
30
31impl LedgerDataSerializer {
32 pub fn new() -> Self {
34 Self {
35 amount_serializer: AmountSerializer::new(),
36 }
37 }
38}
39
40impl Serializer<LedgerData> for LedgerDataSerializer {
41 fn serialize(&self, value: &LedgerData, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
55 self.amount_serializer.serialize(&value.balance, buffer)?;
56 Ok(())
57 }
58}
59
60pub struct LedgerDataDeserializer {
62 amount_deserializer: AmountDeserializer,
63}
64
65impl LedgerDataDeserializer {
66 pub fn new() -> Self {
68 Self {
69 amount_deserializer: AmountDeserializer::new(
70 Included(Amount::MIN),
71 Included(Amount::MAX),
72 ),
73 }
74 }
75}
76
77impl Default for LedgerDataDeserializer {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83impl Deserializer<LedgerData> for LedgerDataDeserializer {
84 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
101 &self,
102 buffer: &'a [u8],
103 ) -> IResult<&'a [u8], LedgerData, E> {
104 context("Failed LedgerData deserialization", |input| {
105 self.amount_deserializer.deserialize(input)
106 })
107 .map(|balance| LedgerData { balance })
108 .parse(buffer)
109 }
110}
111
112impl LedgerData {
113 pub fn new(starting_balance: Amount) -> LedgerData {
115 LedgerData {
116 balance: starting_balance,
117 }
118 }
119
120 pub fn apply_change(&mut self, change: &LedgerChange) -> Result<()> {
123 if change.balance_increment {
124 self.balance = self
125 .balance
126 .checked_add(change.balance_delta)
127 .ok_or_else(|| {
128 ModelsError::InvalidLedgerChange(
129 "balance overflow in LedgerData::apply_change".into(),
130 )
131 })?;
132 } else {
133 self.balance = self
134 .balance
135 .checked_sub(change.balance_delta)
136 .ok_or_else(|| {
137 ModelsError::InvalidLedgerChange(
138 "balance underflow in LedgerData::apply_change".into(),
139 )
140 })?;
141 }
142 Ok(())
143 }
144
145 pub fn is_nil(&self) -> bool {
147 self.balance == Amount::default()
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct LedgerChange {
154 pub balance_delta: Amount,
156 pub balance_increment: bool,
158}
159
160impl Default for LedgerChange {
161 fn default() -> Self {
162 LedgerChange {
163 balance_delta: Amount::default(),
164 balance_increment: true,
165 }
166 }
167}
168
169impl LedgerChange {
170 pub fn chain(&mut self, change: &LedgerChange) -> Result<(), ModelsError> {
172 if self.balance_increment == change.balance_increment {
173 self.balance_delta = self
174 .balance_delta
175 .checked_add(change.balance_delta)
176 .ok_or_else(|| {
177 ModelsError::InvalidLedgerChange("overflow in LedgerChange::chain".into())
178 })?;
179 } else if change.balance_delta > self.balance_delta {
180 self.balance_delta = change
181 .balance_delta
182 .checked_sub(self.balance_delta)
183 .ok_or_else(|| {
184 ModelsError::InvalidLedgerChange("underflow in LedgerChange::chain".into())
185 })?;
186 self.balance_increment = !self.balance_increment;
187 } else {
188 self.balance_delta = self
189 .balance_delta
190 .checked_sub(change.balance_delta)
191 .ok_or_else(|| {
192 ModelsError::InvalidLedgerChange("underflow in LedgerChange::chain".into())
193 })?;
194 }
195 if self.balance_delta == Amount::default() {
196 self.balance_increment = true;
197 }
198 Ok(())
199 }
200
201 pub fn is_nil(&self) -> bool {
203 self.balance_delta == Amount::default()
204 }
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize, Default)]
209pub struct LedgerChanges(pub PreHashMap<Address, LedgerChange>);
210
211impl LedgerChanges {
212 pub fn get_involved_addresses(&self) -> PreHashSet<Address> {
214 self.0.keys().copied().collect()
215 }
216
217 pub fn apply(&mut self, addr: &Address, change: &LedgerChange) -> Result<()> {
219 match self.0.entry(*addr) {
220 hash_map::Entry::Occupied(mut occ) => {
221 occ.get_mut().chain(change)?;
222 if occ.get().is_nil() {
223 occ.remove();
224 }
225 }
226 hash_map::Entry::Vacant(vac) => {
227 let mut res = LedgerChange::default();
228 res.chain(change)?;
229 if !res.is_nil() {
230 vac.insert(res);
231 }
232 }
233 }
234 Ok(())
235 }
236
237 pub fn chain(&mut self, other: &LedgerChanges) -> Result<()> {
239 let mut updated = self.clone();
241 for (addr, change) in other.0.iter() {
242 updated.apply(addr, change)?;
243 }
244 *self = updated;
245 Ok(())
246 }
247
248 pub fn sync_from(&mut self, addrs: &PreHashSet<Address>, mut other: LedgerChanges) {
251 for addr in addrs.iter() {
252 if let Some(new_val) = other.0.remove(addr) {
253 self.0.insert(*addr, new_val);
254 } else {
255 self.0.remove(addr);
256 }
257 }
258 }
259
260 #[must_use]
262 pub fn clone_subset(&self, addrs: &PreHashSet<Address>) -> Self {
263 LedgerChanges(
264 self.0
265 .iter()
266 .filter_map(|(a, dta)| {
267 if addrs.contains(a) {
268 Some((*a, dta.clone()))
269 } else {
270 None
271 }
272 })
273 .collect(),
274 )
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn apply_change_reports_balance_underflow() {
284 let mut ledger_data = LedgerData::new(Amount::zero());
285 let change = LedgerChange {
286 balance_delta: Amount::from_raw(1),
287 balance_increment: false,
288 };
289
290 let error = ledger_data.apply_change(&change).unwrap_err();
291
292 assert!(matches!(
293 error,
294 ModelsError::InvalidLedgerChange(message)
295 if message == "balance underflow in LedgerData::apply_change"
296 ));
297 }
298}