# SVM Series 03: Compute Budget & Syscalls

The SVM requires compute budget limits and syscalls that programs can call to perform certain tasks.

## Compute Budget

### What is the Compute Budget & Compute Budget Program?

The compute budget defines the maximum compute units (CU) a transaction can consume to prevent resource abuse on the Solana network. The default CUs are 200,000 with a maximum limit of 1.4 million CUs. During program execution, the SVM breaks the transaction logic into [opcodes](https://github.com/anza-xyz/sbpf/blob/main/doc/bytecode.md) (BPF instructions) where the VM consumes 1 CU per opcode. Examples of operations that are converted to opcodes are program syscalls, signature verification, locking instructions during transaction sanitization, etc. The compute budget program `ComputeBudget111111111111111111111111111111` handles requesting more CUs for program execution if the default CUs are not enough.

### To use the Compute Budget in the SVM API

```bash
cargo add solana-compute-budget
```

```rust
fn main() {
    // Initialize `ComputeBudget` struct with default paramemters.
    // These parameters include:
    //    - compute unit limit (defaults to 1.4 million CUs)
    //    - Maximum nesting of instructions that can happen during a transaction (defaults to 5)
    //    - Maximum cross-program invocation and instructions per transaction (defaults to 64)
    //    - Maximum number of slices hashed per syscall (defaults to 20,000)
    //    - Maximum SBF to BPF call nesting that can happen within a program (defaults to 64 SBF to BPF to BPF calls)
    //    - Size of a stack frame in bytes matching the size specified in the LLVM SBF backend (defaults to 4096)
    //    - Maximum cross-program invocation instruction size (default to IPv6 Min MTU size of 1280)
    //    - Length of the heap memory region used for program heap 
    //      (defaults to solana_program_entrypoint::HEAP_LENGTH of 32768 = 1024 * 32) 
    //    - Loads the default execution costs defined by `solana_program_runtime::execution_budget::SVMTransactionExecutionCost` struct
    //      which is defines cost paramemters for operations like loggin, CPI, signature verification and more. 
    let compute_budget_config = ComputeBudget::default();    
}
```

The `SVMTransactionExecutionCost` source code parameters are defined at [Solana Program Runtime Modeule #L90](https://github.com/anza-xyz/agave/blob/86bc9187b3d8988abfa52572c988a11ca7fcc931/program-runtime/src/execution_budget.rs#L90).

## Syscalls

### What are syscalls?

In SVM, a syscall (short for system call) is a low-level function provided by the Solana VM that programs can invoke to interact with the VM environment. Syscalls are predefined entrypoints into the runtime that ensure only specific actions are allowed during program execution. These operations that are not part of the program itself but are offered by the VM as trusted privileged operations. Examples of a syscall is verifying Ed25519 signatures or logging information.

### Configuration parameters used in syscalls parameters

Defining syscalls in SVM requires some configuration provided by the [`solana_sbpf::vm::Config`](https://docs.rs/solana-sbpf/latest/solana_sbpf/vm/struct.Config.html). This configures VM parameters like maximum call depth, stack frame size, copying read-only data, tracing instructions, which SBF versions are allowed and more.

The [solana-sbpf](https://crates.io/crates/solana-sbpf) Rust crate is used to define syscalls and their configuration parameters. An example of this:

```bash
# add `solana-program` to your dependencies
# it contains `solana-sbpf` crate used to configure the VM
cargo add solana-program-runtime 

cargo add solana-bpf-loader-program # Contains the syscalls source code to configure the VM
```

In the `src/main.rs` file part of the `Compute Budget` code above.

```rust
use solana_program_runtime::{
    // `InvokeContext` is used to configure the entire exeuction pipeline
    invoke_context::InvokeContext,
    solana_sbpf::{
        program::BuiltinProgram,
        // Rename from `Config` to `SbpfVmConfig` to avoid conflicts with other deps with structs called `Config`
        vm::Config as SbpfVmConfig,
    },
};

    // ... previous code from above

    // Defining syscalls requires some configuration using `solana_sbpf::vm::Config`.
    // This configures maximum call depth, stack frame size, copying read only data, tracing instructions,
    // allowed SBF versions, sanitizing user provided values and many more
    let vm_config = SbpfVmConfig {
        // configure maximum call depth using that of our compute budget `compute_budget_config`
        max_call_depth: compute_budget_config.max_call_depth,
        // configure stack frame size using that of our compute budget `compute_budget_config`
        stack_frame_size: compute_budget_config.stack_frame_size,
        // Enable instruction tracing
        enable_instruction_tracing: true,
        // enable symbol and section labels for BPF (disabled by default)
        enable_symbol_and_section_labels: true,
        // Reject ELF files containing issues that the verifier did not catch before (up to v0.2.21). Disabled by default
        reject_broken_elfs: true,
        // Avoid copying read only sections when possible. Enabled by default
        optimize_rodata: false,
        // Use all other default parameters
        ..Default::default()
    };
```

We can now specify the syscalls the compiled program can call during execution using [`solana_sbf::program::BuiltinProgram`](https://docs.rs/solana-sbpf/latest/solana_sbpf/program/struct.BuiltinProgram.html#method.register_function) Rust crate. The SVM configuration defined above as `vm_config` is used to configure the VM. The `BuiltinProgram` takes a generic parameter, in our case [`InvokeContext`](https://docs.rs/solana-program-runtime/latest/solana_program_runtime/invoke_context/struct.InvokeContext.html) which defines the main pipeline from runtime to program execution.

```rust
   
    // The SVM configuration `vm_config` defined above is used  to configure the VM.
    // The `BuiltinProgram` takes a generic parameter, in our case the generic parameter will be
    // `solana_program_runtime::invoke_context::InvokeContext`
    let mut loader: BuiltinProgram<InvokeContext<'_>> = BuiltinProgram::new_loader(vm_config);
```

To register a syscall we use the `register_function()` method on `BuiltinProgram<InvokeContext<'_>>`. This function expects the name of the syscall and the syscall itself defined in the [`solana_bpf_loader_program::syscalls`](https://docs.rs/solana-bpf-loader-program/latest/solana_bpf_loader_program/syscalls/struct.SyscallAbort.html#method.vm) module. For example the [msg!](https://docs.rs/solana-msg/latest/solana_msg/macro.msg.html) macro used in Solana programs to log messages is dependent on the `sol_log()` function which calls the `sol_log_` syscall in the VM.

```rust
// How the `msg!()` macro is defined in the source code
#[cfg(feature = "std")]
#[macro_export]
macro_rules! msg {
    ($msg:expr) => {
        $crate::sol_log($msg)
    };
    
    // The macro calls `sol_log()` function within the same crate and passes a message
    // formatted message using Rust `format!()` macro 
    ($($arg:tt)*) => ($crate::sol_log(&format!($($arg)*))); 
}

// How the `sol_log()` function calls the sycall to show the formatted message
/// Print a string to the log.
#[inline]
pub fn sol_log(message: &str) {
    #[cfg(target_os = "solana")] // Only available on the SVM
    unsafe {
        // syscall is defined as `sol_log_` in the VM
        syscalls::sol_log_(message.as_ptr(), message.len() as u64);
    }

   // ...
}
```

We can define our own `sol_log_` syscall in the VM similar to the above `msg!()` implementation using the `loader` variable we defined earlier.

```rust
// Import the `SyscallLog` from the `solana_bpf_loader_program::syscalls` module.
use solana_bpf_loader_program::syscalls::SyscallLog;

    loader
        // call this method and pass in the syscall function name in our case `sol_log_` 
        // and `SyscallLog::vm` which provides `vm()` method which is the VM interface that accepts parameters for execution.
        .register_function("sol_log_", SyscallLog::vm)
        .expect("Registration of `sol_log_` syscall failed!"); //Handle the error
```

These syscalls have a `vm()` method that provides the VM interface and a `rust()` method that provides a Rust interface.

We have learnt how to configure the VM and add syscalls. Explore the [`syscalls`](https://docs.rs/solana-bpf-loader-program/latest/solana_bpf_loader_program/syscalls/index.html) of the [`solana-bpf-loader-program`](https://docs.rs/solana-bpf-loader-program) Rust crate to learn other syscalls like `SyscallLogPubkey`, `SyscallGetClockSysvar` and many more.

The source code for this article can be found at - [https://github.com/448-OG/SVM-Series/tree/master/syscalls](https://github.com/448-OG/SVM-Series/tree/master/syscalls)

[&lt; Previous Article: SVM Series 02: SVM Feature Activation & Management](https://448.africa/svm-series-02-svm-feature-activation-and-management)

[Next Article: SVM Series 04: TransactionBatchProcessor &gt;](https://448.africa/svm-series-04-transactionbatchprocessor)

## Resources

1. Cost model source code  
    [https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/cost-model/src/transaction\_cost.rs#L121](https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/cost-model/src/transaction_cost.rs#L121)
    
2. Compute budget source code  
    [https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/compute-budget/src/compute\_budget.rs#L22](https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/compute-budget/src/compute_budget.rs#L22)
    
3. Bytecode / ISA  
    [https://github.com/anza-xyz/sbpf/blob/main/doc/bytecode.md](https://github.com/anza-xyz/sbpf/blob/main/doc/bytecode.md)
    
4. How does the Solana BPF\_VM calculate how many Compute units a program consumes?  
    [https://solana.stackexchange.com/questions/15119/how-does-the-solana-bpf-vm-calculate-how-many-compute-units-a-program-consumes](https://solana.stackexchange.com/questions/15119/how-does-the-solana-bpf-vm-calculate-how-many-compute-units-a-program-consumes)  
    [https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/program-runtime/src/invoke\_context.rs#L529](https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/program-runtime/src/invoke_context.rs#L529)  
    [https://github.com/solana-labs/rbpf/blob/4dc039f4ee7409838c7f230558aebf6869c32db9/src/program.rs#L331](https://github.com/solana-labs/rbpf/blob/4dc039f4ee7409838c7f230558aebf6869c32db9/src/program.rs#L331)  
    [https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/programs/bpf\_loader/src/lib.rs#L1405](https://github.com/anza-xyz/agave/blob/d5a84daebd2a7225684aa3f722b330e9d5381e76/programs/bpf_loader/src/lib.rs#L1405)  
    [https://github.com/solana-labs/rbpf/blob/4dc039f4ee7409838c7f230558aebf6869c32db9/src/vm.rs#L422](https://github.com/solana-labs/rbpf/blob/4dc039f4ee7409838c7f230558aebf6869c32db9/src/vm.rs#L422)
    
5. BPF instructions all consume 1 CU in the interpreter (which must match the JIT results)  
    [https://github.com/solana-labs/rbpf/blob/f3758ecee89198433422f751beee7f0f52dbcd55/src/interpreter.rs#L162](https://github.com/solana-labs/rbpf/blob/f3758ecee89198433422f751beee7f0f52dbcd55/src/interpreter.rs#L162)
    
6. Solana sycalls module  
    [https://docs.rs/solana-bpf-loader-program/latest/solana\_bpf\_loader\_program/syscalls/index.html](https://docs.rs/solana-bpf-loader-program/latest/solana_bpf_loader_program/syscalls/index.html)
