#![no_std]
#![allow(clippy::unwrap_used)]
extern crate alloc;
pub use alloc::{vec, vec::Vec};
pub use core::alloc::Layout;
use jam_types::*;
use scale::Output;
#[cfg(feature = "authorizer")]
mod authorizer;
#[cfg(feature = "authorizer")]
pub use authorizer::Authorizer;
#[cfg(feature = "service")]
mod service;
#[cfg(feature = "service")]
pub use service::Service;
#[cfg(feature = "service")]
pub mod refine;
#[cfg(feature = "service")]
pub mod accumulate;
#[cfg(feature = "service")]
pub use accumulate as on_transfer;
pub(crate) mod imports;
#[macro_export]
macro_rules! error {
(target=$target:expr,$($arg:tt)*) => {
$crate::log_target(0, $target, &alloc::format!($($arg)*));
};
($($arg:tt)*) => {
$crate::log(0, &alloc::format!($($arg)*));
};
}
#[macro_export]
macro_rules! warn {
(target=$target:expr,$($arg:tt)*) => {
$crate::log_target(1, $target, &alloc::format!($($arg)*));
};
($($arg:tt)*) => {
$crate::log(1, &alloc::format!($($arg)*));
};
}
#[macro_export]
macro_rules! info {
(target=$target:expr,$($arg:tt)*) => {
$crate::log_target(2, $target, &alloc::format!($($arg)*));
};
($($arg:tt)*) => {
$crate::log(2, &alloc::format!($($arg)*));
};
}
#[macro_export]
macro_rules! debug {
(target=$target:expr,$($arg:tt)*) => {
$crate::log_target(3, $target, &alloc::format!($($arg)*));
};
($($arg:tt)*) => {
$crate::log(3, &alloc::format!($($arg)*));
};
}
#[macro_export]
macro_rules! trace {
(target=$target:expr,$($arg:tt)*) => {
$crate::log_target(4, $target, &alloc::format!($($arg)*));
};
($($arg:tt)*) => {
$crate::log(4, &alloc::format!($($arg)*));
};
}
pub fn log_target(level: u32, target: &str, msg: &str) {
let t = target.as_bytes();
let m = msg.as_bytes();
unsafe { imports::log(level, t.as_ptr(), t.len() as u32, m.as_ptr(), m.len() as u32) }
}
pub fn log(level: u32, msg: &str) {
let m = msg.as_bytes();
unsafe { imports::log(level, core::ptr::null(), 0u32, m.as_ptr(), m.len() as u32) }
}
pub fn gas() -> Gas {
unsafe { imports::gas() }
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#[global_allocator]
static ALLOCATOR: polkavm_derive::LeakingAllocator = polkavm_derive::LeakingAllocator;
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unsafe {
core::arch::asm!("unimp", options(noreturn));
}
}
pub fn alloc(size: u32) -> u32 {
let ptr = unsafe { alloc::alloc::alloc(Layout::from_size_align(size as usize, 4).unwrap()) };
ptr as u32
}
pub fn dealloc(ptr: u32, size: u32) {
unsafe {
alloc::alloc::dealloc(ptr as *mut u8, Layout::from_size_align(size as usize, 4).unwrap())
};
}
pub struct BufferOutput<'a>(&'a mut [u8], usize);
impl<'a> Output for BufferOutput<'a> {
fn write(&mut self, bytes: &[u8]) {
let (_, rest) = self.0.split_at_mut(self.1);
let len = bytes.len().min(rest.len());
rest[..len].copy_from_slice(&bytes[..len]);
self.1 += len;
}
}
pub fn decode_buf<T: Decode>(ptr: u32, size: u32) -> T {
let slice = unsafe { core::slice::from_raw_parts(ptr as *const u8, size as usize) };
let params = T::decode(&mut &slice[..]);
dealloc(ptr, size);
params.unwrap()
}
pub fn encode_to_buf<T: Encode>(value: T) -> (u32, u32) {
let size = value.encoded_size();
let ptr = alloc(size as u32);
let slice = unsafe { core::slice::from_raw_parts_mut(ptr as *mut u8, size) };
value.encode_to(&mut BufferOutput(&mut slice[..], 0));
(ptr, size as u32)
}
#[derive(Debug)]
pub enum ApiError {
OutOfBounds,
IndexUnknown,
StorageFull,
BadCore,
NoCash,
GasLimitTooLow,
GasLimitTooHigh,
ActionInvalid,
}
impl From<u32> for ApiError {
fn from(code: SimpleResult) -> Self {
match code {
c if c == SimpleResultCode::OutOfBounds as u32 => ApiError::OutOfBounds,
c if c == SimpleResultCode::IndexUnknown as u32 => ApiError::IndexUnknown,
c if c == SimpleResultCode::StorageFull as u32 => ApiError::StorageFull,
c if c == SimpleResultCode::BadCore as u32 => ApiError::BadCore,
c if c == SimpleResultCode::NoCash as u32 => ApiError::NoCash,
c if c == SimpleResultCode::GasLimitTooLow as u32 => ApiError::GasLimitTooLow,
c if c == SimpleResultCode::GasLimitTooHigh as u32 => ApiError::GasLimitTooHigh,
c if c == SimpleResultCode::ActionInvalid as u32 => ApiError::ActionInvalid,
_ => panic!("unknown error code: {}", code),
}
}
}
pub trait IntoResult<T> {
fn into_result(self) -> Result<T, ApiError>;
}
impl IntoResult<()> for SimpleResult {
fn into_result(self) -> Result<(), ApiError> {
if self == SimpleResultCode::Ok as u32 {
Ok(())
} else {
Err(self.into())
}
}
}
impl IntoResult<u32> for SimpleResult {
fn into_result(self) -> Result<u32, ApiError> {
if self < LOWEST_ERROR {
Ok(self)
} else {
Err(self.into())
}
}
}
impl IntoResult<Option<()>> for SimpleResult {
fn into_result(self) -> Result<Option<()>, ApiError> {
if self < LOWEST_ERROR {
Ok(Some(()))
} else if self == SimpleResultCode::Nothing as u32 {
Ok(None)
} else {
Err(self.into())
}
}
}
impl IntoResult<Option<u32>> for SimpleResult {
fn into_result(self) -> Result<Option<u32>, ApiError> {
if self < LOWEST_ERROR {
Ok(Some(self))
} else if self == SimpleResultCode::Nothing as u32 {
Ok(None)
} else {
Err(self.into())
}
}
}
pub enum InnerResult {
Halt,
PageFault(u32),
HostCallFault(u32),
}
#[derive(Debug)]
pub enum InnerError {
Panic,
OutOfGas,
ApiError(ApiError),
}
impl From<ApiError> for InnerError {
fn from(e: ApiError) -> Self {
Self::ApiError(e)
}
}
pub trait IntoInnerResult {
fn into_inner_result(self) -> Result<InnerResult, InnerError>;
}
impl IntoInnerResult for u64 {
fn into_inner_result(self) -> Result<InnerResult, InnerError> {
const STATUS_HALT: u32 = InnerResultCode::Halt as u32;
const STATUS_PANIC: u32 = InnerResultCode::Panic as u32;
const STATUS_FAULT: u32 = InnerResultCode::PageFault as u32;
const STATUS_HOST: u32 = InnerResultCode::HostCallFault as u32;
const STATUS_OOG: u32 = InnerResultCode::OutOfGas as u32;
let a0 = self as u32;
let a1 = (self >> 32) as u32;
match (a0, a1) {
(STATUS_HALT, _) => Ok(InnerResult::Halt),
(STATUS_FAULT, address) => Ok(InnerResult::PageFault(address)),
(STATUS_HOST, index) => Ok(InnerResult::HostCallFault(index)),
(STATUS_PANIC, _) => Err(InnerError::Panic),
(STATUS_OOG, _) => Err(InnerError::OutOfGas),
(code, _) => Err(InnerError::ApiError(code.into())),
}
}
}