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