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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// This file is part of Gear.

// Copyright (C) 2021-2024 Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Configurations.

use alloc::{collections::BTreeSet, vec::Vec};
use gear_core::{
    costs::{CostPerPage, HostFnWeights},
    pages::{GearPage, WasmPage},
};
use gear_lazy_pages_common::LazyPagesWeights;
use gear_wasm_instrument::syscalls::SyscallName;
use scale_info::scale::{self, Decode, Encode};

/// Number of max pages number to use it in tests.
pub const TESTS_MAX_PAGES_NUMBER: u16 = 512;

/// Contextual block information.
#[derive(Clone, Copy, Debug, Encode, Decode, Default)]
#[codec(crate = scale)]
pub struct BlockInfo {
    /// Height.
    pub height: u32,
    /// Timestamp.
    pub timestamp: u64,
}

/// Memory operations costs.
///
/// Each weight with `lazy_pages_` prefix contains weight for storage read,
/// because for each first page access we need at least check if page exists in storage.
/// But they do not include cost for loading page data from storage into program memory.
/// This weight is taken in account separately, when loading occurs.
///
/// Lazy-pages write accesses does not include cost for uploading page data to storage,
/// because uploading happens after execution, so benchmarks do not include this cost.
/// But they include cost for processing changed page data in runtime.
#[derive(Clone, Debug, Decode, Encode, Default)]
#[codec(crate = scale)]
pub struct PageCosts {
    /// Cost per one [GearPage] signal `read` processing in lazy-pages.
    pub lazy_pages_signal_read: CostPerPage<GearPage>,

    /// Cost per one [GearPage] signal `write` processing in lazy-pages,
    pub lazy_pages_signal_write: CostPerPage<GearPage>,

    /// Cost per one [GearPage] signal `write after read` processing in lazy-pages.
    pub lazy_pages_signal_write_after_read: CostPerPage<GearPage>,

    /// Cost per one [GearPage] host func `read` access processing in lazy-pages.
    pub lazy_pages_host_func_read: CostPerPage<GearPage>,

    /// Cost per one [GearPage] host func `write` access processing in lazy-pages.
    pub lazy_pages_host_func_write: CostPerPage<GearPage>,

    /// Cost per one [GearPage] host func `write after read` access processing in lazy-pages,
    pub lazy_pages_host_func_write_after_read: CostPerPage<GearPage>,

    /// Cost per one [GearPage] data loading from storage and moving it in program memory.
    /// Does not include cost for storage read, because it is taken in account separately.
    pub load_page_data: CostPerPage<GearPage>,

    /// Cost per one [GearPage] uploading data to storage.
    /// Does not include cost for processing changed page data in runtime,
    /// cause it is taken in account separately.
    pub upload_page_data: CostPerPage<GearPage>,

    /// Cost per one [WasmPage] static page. Static pages can have static data,
    /// and executor must to move this data to static pages before execution.
    pub static_page: CostPerPage<WasmPage>,

    /// Cost per one [WasmPage] for memory growing.
    pub mem_grow: CostPerPage<WasmPage>,

    /// Cost per one [GearPage] storage read, when para-chain execution.
    pub parachain_load_heuristic: CostPerPage<GearPage>,
}

impl PageCosts {
    /// Calculates and returns weights for lazy-pages.
    pub fn lazy_pages_weights(&self) -> LazyPagesWeights {
        // Because page may have not data in storage, we do not include
        // cost for loading page data from storage in weights. We provide
        // this cost in `load_page_data` field, so lazy-pages can use it
        // when page data is in storage and must be loaded.
        // On other hand we include cost for uploading page data to storage
        // in each `write` weight, because each write cause page uploading.
        LazyPagesWeights {
            signal_read: self.lazy_pages_signal_read,
            signal_write: self
                .lazy_pages_signal_write
                .saturating_add(self.upload_page_data),
            signal_write_after_read: self
                .lazy_pages_signal_write_after_read
                .saturating_add(self.upload_page_data),
            host_func_read: self.lazy_pages_host_func_read,
            host_func_write: self
                .lazy_pages_host_func_write
                .saturating_add(self.upload_page_data),
            host_func_write_after_read: self
                .lazy_pages_host_func_write_after_read
                .saturating_add(self.upload_page_data),
            load_page_storage_data: self
                .load_page_data
                .saturating_add(self.parachain_load_heuristic),
        }
    }
    /// New one for tests usage.
    pub fn new_for_tests() -> Self {
        let a = 1000.into();
        let b = 4000.into();
        Self {
            lazy_pages_signal_read: a,
            lazy_pages_signal_write: a,
            lazy_pages_signal_write_after_read: a,
            lazy_pages_host_func_read: a,
            lazy_pages_host_func_write: a,
            lazy_pages_host_func_write_after_read: a,
            load_page_data: a,
            upload_page_data: a,
            static_page: b,
            mem_grow: b,
            parachain_load_heuristic: a,
        }
    }
}

