|
| 1 | +//! Stores all the gas costs is one place so they can be compared easily. |
| 2 | +//! Determinism: Once deployed, none of these values can be changed without a version upgrade. |
| 3 | +
|
| 4 | +use super::*; |
| 5 | +use lazy_static::lazy_static; |
| 6 | +use std::str::FromStr; |
| 7 | + |
| 8 | +/// Using 10 gas = ~1ns for WASM instructions. |
| 9 | +const GAS_PER_SECOND: u64 = 10_000_000_000; |
| 10 | + |
| 11 | +/// Set max gas to 1000 seconds worth of gas per handler. The intent here is to have the determinism |
| 12 | +/// cutoff be very high, while still allowing more reasonable timer based cutoffs. Having a unit |
| 13 | +/// like 10 gas for ~1ns allows us to be granular in instructions which are aggregated into metered |
| 14 | +/// blocks via https://docs.rs/pwasm-utils/0.16.0/pwasm_utils/fn.inject_gas_counter.html But we can |
| 15 | +/// still charge very high numbers for other things. |
| 16 | +const CONST_MAX_GAS_PER_HANDLER: u64 = 1000 * GAS_PER_SECOND; |
| 17 | + |
| 18 | +lazy_static! { |
| 19 | + /// This is configurable only for debugging purposes. This value is set by the protocol, |
| 20 | + /// so indexers running in the network should never set this config. |
| 21 | + pub static ref MAX_GAS_PER_HANDLER: u64 = std::env::var("GRAPH_MAX_GAS_PER_HANDLER") |
| 22 | + .ok() |
| 23 | + .map(|s| { |
| 24 | + u64::from_str(&s.replace("_", "")).unwrap_or_else(|_| { |
| 25 | + panic!("GRAPH_LOAD_WINDOW_SIZE must be a number, but is `{}`", s) |
| 26 | + }) |
| 27 | + }) |
| 28 | + .unwrap_or(CONST_MAX_GAS_PER_HANDLER); |
| 29 | +} |
| 30 | + |
| 31 | +/// Gas for instructions are aggregated into blocks, so hopefully gas calls each have relatively |
| 32 | +/// large gas. But in the case they don't, we don't want the overhead of calling out into a host |
| 33 | +/// export to be the dominant cost that causes unexpectedly high execution times. |
| 34 | +/// |
| 35 | +/// This value is based on the benchmark of an empty infinite loop, which does basically nothing |
| 36 | +/// other than call the gas function. The benchmark result was closer to 5000 gas but use 10_000 to |
| 37 | +/// be conservative. |
| 38 | +pub const HOST_EXPORT_GAS: Gas = Gas(10_000); |
| 39 | + |
| 40 | +/// As a heuristic for the cost of host fns it makes sense to reason in terms of bandwidth and |
| 41 | +/// calculate the cost from there. Because we don't have benchmarks for each host fn, we go with |
| 42 | +/// pessimistic assumption of performance of 10 MB/s, which nonetheless allows for 10 GB to be |
| 43 | +/// processed through host exports by a single handler at a 1000 seconds budget. |
| 44 | +const DEFAULT_BYTE_PER_SECOND: u64 = 10_000_000; |
| 45 | + |
| 46 | +/// With the current parameters DEFAULT_GAS_PER_BYTE = 1_000. |
| 47 | +const DEFAULT_GAS_PER_BYTE: u64 = GAS_PER_SECOND / DEFAULT_BYTE_PER_SECOND; |
| 48 | + |
| 49 | +/// Base gas cost for calling any host export. |
| 50 | +/// Security: This must be non-zero. |
| 51 | +pub const DEFAULT_BASE_COST: u64 = 100_000; |
| 52 | + |
| 53 | +pub const DEFAULT_GAS_OP: GasOp = GasOp { |
| 54 | + base_cost: DEFAULT_BASE_COST, |
| 55 | + size_mult: DEFAULT_GAS_PER_BYTE, |
| 56 | +}; |
| 57 | + |
| 58 | +/// Because big math has a multiplicative complexity, that can result in high sizes, so assume a |
| 59 | +/// bandwidth of 100 MB/s, faster than the default. |
| 60 | +const BIG_MATH_BYTE_PER_SECOND: u64 = 100_000_000; |
| 61 | +const BIG_MATH_GAS_PER_BYTE: u64 = GAS_PER_SECOND / BIG_MATH_BYTE_PER_SECOND; |
| 62 | + |
| 63 | +pub const BIG_MATH_GAS_OP: GasOp = GasOp { |
| 64 | + base_cost: DEFAULT_BASE_COST, |
| 65 | + size_mult: BIG_MATH_GAS_PER_BYTE, |
| 66 | +}; |
| 67 | + |
| 68 | +// Allow up to 100,000 data sources to be created |
| 69 | +pub const CREATE_DATA_SOURCE: Gas = Gas(CONST_MAX_GAS_PER_HANDLER / 100_000); |
| 70 | + |
| 71 | +pub const LOG_OP: GasOp = GasOp { |
| 72 | + // Allow up to 100,000 logs |
| 73 | + base_cost: CONST_MAX_GAS_PER_HANDLER / 100_000, |
| 74 | + size_mult: DEFAULT_GAS_PER_BYTE, |
| 75 | +}; |
| 76 | + |
| 77 | +// Saving to the store is one of the most expensive operations. |
| 78 | +pub const STORE_SET: GasOp = GasOp { |
| 79 | + // Allow up to 250k entities saved. |
| 80 | + base_cost: CONST_MAX_GAS_PER_HANDLER / 250_000, |
| 81 | + // If the size roughly corresponds to bytes, allow 1GB to be saved. |
| 82 | + size_mult: CONST_MAX_GAS_PER_HANDLER / 1_000_000_000, |
| 83 | +}; |
| 84 | + |
| 85 | +// Reading from the store is much cheaper than writing. |
| 86 | +pub const STORE_GET: GasOp = GasOp { |
| 87 | + base_cost: CONST_MAX_GAS_PER_HANDLER / 10_000_000, |
| 88 | + size_mult: CONST_MAX_GAS_PER_HANDLER / 10_000_000_000, |
| 89 | +}; |
| 90 | + |
| 91 | +pub const STORE_REMOVE: GasOp = STORE_SET; |
0 commit comments