1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
//! Tools for gas instrumentalization
use fuel_types::Word;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
/// Gas unit cost that embeds a unit price and operations count.
///
/// The operations count will be the argument of every variant except
/// `Accumulated`, that will hold the total acumulated gas.
pub enum GasUnit {
/// Atomic operation.
Atom(Word),
/// Arithmetic operation.
Arithmetic(Word),
/// Expensive arithmetic operation.
ArithmeticExpensive(Word),
/// Write to a register.
RegisterWrite(Word),
/// Branching cost.
Branching(Word),
/// Hash crypto operation.
Hash(Word),
/// Memory ownership test cost.
MemoryOwnership(Word),
/// Cost of memory read, per byte.
MemoryRead(Word),
/// Cost of memory write, per byte.
MemoryWrite(Word),
/// Crypto public key recover.
Recover(Word),
/// Cost to read bytes from a storage tree
StorageReadTree(Word),
/// Cost to write bytes to a storage tree
StorageWriteTree(Word),
/// Cost to write a word to the storage
StorageWriteWord(Word),
/// Accumulated cost of several operations.
Accumulated(Word),
}
impl GasUnit {
/// Return the `cost := price · N`.
pub const fn cost(&self) -> Word {
use GasUnit::*;
match self {
Atom(1) => self.unit_price(),
Arithmetic(1) => self.unit_price(),
ArithmeticExpensive(1) => self.unit_price(),
RegisterWrite(1) => self.unit_price(),
Branching(1) => self.unit_price(),
Hash(1) => self.unit_price(),
MemoryOwnership(1) => self.unit_price(),
MemoryRead(1) => self.unit_price(),
MemoryWrite(1) => self.unit_price(),
Recover(1) => self.unit_price(),
StorageReadTree(1) => self.unit_price(),
StorageWriteTree(1) => self.unit_price(),
StorageWriteWord(1) => self.unit_price(),
Atom(n) => *n * Atom(1).cost(),
Arithmetic(n) => *n * Arithmetic(1).cost(),
ArithmeticExpensive(n) => *n * ArithmeticExpensive(1).cost(),
RegisterWrite(n) => *n * RegisterWrite(1).cost(),
Branching(n) => *n * Branching(1).cost(),
Hash(n) => *n * Hash(1).cost(),
MemoryOwnership(n) => *n * MemoryOwnership(1).cost(),
MemoryRead(n) => *n * MemoryRead(1).cost(),
MemoryWrite(n) => *n * MemoryWrite(1).cost(),
Recover(n) => *n * Recover(1).cost(),
StorageReadTree(n) => *n * StorageReadTree(1).cost(),
StorageWriteTree(n) => *n * StorageWriteTree(1).cost(),
StorageWriteWord(n) => *n * StorageWriteWord(1).cost(),
Accumulated(c) => *c,
}
}
/// Return the price per unit.
pub const fn unit_price(&self) -> Word {
use GasUnit::*;
// the values are defined empirically from tests performed in fuel-core-benches.
//
// the worst case scenario of execution is a memory write for chunks larger than the OS
// page size, that is commonly set to `4096` bytes.
//
// the storage, as expected from a production-ready implementation, didn't present alarming
// computing power demand from increased operations because tree-seek should be, in worst
// case scenario, logarithmic.
match self {
// base price for pc inc
Atom(_) => 10,
// arithmetic operations
Arithmetic(_) => 15,
// expensive arith operations
ArithmeticExpensive(_) => 100,
// write a register with reserved branching check
RegisterWrite(_) => 20,
// branching different than reserved reg
Branching(_) => 20,
// native hash operation
Hash(_) => 300,
// memory ownership branching check
MemoryOwnership(_) => 20,
// memory read per page. should increase exponentially to the number of used pages
MemoryRead(_) => 15,
// memory write per page. should increase exponentially to the number of used pages
MemoryWrite(_) => 20,
// native ecrecover operation
Recover(_) => 950,
// storage read. the storage backend should offer logarithmic worst case scenarios
StorageReadTree(_) => 75,
// storage write. the storage backend should offer logarithmic worst case scenarios
StorageWriteTree(_) => 150,
// storage write word. the storage backend should offer logarithmic worst case scenarios
StorageWriteWord(_) => 130,
// accumulated cost for different operations
Accumulated(c) => *c,
}
}
/// Combine two gas computations, accumulating their cost.
pub const fn join(self, other: Self) -> Self {
Self::Accumulated(self.cost() + other.cost())
}
}