sbpf-runtime 0.1.9

Lightweight runtime for sBPF programs
Documentation
use {
    crate::{config::ExecutionCost, cpi::ReturnData},
    sbpf_vm::{compute::ComputeMeter, errors::SbpfVmResult, memory::Memory},
    solana_address::Address,
};

const MAX_RETURN_DATA: usize = 1024;

pub fn sol_set_return_data(
    registers: [u64; 5],
    memory: &Memory,
    compute: &ComputeMeter,
    costs: &ExecutionCost,
    program_id: &Address,
) -> SbpfVmResult<(u64, ReturnData)> {
    let addr = registers[0];
    let len = registers[1];

    let cost = len
        .checked_div(costs.cpi_bytes_per_unit)
        .unwrap_or(u64::MAX)
        .saturating_add(costs.syscall_base_cost);
    compute.consume(cost)?;

    if len > MAX_RETURN_DATA as u64 {
        return Err(sbpf_vm::errors::SbpfVmError::SyscallError(format!(
            "Return data too large: {} > {}",
            len, MAX_RETURN_DATA
        )));
    }

    let data = if len == 0 {
        Vec::new()
    } else {
        memory.read_bytes(addr, len as usize)?.to_vec()
    };

    Ok((0, Some((*program_id, data))))
}

pub fn sol_get_return_data(
    registers: [u64; 5],
    memory: &mut Memory,
    compute: &ComputeMeter,
    costs: &ExecutionCost,
    return_data: &ReturnData,
) -> SbpfVmResult<u64> {
    let buf_addr = registers[0];
    let buf_len = registers[1];
    let program_id_addr = registers[2];

    compute.consume(costs.syscall_base_cost)?;

    let Some((program_id, data)) = return_data else {
        return Ok(0);
    };

    let length = buf_len.min(data.len() as u64);

    if length != 0 {
        let cost = length
            .saturating_add(32)
            .checked_div(costs.cpi_bytes_per_unit)
            .unwrap_or(u64::MAX);
        compute.consume(cost)?;

        let from_slice = &data[..length as usize];
        memory.write_bytes(buf_addr, from_slice)?;
        memory.write_bytes(program_id_addr, program_id.as_ref())?;
    }

    Ok(data.len() as u64)
}