gear_lazy_pages/
host_func.rs1use crate::{
22 LAZY_PAGES_CONTEXT,
23 common::{CostNo, Error, GasCharger, LazyPagesExecutionContext, LazyPagesRuntimeContext},
24 pages::GearPage,
25 process::{self, AccessHandler},
26};
27use gear_core::{self, memory::MemoryInterval};
28use gear_lazy_pages_common::{ProcessAccessError, Status};
29use std::collections::BTreeSet;
30
31pub(crate) struct HostFuncAccessHandler<'a> {
32 pub is_write: bool,
33 pub gas_counter: &'a mut u64,
34 pub gas_charger: GasCharger,
35}
36
37impl AccessHandler for HostFuncAccessHandler<'_> {
38 type Pages = BTreeSet<GearPage>;
39 type Output = Status;
40
41 fn is_write(&self) -> bool {
42 self.is_write
43 }
44
45 fn check_status_is_gas_exceeded() -> Result<(), Error> {
46 Ok(())
49 }
50
51 fn check_stack_memory_access() -> Result<(), Error> {
52 Ok(())
53 }
54
55 fn check_write_accessed_memory_access() -> Result<(), Error> {
56 Ok(())
57 }
58
59 fn check_read_from_accessed_memory() -> Result<(), Error> {
60 Ok(())
61 }
62
63 fn charge_for_page_access(
64 &mut self,
65 page: GearPage,
66 is_accessed: bool,
67 ) -> Result<Status, Error> {
68 self.gas_charger
69 .charge_for_page_access(self.gas_counter, page, self.is_write, is_accessed)
70 }
71
72 fn charge_for_page_data_loading(&mut self) -> Result<Status, Error> {
73 Ok(self.gas_charger.charge_for_page_data_load(self.gas_counter))
74 }
75
76 fn last_page(pages: &Self::Pages) -> Option<GearPage> {
77 pages.last().copied()
78 }
79
80 fn process_pages(
81 pages: Self::Pages,
82 mut process_one: impl FnMut(GearPage) -> Result<(), Error>,
83 ) -> Result<(), Error> {
84 pages.iter().try_for_each(|page| -> Result<(), Error> {
85 process_one(*page)?;
86 Ok(())
87 })
88 }
89
90 fn into_output(self, ctx: &mut LazyPagesExecutionContext) -> Result<Self::Output, Error> {
91 Ok(ctx.status)
92 }
93}
94
95fn accesses_pages(
96 ctx: &LazyPagesRuntimeContext,
97 accesses: &[MemoryInterval],
98 pages: &mut BTreeSet<GearPage>,
99) -> Result<(), Error> {
100 let page_size = GearPage::size(ctx);
101
102 accesses
103 .iter()
104 .try_for_each(|access| -> Result<(), Error> {
105 let last_byte = access
108 .offset
109 .checked_add(access.size.saturating_sub(1))
110 .ok_or(Error::OutOfWasmMemoryAccess)?;
111
112 let start = (access.offset / page_size) * page_size;
113 let end = (last_byte / page_size) * page_size;
114 let mut offset = start;
115 while offset <= end {
116 pages.insert(GearPage::from_offset(ctx, offset));
117 offset = match offset.checked_add(page_size) {
118 Some(next_offset) => next_offset,
119 None => break,
120 }
121 }
122 Ok(())
123 })?;
124 Ok(())
125}
126
127pub fn pre_process_memory_accesses(
128 reads: &[MemoryInterval],
129 writes: &[MemoryInterval],
130 gas_counter: &mut u64,
131) -> Result<(), ProcessAccessError> {
132 log::trace!("host func mem accesses: {reads:?} {writes:?}");
133 LAZY_PAGES_CONTEXT
134 .with(|ctx| {
135 let mut ctx = ctx.borrow_mut();
136 let (rt_ctx, exec_ctx) = ctx.contexts_mut()?;
137
138 let gas_charger = {
139 GasCharger {
140 read_cost: exec_ctx.cost(CostNo::HostFuncRead),
141 write_cost: exec_ctx.cost(CostNo::HostFuncWrite),
142 write_after_read_cost: exec_ctx.cost(CostNo::HostFuncWriteAfterRead),
143 load_data_cost: exec_ctx.cost(CostNo::LoadPageDataFromStorage),
144 }
145 };
146 let mut status = Status::Normal;
147
148 if !reads.is_empty() {
149 let mut read_pages = BTreeSet::new();
150 accesses_pages(rt_ctx, reads, &mut read_pages)?;
151
152 status = process::process_lazy_pages(
153 rt_ctx,
154 exec_ctx,
155 HostFuncAccessHandler {
156 is_write: false,
157 gas_counter,
158 gas_charger: gas_charger.clone(),
159 },
160 read_pages,
161 )?;
162 }
163
164 if !matches!(status, Status::Normal) {
166 return Ok(status);
167 }
168
169 if !writes.is_empty() {
170 let mut write_pages = BTreeSet::new();
171 accesses_pages(rt_ctx, writes, &mut write_pages)?;
172
173 status = process::process_lazy_pages(
174 rt_ctx,
175 exec_ctx,
176 HostFuncAccessHandler {
177 is_write: true,
178 gas_counter,
179 gas_charger,
180 },
181 write_pages,
182 )?;
183 }
184
185 Ok(status)
186 })
187 .map_err(|err| match err {
188 Error::WasmMemAddrIsNotSet | Error::OutOfWasmMemoryAccess => {
189 ProcessAccessError::OutOfBounds
190 }
191 err => {
192 let err_msg = format!(
193 "pre_process_memory_accesses: unexpected error. \
194 Reads - {reads:?}, writes - {writes:?}, gas counter - {gas_counter}. Got error - {err}"
195 );
196
197 log::error!("{err_msg}");
198 unreachable!("{err_msg}")
199 }
200 })
201 .map(|status| match status {
202 Status::Normal => Ok(()),
203 Status::GasLimitExceeded => Err(ProcessAccessError::GasLimitExceeded),
204 })?
205}