1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9pub use bobcat_maths::U;
10
11type Address = [u8; 20];
12
13pub use bobcat_cd::read_words;
14
15#[cfg(all(target_family = "wasm", target_os = "unknown"))]
16mod impls {
17 #[link(wasm_import_module = "vm_hooks")]
18 unsafe extern "C" {
19 pub(crate) fn account_balance(addr: *const u8, dest: *mut u8);
20 #[allow(unused)]
21 pub(crate) fn pay_for_memory_grow(pages: u16);
22 pub(crate) fn write_result(d: *const u8, l: usize);
23 pub(crate) fn return_data_size() -> usize;
24 pub(crate) fn read_args(out: *mut u8);
25 pub(crate) fn msg_sender(addr: *mut u8);
26 pub(crate) fn contract_address(addr: *mut u8);
27 pub(crate) fn msg_value(value: *mut u8);
28 pub fn chainid() -> u64;
29 pub(crate) fn account_code_size(address: *const u8) -> usize;
30 pub(crate) fn account_code(
31 address: *const u8,
32 offset: usize,
33 size: usize,
34 dest: *mut u8,
35 ) -> usize;
36 pub(crate) fn account_codehash(address: *const u8, dest: *mut u8);
37 pub(crate) fn block_timestamp() -> u64;
38 pub(crate) fn block_basefee(out: *mut u8);
39 pub(crate) fn evm_gas_left() -> u64;
40 pub(crate) fn evm_ink_left() -> u64;
41 }
42}
43
44#[cfg(all(
45 not(all(target_family = "wasm", target_os = "unknown")),
46 feature = "std"
47))]
48pub mod entry_host {
49 use super::{Address, U};
50
51 use core::{ptr::copy_nonoverlapping, slice::from_raw_parts};
52
53 use std::{cell::RefCell, cmp::min, collections::HashMap};
54
55 use bobcat_storage::keccak256;
56
57 thread_local! {
58 static ACCOUNT_BALANCE: RefCell<HashMap<Address, U>> = RefCell::default();
59 static ARGS: RefCell<Vec<u8>> = RefCell::default();
60 static MSG_SENDER: RefCell<[u8; 20]> = RefCell::default();
61 static CONTRACT_ADDRESS: RefCell<Address> = RefCell::default();
62 static MSG_VALUE: RefCell<U> = RefCell::default();
63 static CHAIN_ID: RefCell<u64> = RefCell::default();
64 static BLOCK_TIMESTAMP: RefCell<u64> = RefCell::default();
65 static ACCOUNT_CODE: RefCell<HashMap<Address, Vec<u8>>> = RefCell::default();
66 }
67
68 const EMPTY_HASH: U = U([
69 0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03,
70 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85,
71 0xa4, 0x70,
72 ]);
73
74 pub(crate) unsafe fn account_balance(addr_: *const u8, out: *mut u8) {
75 ACCOUNT_BALANCE.with(|s| {
76 let mut addr = [0u8; 20];
77 unsafe {
78 copy_nonoverlapping(addr_, addr.as_mut_ptr(), 32);
79 }
80 let h = s.borrow();
81 let amt = h.get(&addr).unwrap_or(&U::ZERO);
82 unsafe {
83 copy_nonoverlapping(amt.as_ptr(), out, 32);
84 }
85 })
86 }
87
88 #[allow(unused)]
89 pub(crate) unsafe fn pay_for_memory_grow(_: u16) {}
90
91 pub(crate) unsafe fn write_result(d: *const u8, l: usize) {
92 println!("{}", const_hex::encode(unsafe { from_raw_parts(d, l) }));
93 }
94
95 pub(crate) fn return_data_size() -> usize {
96 0
97 }
98
99 pub fn set_args(x: Vec<u8>) {
100 ARGS.with(|s| *s.borrow_mut() = x)
101 }
102
103 pub fn args_len() -> usize {
104 ARGS.with(|s| s.borrow().len())
105 }
106
107 pub(crate) unsafe fn read_args(out: *mut u8) {
108 ARGS.with(|s| {
109 let b = s.borrow();
110 unsafe {
111 copy_nonoverlapping(b.as_ptr(), out, b.len());
112 }
113 })
114 }
115
116 pub fn set_msg_sender(x: Address) {
117 MSG_SENDER.with(|s| *s.borrow_mut() = x)
118 }
119
120 pub(crate) unsafe fn msg_sender(out: *mut u8) {
121 MSG_SENDER.with(|s| {
122 let b = s.borrow();
123 unsafe {
124 copy_nonoverlapping(b.as_ptr(), out, 20);
125 }
126 })
127 }
128
129 pub fn set_contract_address(x: Address) {
130 CONTRACT_ADDRESS.with(|s| {
131 *s.borrow_mut() = x;
132 })
133 }
134
135 pub fn set_account_code(x: Address, code: Vec<u8>) {
136 ACCOUNT_CODE.with(|s| s.borrow_mut().insert(x, code));
137 }
138
139 pub(crate) unsafe fn contract_address(out: *mut u8) {
140 CONTRACT_ADDRESS.with(|s| {
141 let b = s.borrow();
142 unsafe {
143 copy_nonoverlapping(b.as_ptr(), out, 20);
144 }
145 })
146 }
147
148 pub(crate) unsafe fn msg_value(out: *mut u8) {
149 MSG_VALUE.with(|s| {
150 let b = s.borrow();
151 unsafe {
152 copy_nonoverlapping(b.as_ptr(), out, 32);
153 }
154 })
155 }
156
157 pub(crate) unsafe fn chainid() -> u64 {
158 CHAIN_ID.with(|s| s.borrow().clone())
159 }
160
161 pub(crate) unsafe fn account_code_size(addr_: *const u8) -> usize {
162 ACCOUNT_CODE.with(|s| {
163 let mut addr = [0u8; 20];
164 unsafe {
165 copy_nonoverlapping(addr_, addr.as_mut_ptr(), 20);
166 }
167 s.borrow().get(&addr).map(|s| s.len()).unwrap_or(0)
168 })
169 }
170
171 pub(crate) unsafe fn account_code(
172 addr_: *const u8,
173 offset: usize,
174 size: usize,
175 out: *mut u8,
176 ) -> usize {
177 ACCOUNT_CODE.with(|s| {
178 let mut addr = [0u8; 20];
179 unsafe {
180 copy_nonoverlapping(addr_, addr.as_mut_ptr(), 20);
181 }
182 let b = s.borrow();
183 match b.get(&addr) {
184 Some(b) => {
185 if offset >= b.len() {
186 return 0;
187 }
188 let src = &b[offset..];
189 let len = min(size, src.len());
190 unsafe {
191 copy_nonoverlapping(src.as_ptr(), out, len);
192 }
193 len
194 }
195 None => 0,
196 }
197 })
198 }
199
200 pub unsafe fn account_codehash(addr_: *const u8, out: *mut u8) {
201 ACCOUNT_CODE.with(|s| {
202 let mut addr = [0u8; 20];
203 unsafe {
204 copy_nonoverlapping(addr_, addr.as_mut_ptr(), 20);
205 }
206 let b = s.borrow();
207 let h = match b.get(&addr) {
208 None => EMPTY_HASH,
209 Some(b) if b.len() == 0 => EMPTY_HASH,
210 Some(b) => keccak256(&b),
211 };
212 unsafe {
213 copy_nonoverlapping(h.as_ptr(), out, 32);
214 }
215 })
216 }
217
218 pub fn set_block_timestamp(n: u64) {
219 BLOCK_TIMESTAMP.with(|s| *s.borrow_mut() = n)
220 }
221
222 pub(crate) unsafe fn block_timestamp() -> u64 {
223 BLOCK_TIMESTAMP.with(|s| *s.borrow())
224 }
225
226 pub(crate) unsafe fn block_basefee(out: *mut u8) {}
227
228 pub(crate) unsafe fn evm_gas_left() -> u64 {
229 0
230 }
231
232 pub(crate) fn evm_ink_left() -> u64 {
233 0
234 }
235}
236
237#[cfg(all(
238 not(all(target_family = "wasm", target_os = "unknown")),
239 feature = "std"
240))]
241pub use entry_host as impls;
242
243#[cfg(all(
244 not(all(target_family = "wasm", target_os = "unknown")),
245 not(feature = "std")
246))]
247mod impls {
248 pub(crate) fn account_balance(_: *const u8, _: *mut u8) {}
249
250 #[allow(unused)]
251 pub(crate) unsafe fn pay_for_memory_grow(_: u16) {}
252
253 pub(crate) unsafe fn write_result(_: *const u8, _: usize) {}
254
255 pub(crate) unsafe fn return_data_size() -> usize { 0 }
256
257 pub(crate) unsafe fn read_args(_out: *mut u8) {}
258
259 pub(crate) unsafe fn msg_sender(_: *mut u8) {}
260
261 pub(crate) unsafe fn contract_address(_: *mut u8) {}
262
263 pub(crate) unsafe fn msg_value(_: *mut u8) {}
264
265 pub(crate) unsafe fn chainid() -> u64 {
266 0
267 }
268
269 pub unsafe fn account_code_size(_: *const u8) -> usize {
270 0
271 }
272
273 pub(crate) unsafe fn account_code(_: *const u8, _: usize, _: usize, _: *mut u8) -> usize {
274 0
275 }
276
277 pub(crate) unsafe fn account_codehash(_: *const u8, _: *mut u8) {}
278
279 pub(crate) unsafe fn block_timestamp() -> u64 {
280 0
281 }
282
283 pub(crate) unsafe fn block_basefee(out: *mut u8) {}
284
285 pub(crate) unsafe fn evm_gas_left() -> u64 {
286 0
287 }
288 pub(crate) fn evm_ink_left() -> u64 {
289 0
290 }
291}
292
293pub fn balance(addr: Address) -> U {
294 let mut out = U::ZERO;
295 unsafe { impls::account_balance(addr.as_ptr(), out.as_mut_ptr()) }
296 out
297}
298
299#[unsafe(no_mangle)]
300#[cfg(all(
301 target_family = "wasm",
302 target_os = "unknown",
303 not(feature = "dont-define-symbols")
304))]
305pub unsafe fn mark_used() {
306 unsafe { impls::pay_for_memory_grow(0) }
307 panic!();
308}
309
310pub fn write_result_slice(s: &[u8]) {
311 unsafe { impls::write_result(s.as_ptr(), s.len()) }
312}
313
314pub fn write_result_word(s: &U) {
315 write_result_slice(&s.0)
316}
317
318pub fn write_result_bool(v: bool) {
319 write_result_slice(&U::from(v).0)
320}
321
322pub fn return_data_size() -> usize {
323 unsafe { impls::return_data_size() }
324}
325
326pub use bobcat_cd::leftpad_addr;
327
328#[macro_export]
332macro_rules! revert_if_bad_call_vec {
333 ($e:expr) => {{
334 let (rc, rd) = $e;
335 if !rc {
336 $crate::write_result_slice(&rd);
337 return 1;
338 }
339 rd
340 }};
341}
342
343#[macro_export]
346macro_rules! revert_if_bad_call_unit_vec {
347 ($e:expr) => {{
348 let (rc, revertdata) = $e;
349 match (rc, revertdata) {
350 (true, _) => (),
351 (false, Some(v)) => {
352 $crate::write_result_slice(&v);
353 return 1;
354 }
355 (false, _) => return 1,
356 }
357 }};
358}
359
360#[macro_export]
362macro_rules! revert_if_bad_call_slice_vec {
363 ($e:expr) => {{
364 let (rc, returndata, revertdata) = $e;
365 match (rc, revertdata) {
366 (true, _) => returndata,
367 (false, Some(v)) => {
368 $crate::write_result_slice(&v);
369 return 1;
370 }
371 (false, _) => return 1,
372 }
373 }};
374}
375
376#[macro_export]
377macro_rules! write_result_exit_res {
378 ($ident:expr) => {{
379 match $ident {
380 Ok(v) => {
381 $crate::write_result_slice(&v);
382 0
383 }
384 Err(v) => {
385 $crate::write_result_slice(&v);
386 1
387 }
388 }
389 }};
390}
391
392#[macro_export]
393macro_rules! write_result_exit_create {
394 ($ident:expr) => {{
395 let (addr, b, i) = $ident;
396 if addr != [0u8; 20] {
397 $crate::write_result_slice(&leftpad_addr(addr));
398 0
399 } else {
400 $crate::write_result_slice(&b[..i]);
401 1
402 }
403 }};
404}
405
406#[macro_export]
407macro_rules! write_result_exit_call {
408 ($ident:expr) => {{
409 let (rc, l, v) = $ident;
410 $crate::write_result_slice(&v[..l]);
411 if rc {
412 0
413 } else {
414 1
415 }
416 }};
417}
418
419pub fn read_args<const CAP: usize>(len: usize) -> ([u8; CAP], usize) {
420 assert!(CAP >= len, "cap not enough");
421 let mut b = [0u8; CAP];
422 unsafe { impls::read_args(b.as_mut_ptr()) };
423 (b, len)
424}
425
426#[macro_export]
427macro_rules! read_args_safe {
428 ($len:expr, $max_len:expr) => {{
429 assert!($max_len >= $len, "{} < {}", $max_len, $len);
430 $crate::read_args::<$max_len>($len).0
431 }};
432}
433
434#[cfg(feature = "alloc")]
435pub fn read_args_vec(len: usize) -> Vec<u8> {
436 let mut b = Vec::with_capacity(len);
437 unsafe {
438 impls::read_args(b.as_mut_ptr());
439 b.set_len(len);
440 };
441 b
442}
443
444pub fn msg_sender() -> Address {
445 let mut b = [0u8; 20];
446 unsafe { impls::msg_sender(b.as_mut_ptr()) }
447 b
448}
449
450pub fn contract_address() -> Address {
451 let mut b = [0u8; 20];
452 unsafe { impls::contract_address(b.as_mut_ptr()) }
453 b
454}
455
456pub fn msg_value() -> U {
457 let mut b = [0u8; 32];
458 unsafe { impls::msg_value(b.as_mut_ptr()) }
459 U(b)
460}
461
462pub fn code_size(addr: Address) -> usize {
463 unsafe { impls::account_code_size(addr.as_ptr()) }
464}
465
466pub fn code_slice<const CAP: usize>(
467 addr: Address,
468 size: usize,
469 offset: usize,
470) -> ([u8; CAP], usize) {
471 let mut b = [0u8; CAP];
472 assert!(CAP >= size, "not enough size: {size}, capacity: {CAP}");
473 let rd = unsafe { impls::account_code(addr.as_ptr(), offset, size, b.as_mut_ptr()) };
474 (b, rd)
475}
476
477#[cfg(feature = "alloc")]
478pub fn code_vec_size(addr: Address, offset: usize, size: usize) -> Vec<u8> {
479 let mut b = Vec::with_capacity(size);
480 let rd = unsafe { impls::account_code(addr.as_ptr(), offset, size, b.as_mut_ptr()) };
481 unsafe { b.set_len(rd) };
482 b
483}
484
485#[cfg(feature = "alloc")]
486pub fn code_vec(addr: Address, offset: usize) -> Vec<u8> {
487 code_vec_size(addr, offset, code_size(addr))
488}
489
490pub fn code_hash(addr: Address) -> U {
491 let mut b = U::ZERO;
492 unsafe { impls::account_codehash(addr.as_ptr(), b.as_mut_ptr()) };
493 b
494}
495
496pub fn chain_id() -> u64 {
497 unsafe { impls::chainid() }
498}
499
500pub fn block_timestamp() -> u64 {
501 unsafe { impls::block_timestamp() }
502}
503
504pub fn block_basefee() -> U {
505 let mut out = U::ZERO;
506 unsafe { impls::block_basefee(out.as_mut_ptr()) }
507 out
508}
509
510pub fn evm_gas_left() -> u64 {
511 unsafe { impls::evm_gas_left() }
512}
513
514pub fn evm_ink_left() -> u64 {
515 unsafe { impls::evm_ink_left() }
516}