#![no_std]
#![allow(clippy::unwrap_used)]
extern crate alloc;
#[doc(hidden)]
pub use alloc::{vec, vec::Vec};
#[doc(hidden)]
pub use core::alloc::Layout;
use jam_types::*;
use scale::Output;
use simple_result_code::InvokeOutcomeCode;
#[cfg(any(feature = "authorizer", doc))]
mod authorizer;
#[cfg(any(feature = "authorizer", doc))]
pub use authorizer::Authorizer;
#[cfg(any(feature = "service", doc))]
mod service;
#[cfg(any(feature = "service", doc))]
pub use service::Service;
#[cfg(any(feature = "service", doc))]
pub mod refine;
#[cfg(any(feature = "service", doc))]
pub mod accumulate;
#[cfg(feature = "service")]
pub use accumulate as on_transfer;
pub(crate) mod imports;
#[cfg(any(feature = "logging", doc))]
#[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)*));
};
}
#[cfg(any(feature = "logging", doc))]
#[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)*));
};
}
#[cfg(any(feature = "logging", doc))]
#[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)*));
};
}
#[cfg(any(feature = "logging", doc))]
#[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)*));
};
}
#[cfg(any(feature = "logging", doc))]
#[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)*));
};
}
#[cfg(not(any(feature = "logging", doc)))]
#[macro_export]
macro_rules! error {
($($arg:tt)*) => {
()
};
}
#[cfg(not(any(feature = "logging", doc)))]
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => {
()
};
}
#[cfg(not(any(feature = "logging", doc)))]
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {
()
};
}
#[cfg(not(any(feature = "logging", doc)))]
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {
()
};
}
#[cfg(not(any(feature = "logging", doc)))]
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => {
()
};
}
#[cfg(any(feature = "logging", doc))]
#[doc(hidden)]
pub fn log_target(level: u64, target: &str, msg: &str) {
let t = target.as_bytes();
let m = msg.as_bytes();
unsafe { imports::log(level, t.as_ptr(), t.len() as u64, m.as_ptr(), m.len() as u64) }
}
#[cfg(any(feature = "logging", doc))]
#[doc(hidden)]
pub fn log(level: u64, msg: &str) {
let m = msg.as_bytes();
unsafe { imports::log(level, core::ptr::null(), 0u64, m.as_ptr(), m.len() as u64) }
}
#[cfg(not(any(feature = "logging", doc)))]
pub fn log_target(_: u64, _: &str, _: &str) {}
#[cfg(not(any(feature = "logging", doc)))]
pub fn log(_: u64, _: &str) {}
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));
}
}
#[doc(hidden)]
pub fn alloc(size: u32) -> u32 {
let ptr = unsafe { alloc::alloc::alloc(Layout::from_size_align(size as usize, 4).unwrap()) };
ptr as u32
}
#[doc(hidden)]
pub fn dealloc(ptr: u32, size: u32) {
unsafe {
alloc::alloc::dealloc(ptr as *mut u8, Layout::from_size_align(size as usize, 4).unwrap())
};
}
#[doc(hidden)]
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;
}
}
#[doc(hidden)]
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()
}
#[doc(hidden)]
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<u64> for ApiError {
fn from(code: SimpleResult) -> Self {
match code {
c if c == SimpleResultCode::OutOfBounds as u64 => ApiError::OutOfBounds,
c if c == SimpleResultCode::IndexUnknown as u64 => ApiError::IndexUnknown,
c if c == SimpleResultCode::StorageFull as u64 => ApiError::StorageFull,
c if c == SimpleResultCode::BadCore as u64 => ApiError::BadCore,
c if c == SimpleResultCode::NoCash as u64 => ApiError::NoCash,
c if c == SimpleResultCode::GasLimitTooLow as u64 => ApiError::GasLimitTooLow,
c if c == SimpleResultCode::GasLimitTooHigh as u64 => ApiError::GasLimitTooHigh,
c if c == SimpleResultCode::ActionInvalid as u64 => ApiError::ActionInvalid,
_ => panic!("unknown error code: {}", code),
}
}
}
pub type ApiResult<T> = Result<T, ApiError>;
pub trait IntoApiResult<T> {
fn into_api_result(self) -> ApiResult<T>;
}
impl IntoApiResult<()> for SimpleResult {
fn into_api_result(self) -> ApiResult<()> {
if self == SimpleResultCode::Ok as u64 {
Ok(())
} else {
Err(self.into())
}
}
}
impl IntoApiResult<u64> for SimpleResult {
fn into_api_result(self) -> ApiResult<u64> {
if self < LOWEST_ERROR {
Ok(self)
} else {
Err(self.into())
}
}
}
impl IntoApiResult<u32> for SimpleResult {
fn into_api_result(self) -> ApiResult<u32> {
if self <= u32::MAX as _ {
Ok(self as u32)
} else if self < LOWEST_ERROR {
panic!("Our own API impl has resulted in success value which is out of range.");
} else {
Err(self.into())
}
}
}
impl IntoApiResult<Option<()>> for SimpleResult {
fn into_api_result(self) -> ApiResult<Option<()>> {
if self < LOWEST_ERROR {
Ok(Some(()))
} else if self == SimpleResultCode::Nothing as u64 {
Ok(None)
} else {
Err(self.into())
}
}
}
impl IntoApiResult<Option<u64>> for SimpleResult {
fn into_api_result(self) -> ApiResult<Option<u64>> {
if self < LOWEST_ERROR {
Ok(Some(self))
} else if self == SimpleResultCode::Nothing as u64 {
Ok(None)
} else {
Err(self.into())
}
}
}
pub enum InvokeOutcome {
Halt,
PageFault(u64),
HostCallFault(u64),
Panic,
OutOfGas,
}
pub type InvokeResult = ApiResult<InvokeOutcome>;
pub trait IntoInvokeResult {
fn into_invoke_result(self) -> InvokeResult;
}
impl IntoInvokeResult for [u64; 2] {
fn into_invoke_result(self) -> InvokeResult {
const STATUS_HALT: u64 = InvokeOutcomeCode::Halt as u64;
const STATUS_PANIC: u64 = InvokeOutcomeCode::Panic as u64;
const STATUS_FAULT: u64 = InvokeOutcomeCode::PageFault as u64;
const STATUS_HOST: u64 = InvokeOutcomeCode::HostCallFault as u64;
const STATUS_OOG: u64 = InvokeOutcomeCode::OutOfGas as u64;
match self {
[STATUS_HALT, _] => Ok(InvokeOutcome::Halt),
[STATUS_FAULT, address] => Ok(InvokeOutcome::PageFault(address)),
[STATUS_HOST, index] => Ok(InvokeOutcome::HostCallFault(index)),
[STATUS_PANIC, _] => Ok(InvokeOutcome::Panic),
[STATUS_OOG, _] => Ok(InvokeOutcome::OutOfGas),
[code, _] => Err(code.into()),
}
}
}
impl IntoInvokeResult for SimpleResult {
fn into_invoke_result(self) -> InvokeResult {
const STATUS_HALT: u64 = InvokeOutcomeCode::Halt as u64;
const STATUS_PANIC: u64 = InvokeOutcomeCode::Panic as u64;
const STATUS_FAULT: u64 = InvokeOutcomeCode::PageFault as u64;
const STATUS_HOST: u64 = InvokeOutcomeCode::HostCallFault as u64;
const STATUS_OOG: u64 = InvokeOutcomeCode::OutOfGas as u64;
match (self % (1 << 32), self / (1 << 32)) {
(STATUS_HALT, _) => Ok(InvokeOutcome::Halt),
(STATUS_FAULT, address) => Ok(InvokeOutcome::PageFault(address)),
(STATUS_HOST, index) => Ok(InvokeOutcome::HostCallFault(index)),
(STATUS_PANIC, _) => Ok(InvokeOutcome::Panic),
(STATUS_OOG, _) => Ok(InvokeOutcome::OutOfGas),
(code, _) => Err(code.into()),
}
}
}