1use crate::util::batch::GroveDbOpBatch;
2use grovedb_costs::storage_cost::removal::Identifier;
3use grovedb_costs::storage_cost::removal::StorageRemovedBytes::{
4 BasicStorageRemoval, NoStorageRemoval, SectionedStorageRemoval,
5};
6use std::collections::BTreeMap;
7
8use enum_map::Enum;
9use grovedb::batch::key_info::KeyInfo;
10use grovedb::batch::KeyInfoPath;
11use grovedb::element::reference_path::ReferencePathType;
12use grovedb::element::MaxReferenceHop;
13use grovedb::{batch::QualifiedGroveDbOp, Element, ElementFlags, TreeType};
14use grovedb_costs::OperationCost;
15use itertools::Itertools;
16
17use crate::error::drive::DriveError;
18use crate::error::Error;
19use crate::fees::get_overflow_error;
20use crate::fees::op::LowLevelDriveOperation::{
21 CalculatedCostOperation, FunctionOperation, GroveOperation, PreCalculatedFeeResult,
22};
23use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods;
24use crate::util::storage_flags::StorageFlags;
25use dpp::block::epoch::Epoch;
26use dpp::fee::default_costs::CachedEpochIndexFeeVersions;
27use dpp::fee::fee_result::refunds::FeeRefunds;
28use dpp::fee::fee_result::FeeResult;
29use dpp::fee::Credits;
30use platform_version::version::fee::FeeVersion;
31
32#[derive(Debug, Enum)]
34pub enum BaseOp {
35 Stop,
37 Add,
39 Mul,
41 Sub,
43 Div,
45 Sdiv,
47 Mod,
49 Smod,
51 Addmod,
53 Mulmod,
55 Signextend,
57 Lt,
59 Gt,
61 Slt,
63 Sgt,
65 Eq,
67 Iszero,
69 And,
71 Or,
73 Xor,
75 Not,
77 Byte,
79}
80
81impl BaseOp {
82 pub fn cost(&self) -> u64 {
84 match self {
85 BaseOp::Stop => 0,
86 BaseOp::Add => 12,
87 BaseOp::Mul => 20,
88 BaseOp::Sub => 12,
89 BaseOp::Div => 20,
90 BaseOp::Sdiv => 20,
91 BaseOp::Mod => 20,
92 BaseOp::Smod => 20,
93 BaseOp::Addmod => 32,
94 BaseOp::Mulmod => 32,
95 BaseOp::Signextend => 20,
96 BaseOp::Lt => 12,
97 BaseOp::Gt => 12,
98 BaseOp::Slt => 12,
99 BaseOp::Sgt => 12,
100 BaseOp::Eq => 12,
101 BaseOp::Iszero => 12,
102 BaseOp::And => 12,
103 BaseOp::Or => 12,
104 BaseOp::Xor => 12,
105 BaseOp::Not => 12,
106 BaseOp::Byte => 12,
107 }
108 }
109}
110
111#[derive(Debug, Enum, PartialEq, Eq)]
113pub enum HashFunction {
114 Sha256RipeMD160,
116 Sha256,
118 Sha256_2,
120 Blake3,
122}
123
124impl HashFunction {
125 fn block_size(&self) -> u16 {
126 match self {
127 HashFunction::Sha256 => 64,
128 HashFunction::Sha256_2 => 64,
129 HashFunction::Blake3 => 64,
130 HashFunction::Sha256RipeMD160 => 64,
131 }
132 }
133
134 fn rounds(&self) -> u16 {
135 match self {
136 HashFunction::Sha256 => 1,
137 HashFunction::Sha256_2 => 2,
138 HashFunction::Blake3 => 1,
139 HashFunction::Sha256RipeMD160 => 1,
140 }
141 }
142
143 fn block_cost(&self, fee_version: &FeeVersion) -> u64 {
144 match self {
145 HashFunction::Sha256 => fee_version.hashing.sha256_per_block,
146 HashFunction::Sha256_2 => fee_version.hashing.sha256_per_block,
147 HashFunction::Blake3 => fee_version.hashing.blake3_per_block,
148 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_per_block,
149 }
150 }
151
152 fn base_cost(&self, fee_version: &FeeVersion) -> u64 {
153 match self {
154 HashFunction::Sha256 => fee_version.hashing.single_sha256_base,
155 HashFunction::Sha256_2 => fee_version.hashing.single_sha256_base,
158 HashFunction::Blake3 => fee_version.hashing.blake3_base,
159 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_ripe_md160_base,
160 }
161 }
162}
163
164#[derive(Debug, PartialEq, Eq)]
166pub struct FunctionOp {
167 pub(crate) hash: HashFunction,
169 pub(crate) rounds: u32,
171}
172
173impl FunctionOp {
174 fn cost(&self, fee_version: &FeeVersion) -> Credits {
176 let block_cost = (self.rounds as u64).saturating_mul(self.hash.block_cost(fee_version));
177 self.hash.base_cost(fee_version).saturating_add(block_cost)
178 }
179
180 pub fn new_with_round_count(hash: HashFunction, rounds: u32) -> Self {
183 FunctionOp { hash, rounds }
184 }
185
186 pub fn new_with_byte_count(hash: HashFunction, byte_count: u16) -> Self {
189 let blocks = byte_count / hash.block_size() + 1;
190 let rounds = blocks + hash.rounds() - 1;
191 FunctionOp {
192 hash,
193 rounds: rounds as u32,
194 }
195 }
196}
197
198#[derive(Debug, Eq, PartialEq)]
200pub enum LowLevelDriveOperation {
201 GroveOperation(QualifiedGroveDbOp),
203 FunctionOperation(FunctionOp),
205 CalculatedCostOperation(OperationCost),
207 PreCalculatedFeeResult(FeeResult),
209}
210
211impl LowLevelDriveOperation {
212 pub fn consume_to_fees_v0(
215 drive_operations: Vec<LowLevelDriveOperation>,
216 epoch: &Epoch,
217 epochs_per_era: u16,
218 fee_version: &FeeVersion,
219 previous_fee_versions: Option<&CachedEpochIndexFeeVersions>,
220 ) -> Result<Vec<FeeResult>, Error> {
221 drive_operations
222 .into_iter()
223 .map(|operation| match operation {
224 PreCalculatedFeeResult(f) => Ok(f),
225 FunctionOperation(op) => Ok(FeeResult {
226 processing_fee: op.cost(fee_version),
227 ..Default::default()
228 }),
229 _ => {
230 let cost = operation.operation_cost()?;
231 let storage_fee = cost.storage_cost.added_bytes as u64 * fee_version.storage.storage_disk_usage_credit_per_byte;
234 let processing_fee = cost.ephemeral_cost(fee_version)?;
235 let (fee_refunds, removed_bytes_from_system) =
236 match cost.storage_cost.removed_bytes {
237 NoStorageRemoval => (FeeRefunds::default(), 0),
238 BasicStorageRemoval(amount) => {
239 (FeeRefunds::default(), amount)
241 }
242 SectionedStorageRemoval(mut removal_per_epoch_by_identifier) => {
243
244 let system_amount = removal_per_epoch_by_identifier
245 .remove(&Identifier::default())
246 .map_or(0, |a| a.values().sum());
247 if fee_version.fee_version_number == 1 {
248 (
249 FeeRefunds::from_storage_removal(
250 removal_per_epoch_by_identifier,
251 epoch.index,
252 epochs_per_era,
253 &BTreeMap::default(),
254 )?,
255 system_amount,
256 )
257 } else {
258 let previous_fee_versions = previous_fee_versions.ok_or(Error::Drive(DriveError::CorruptedCodeExecution("expected previous epoch index fee versions to be able to offer refunds")))?;
259 (
260 FeeRefunds::from_storage_removal(
261 removal_per_epoch_by_identifier,
262 epoch.index,
263 epochs_per_era,
264 previous_fee_versions,
265 )?,
266 system_amount,
267 )
268 }
269 }
270 };
271 Ok(FeeResult {
272 storage_fee,
273 processing_fee,
274 fee_refunds,
275 removed_bytes_from_system,
276 })
277 }
278 })
279 .collect()
280 }
281
282 pub fn operation_cost(self) -> Result<OperationCost, Error> {
284 match self {
285 GroveOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
286 "grove operations must be executed, not directly transformed to costs",
287 ))),
288 CalculatedCostOperation(c) => Ok(c),
289 PreCalculatedFeeResult(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
290 "pre calculated fees should not be requested by operation costs",
291 ))),
292 FunctionOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
293 "function operations should not be requested by operation costs",
294 ))),
295 }
296 }
297
298 pub fn combine_cost_operations(operations: &[LowLevelDriveOperation]) -> OperationCost {
300 let mut cost = OperationCost::default();
301 operations.iter().for_each(|op| {
302 if let CalculatedCostOperation(operation_cost) = op {
303 cost += operation_cost.clone()
304 }
305 });
306 cost
307 }
308
309 pub fn grovedb_operations_batch(
311 insert_operations: &[LowLevelDriveOperation],
312 ) -> GroveDbOpBatch {
313 let operations = insert_operations
314 .iter()
315 .filter_map(|op| match op {
316 GroveOperation(grovedb_op) => Some(grovedb_op.clone()),
317 _ => None,
318 })
319 .collect();
320 GroveDbOpBatch::from_operations(operations)
321 }
322
323 pub fn grovedb_operations_batch_consume(
325 insert_operations: Vec<LowLevelDriveOperation>,
326 ) -> GroveDbOpBatch {
327 let operations = insert_operations
328 .into_iter()
329 .filter_map(|op| match op {
330 GroveOperation(grovedb_op) => Some(grovedb_op),
331 _ => None,
332 })
333 .collect();
334 GroveDbOpBatch::from_operations(operations)
335 }
336
337 pub fn grovedb_operations_batch_consume_with_leftovers(
339 insert_operations: Vec<LowLevelDriveOperation>,
340 ) -> (GroveDbOpBatch, Vec<LowLevelDriveOperation>) {
341 let (grove_operations, other_operations): (Vec<_>, Vec<_>) =
342 insert_operations.into_iter().partition_map(|op| match op {
343 GroveOperation(grovedb_op) => itertools::Either::Left(grovedb_op),
344 _ => itertools::Either::Right(op),
345 });
346
347 (
348 GroveDbOpBatch::from_operations(grove_operations),
349 other_operations,
350 )
351 }
352
353 pub fn grovedb_operations_consume(
355 insert_operations: Vec<LowLevelDriveOperation>,
356 ) -> Vec<QualifiedGroveDbOp> {
357 insert_operations
358 .into_iter()
359 .filter_map(|op| match op {
360 GroveOperation(grovedb_op) => Some(grovedb_op),
361 _ => None,
362 })
363 .collect()
364 }
365
366 pub fn for_known_path_key_empty_tree(
368 path: Vec<Vec<u8>>,
369 key: Vec<u8>,
370 storage_flags: Option<&StorageFlags>,
371 ) -> Self {
372 let tree = match storage_flags {
373 Some(storage_flags) => {
374 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
375 }
376 None => Element::empty_tree(),
377 };
378
379 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
380 }
381
382 pub fn for_known_path_key_empty_sum_tree(
384 path: Vec<Vec<u8>>,
385 key: Vec<u8>,
386 storage_flags: Option<&StorageFlags>,
387 ) -> Self {
388 let tree = match storage_flags {
389 Some(storage_flags) => {
390 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
391 }
392 None => Element::empty_sum_tree(),
393 };
394
395 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
396 }
397
398 pub fn for_known_path_key_empty_big_sum_tree(
400 path: Vec<Vec<u8>>,
401 key: Vec<u8>,
402 storage_flags: Option<&StorageFlags>,
403 ) -> Self {
404 let tree = match storage_flags {
405 Some(storage_flags) => {
406 Element::new_big_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
407 }
408 None => Element::empty_big_sum_tree(),
409 };
410
411 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
412 }
413
414 pub fn for_known_path_key_empty_count_tree(
416 path: Vec<Vec<u8>>,
417 key: Vec<u8>,
418 storage_flags: Option<&StorageFlags>,
419 ) -> Self {
420 let tree = match storage_flags {
421 Some(storage_flags) => {
422 Element::new_count_tree_with_flags(None, storage_flags.to_some_element_flags())
423 }
424 None => Element::empty_count_tree(),
425 };
426
427 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
428 }
429
430 pub fn for_known_path_key_empty_count_sum_tree(
432 path: Vec<Vec<u8>>,
433 key: Vec<u8>,
434 storage_flags: Option<&StorageFlags>,
435 ) -> Self {
436 let tree = match storage_flags {
437 Some(storage_flags) => {
438 Element::new_count_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
439 }
440 None => Element::new_count_sum_tree(None),
441 };
442
443 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
444 }
445
446 pub fn for_estimated_path_key_empty_tree(
448 path: KeyInfoPath,
449 key: KeyInfo,
450 storage_flags: Option<&StorageFlags>,
451 ) -> Self {
452 let tree = match storage_flags {
453 Some(storage_flags) => {
454 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
455 }
456 None => Element::empty_tree(),
457 };
458
459 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
460 }
461
462 pub fn for_estimated_path_key_empty_sum_tree(
464 path: KeyInfoPath,
465 key: KeyInfo,
466 storage_flags: Option<&StorageFlags>,
467 ) -> Self {
468 let tree = match storage_flags {
469 Some(storage_flags) => {
470 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
471 }
472 None => Element::empty_sum_tree(),
473 };
474
475 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
476 }
477
478 pub fn insert_for_known_path_key_element(
480 path: Vec<Vec<u8>>,
481 key: Vec<u8>,
482 element: Element,
483 ) -> Self {
484 GroveOperation(QualifiedGroveDbOp::insert_or_replace_op(path, key, element))
485 }
486
487 pub fn replace_for_known_path_key_element(
489 path: Vec<Vec<u8>>,
490 key: Vec<u8>,
491 element: Element,
492 ) -> Self {
493 GroveOperation(QualifiedGroveDbOp::replace_op(path, key, element))
494 }
495
496 pub fn patch_for_known_path_key_element(
499 path: Vec<Vec<u8>>,
500 key: Vec<u8>,
501 element: Element,
502 change_in_bytes: i32,
503 ) -> Self {
504 GroveOperation(QualifiedGroveDbOp::patch_op(
505 path,
506 key,
507 element,
508 change_in_bytes,
509 ))
510 }
511
512 pub fn insert_for_estimated_path_key_element(
514 path: KeyInfoPath,
515 key: KeyInfo,
516 element: Element,
517 ) -> Self {
518 GroveOperation(QualifiedGroveDbOp::insert_estimated_op(path, key, element))
519 }
520
521 pub fn replace_for_estimated_path_key_element(
523 path: KeyInfoPath,
524 key: KeyInfo,
525 element: Element,
526 ) -> Self {
527 GroveOperation(QualifiedGroveDbOp::replace_estimated_op(path, key, element))
528 }
529
530 pub fn refresh_reference_for_known_path_key_reference_info(
532 path: Vec<Vec<u8>>,
533 key: Vec<u8>,
534 reference_path_type: ReferencePathType,
535 max_reference_hop: MaxReferenceHop,
536 flags: Option<ElementFlags>,
537 trust_refresh_reference: bool,
538 ) -> Self {
539 GroveOperation(QualifiedGroveDbOp::refresh_reference_op(
540 path,
541 key,
542 reference_path_type,
543 max_reference_hop,
544 flags,
545 trust_refresh_reference,
546 ))
547 }
548}
549
550pub trait LowLevelDriveOperationTreeTypeConverter {
552 fn empty_tree_operation_for_known_path_key(
554 &self,
555 path: Vec<Vec<u8>>,
556 key: Vec<u8>,
557 storage_flags: Option<&StorageFlags>,
558 ) -> Result<LowLevelDriveOperation, Error>;
559}
560
561impl LowLevelDriveOperationTreeTypeConverter for TreeType {
562 fn empty_tree_operation_for_known_path_key(
564 &self,
565 path: Vec<Vec<u8>>,
566 key: Vec<u8>,
567 storage_flags: Option<&StorageFlags>,
568 ) -> Result<LowLevelDriveOperation, Error> {
569 let element_flags = storage_flags.map(|storage_flags| storage_flags.to_element_flags());
570 let element = match self {
571 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
572 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
573 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
574 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
575 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
576 TreeType::ProvableCountTree => {
577 Element::empty_provable_count_tree_with_flags(element_flags)
578 }
579 TreeType::ProvableCountSumTree => {
580 Element::empty_provable_count_sum_tree_with_flags(element_flags)
581 }
582 TreeType::CommitmentTree(chunk_power) => {
583 Element::empty_commitment_tree_with_flags(*chunk_power, element_flags)?
584 }
585 TreeType::MmrTree => Element::empty_mmr_tree_with_flags(element_flags),
586 TreeType::BulkAppendTree(chunk_power) => {
587 Element::empty_bulk_append_tree_with_flags(*chunk_power, element_flags)?
588 }
589 TreeType::DenseAppendOnlyFixedSizeTree(chunk_power) => {
590 Element::empty_dense_tree_with_flags(*chunk_power, element_flags)
591 }
592 };
593
594 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
595 path, key, element,
596 ))
597 }
598}
599
600pub trait DriveCost {
602 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<u64, Error>;
604}
605
606impl DriveCost for OperationCost {
607 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<Credits, Error> {
609 let OperationCost {
610 seek_count,
611 storage_cost,
612 storage_loaded_bytes,
613 hash_node_calls,
614 sinsemilla_hash_calls,
615 } = self;
616 let epoch_cost_for_processing_credit_per_byte =
617 fee_version.storage.storage_processing_credit_per_byte;
618 let seek_cost = (*seek_count as u64)
619 .checked_mul(fee_version.storage.storage_seek_cost)
620 .ok_or_else(|| get_overflow_error("seek cost overflow"))?;
621 let storage_added_bytes_ephemeral_cost = (storage_cost.added_bytes as u64)
622 .checked_mul(epoch_cost_for_processing_credit_per_byte)
623 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
624 let storage_replaced_bytes_ephemeral_cost = (storage_cost.replaced_bytes as u64)
625 .checked_mul(epoch_cost_for_processing_credit_per_byte)
626 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
627 let storage_removed_bytes_ephemeral_cost =
628 (storage_cost.removed_bytes.total_removed_bytes() as u64)
629 .checked_mul(epoch_cost_for_processing_credit_per_byte)
630 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
631 let storage_loaded_bytes_cost = { *storage_loaded_bytes }
633 .checked_mul(fee_version.storage.storage_load_credit_per_byte)
634 .ok_or_else(|| get_overflow_error("storage loaded cost overflow"))?;
635
636 let blake3_total = fee_version.hashing.blake3_base + fee_version.hashing.blake3_per_block;
638 let hash_node_cost = blake3_total * (*hash_node_calls as u64);
640 let sinsemilla_cost = fee_version.hashing.sinsemilla_base * (*sinsemilla_hash_calls as u64);
641 seek_cost
642 .checked_add(storage_added_bytes_ephemeral_cost)
643 .and_then(|c| c.checked_add(storage_replaced_bytes_ephemeral_cost))
644 .and_then(|c| c.checked_add(storage_loaded_bytes_cost))
645 .and_then(|c| c.checked_add(storage_removed_bytes_ephemeral_cost))
646 .and_then(|c| c.checked_add(hash_node_cost))
647 .and_then(|c| c.checked_add(sinsemilla_cost))
648 .ok_or_else(|| get_overflow_error("ephemeral cost addition overflow"))
649 }
650}