bobcat_entry/
lib.rs

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/// Like write_result_exit_call, except it only reverts with the
329/// returndata if the underlying call reverted. If it doesn't, then it
330/// just returns the slice.
331#[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/// Reverts if the underlying call failed, using the vector that was
344/// returned as the third argument as slice.
345#[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/// Reverts with a message if the revertdata is Some, and if the rc is false.
361#[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}