mantle 0.2.1

A humane programming model for blockchain.
use mantle_types::{Address, ExtStatusCode};

use super::Error;

/// @see the `blockchain-traits` crate for descriptions of these methods.
extern "C" {
    pub fn mantle_balance(addr: *const Address, balance: *mut u64) -> ExtStatusCode;

    pub fn mantle_code(addr: *const Address, buf: *mut u8) -> ExtStatusCode;
    pub fn mantle_code_len(addr: *const Address, len: *mut u32) -> ExtStatusCode;

    pub fn mantle_fetch_input(buf: *mut u8) -> ExtStatusCode;
    pub fn mantle_input_len(len: *mut u32) -> ExtStatusCode;

    pub fn mantle_ret(buf: *const u8, len: u32) -> ExtStatusCode;
    pub fn mantle_err(buf: *const u8, len: u32) -> ExtStatusCode;

    pub fn mantle_fetch_ret(buf: *mut u8) -> ExtStatusCode;
    pub fn mantle_ret_len(len: *mut u32) -> ExtStatusCode;

    pub fn mantle_fetch_err(buf: *mut u8) -> ExtStatusCode;
    pub fn mantle_err_len(len: *mut u32) -> ExtStatusCode;

    pub fn mantle_transact(
        callee: *const Address,
        value: u64,
        input: *const u8,
        input_len: u32,
    ) -> ExtStatusCode;

    pub fn mantle_address(addr: *mut Address) -> ExtStatusCode;
    pub fn mantle_sender(addr: *mut Address) -> ExtStatusCode;
    pub fn mantle_payer(addr: *mut Address) -> ExtStatusCode;
    pub fn mantle_value(value: *mut u64) -> ExtStatusCode;

    pub fn mantle_read(key: *const u8, key_len: u32, value: *mut u8) -> ExtStatusCode;
    pub fn mantle_read_len(key: *const u8, key_len: u32, value_len: *mut u32) -> ExtStatusCode;
    pub fn mantle_write(
        key: *const u8,
        key_len: u32,
        value: *const u8,
        value_len: u32,
    ) -> ExtStatusCode;

    pub fn mantle_emit(
        topics: *const *const u8,
        topic_lens: *const u32,
        num_topics: u32,
        data: *const u8,
        data_len: u32,
    ) -> ExtStatusCode;
}

impl From<ExtStatusCode> for Error {
    fn from(code: ExtStatusCode) -> Self {
        match code {
            ExtStatusCode::Success => unreachable!(),
            ExtStatusCode::InsufficientFunds => Error::InsufficientFunds,
            ExtStatusCode::InvalidInput => Error::InvalidInput,
            ExtStatusCode::NoAccount => Error::NoAccount,
            code if code.0 <= u32::from(u8::max_value()) => Error::Unknown,
            code => Error::Execution {
                code: code.0,
                payload: fetch_err(),
            },
        }
    }
}

macro_rules! ext {
    ($fn:ident $args:tt ) => {{
        let code = unsafe { $fn$args };
        if code != ExtStatusCode::Success {
            Err(Error::from(code))
        } else {
            Ok(())
        }
    }};
}

pub fn address() -> Address {
    let mut addr = Address::default();
    ext!(mantle_address(&mut addr as *mut _)).unwrap();
    addr
}

pub fn sender() -> Address {
    let mut addr = Address::default();
    ext!(mantle_sender(&mut addr as *mut _)).unwrap();
    addr
}

pub fn payer() -> Address {
    let mut addr = Address::default();
    ext!(mantle_payer(&mut addr as *mut _)).unwrap();
    addr
}

pub fn value() -> u64 {
    let mut value = 0;
    ext!(mantle_value(&mut value as *mut _)).unwrap();
    value
}

pub fn balance(addr: &Address) -> Option<u64> {
    let mut balance = 0;
    ext!(mantle_balance(addr as *const _, &mut balance as *mut _))
        .ok()
        .map(|_| balance)
}

pub fn code(addr: &Address) -> Option<Vec<u8>> {
    let mut code_len = 0u32;
    let mut code = Vec::with_capacity(
        match ext!(mantle_code_len(
            addr as *const Address,
            &mut code_len as *mut _
        )) {
            Ok(_) => code_len as usize,
            Err(_) => return None,
        },
    );
    ext!(mantle_code(addr as *const Address, code.as_mut_ptr()))
        .ok()
        .map(|_| code)
}

pub fn transact(callee: &Address, value: u64, input: &[u8]) -> Result<Vec<u8>, Error> {
    ext!(mantle_transact(
        callee as *const _,
        value,
        input.as_ptr(),
        if input.len() > u32::max_value() as usize {
            return Err(Error::InvalidInput);
        } else {
            input.len() as u32
        },
    ))?;

    let mut ret_len = 0u32;
    ext!(mantle_ret_len(&mut ret_len as *mut _))?;

    let mut ret = Vec::with_capacity(ret_len as usize);
    unsafe { ret.set_len(ret_len as usize) };

    ext!(mantle_fetch_ret(ret.as_mut_ptr())).map(|_| ret)
}

pub fn input() -> Vec<u8> {
    let mut input_len = 0u32;
    ext!(mantle_input_len(&mut input_len as *mut _)).unwrap();

    let mut input = Vec::with_capacity(input_len as usize);
    unsafe { input.set_len(input_len as usize) };

    ext!(mantle_fetch_input(input.as_mut_ptr())).unwrap();
    input
}

pub fn ret(ret: &[u8]) -> ! {
    ext!(mantle_ret(ret.as_ptr(), ret.len() as u32)).unwrap();
    std::process::abort();
}

pub fn err(err: &[u8]) -> ! {
    ext!(mantle_err(err.as_ptr(), err.len() as u32)).unwrap();
    std::process::abort();
}

pub fn fetch_err() -> Vec<u8> {
    let mut err_len = 0u32;
    ext!(mantle_err_len(&mut err_len as *mut _)).unwrap();

    let mut err = Vec::with_capacity(err_len as usize);
    unsafe { err.set_len(err_len as usize) };

    ext!(mantle_fetch_err(err.as_mut_ptr())).unwrap();
    err
}

pub fn read(key: &[u8]) -> Vec<u8> {
    let mut val_len = 0u32;
    ext!(mantle_read_len(
        key.as_ptr(),
        key.len() as u32,
        &mut val_len as *mut _
    ))
    .unwrap();

    let mut val = Vec::with_capacity(val_len as usize);
    unsafe { val.set_len(val_len as usize) };

    ext!(mantle_read(
        key.as_ptr(),
        key.len() as u32,
        val.as_mut_ptr()
    ))
    .unwrap();
    val
}

pub fn write(key: &[u8], value: &[u8]) {
    ext!(mantle_write(
        key.as_ptr(),
        key.len() as u32,
        value.as_ptr(),
        value.len() as u32
    ))
    .unwrap();
}

pub fn emit(topics: &[&[u8]], data: &[u8]) {
    let topic_ptrs: Vec<*const u8> = topics.iter().map(|t| t.as_ptr()).collect();
    let topic_lens: Vec<u32> = topics.iter().map(|t| t.len() as u32).collect();
    ext!(mantle_emit(
        topic_ptrs.as_ptr(),
        topic_lens.as_ptr(),
        topics.len() as u32,
        data.as_ptr(),
        data.len() as u32
    ))
    .unwrap();
}