/// Execution settings for handling messages.
pub struct ExecutionSettings {
    /// Contextual block information.
    pub block_info: BlockInfo,
    /// Performance multiplier.
    pub performance_multiplier: gsys::Percent,
    /// Max amount of pages in program memory during execution.
    pub max_pages: WasmPage,
    /// Pages costs.
    pub page_costs: PageCosts,
    /// Minimal amount of existence for account.
    pub existential_deposit: u128,
    /// Weights of host functions.
    pub host_fn_weights: HostFnWeights,
    /// Functions forbidden to be called.
    pub forbidden_funcs: BTreeSet<SyscallName>,
    /// Threshold for inserting into mailbox
    pub mailbox_threshold: u64,
    /// Cost for single block waitlist holding.
    pub waitlist_cost: u64,
    /// Cost of holding a message in dispatch stash.
    pub dispatch_hold_cost: u64,
    /// Reserve for parameter of scheduling.
    pub reserve_for: u32,
    /// Cost for reservation holding.
    pub reservation: u64,
    /// Most recently determined random seed, along with the time in the past since when it was determinable by chain observers.
    // TODO: find a way to put a random seed inside block config.
    pub random_data: (Vec<u8>, u32),
    /// Gas multiplier.
    pub gas_multiplier: gsys::GasMultiplier,
}

/// Stable parameters for the whole block across processing runs.
#[derive(Clone)]
pub struct BlockConfig {
    /// Block info.
    pub block_info: BlockInfo,
    /// Performance multiplier.
    pub performance_multiplier: gsys::Percent,
    /// Max allowed page numbers for wasm program.
    pub max_pages: WasmPage,
    /// Allocations config.
    pub page_costs: PageCosts,
    /// Existential deposit.
    pub existential_deposit: u128,
    /// Outgoing limit.
    pub outgoing_limit: u32,
    /// Outgoing bytes limit.
    pub outgoing_bytes_limit: u32,
    /// Host function weights.
    pub host_fn_weights: HostFnWeights,
    /// Forbidden functions.
    pub forbidden_funcs: BTreeSet<SyscallName>,
    /// Mailbox threshold.
    pub mailbox_threshold: u64,
    /// Cost for single block waitlist holding.
    pub waitlist_cost: u64,
    /// Cost of holding a message in dispatch stash.
    pub dispatch_hold_cost: u64,
    /// Reserve for parameter of scheduling.
    pub reserve_for: u32,
    /// Cost for reservation holding.
    pub reservation: u64,
    /// One-time db-read cost.
    pub read_cost: u64,
    /// One-time db-write cost.
    pub write_cost: u64,
    /// Per written byte cost.
    pub write_per_byte_cost: u64,
    /// Per loaded byte cost.
    pub read_per_byte_cost: u64,
    /// WASM module instantiation byte cost.
    pub module_instantiation_byte_cost: u64,
    /// Amount of reservations can exist for 1 program.
    pub max_reservations: u64,
    /// WASM code instrumentation base cost.
    pub code_instrumentation_cost: u64,
    /// WASM code instrumentation per-byte cost.
    pub code_instrumentation_byte_cost: u64,
    /// Gas multiplier.
    pub gas_multiplier: gsys::GasMultiplier,
}