Local Validator — Test your Program directly from your Pc

LE◎
Turbin3 Research
Published in
7 min readApr 25, 2024

This article serves as a comprehensive overview on how to use Solana Local Validator and how to test your programs without having to rely on Devnet!

In recent months, the Solana Devnet has encountered several reliability issues, occasionally going offline. and this problem resurfaced this week, raising substantial concerns among developers about how this instability impacts their projects.

Surprisingly, during these discussions, it became evident that many developers are not familiar with Localnet testing environments.

A recent tweet from Trent.sol highlighted this gap in knowledge: https://x.com/trentdotsol/status/1781369947794317371.

My objective today is to solve this gap in knowledge

This issue felt so strange to me since personally, ever since I began learning about smart contract development, I have always preferred testing my programs on a local validator.

I find it beneficial primarily because it allows for debugging directly via the explorer and I can have all these added benefits included in the Solana documentation page:

  • No RPC rate-limits
  • No airdrop limits
  • Direct on-chain program deployment (--bpf-program ...)
  • Clone accounts from a public cluster, including programs (--clone ...)
  • Configurable transaction history retention (--limit-ledger-size ...)
  • Configurable epoch length (--slots-per-epoch ...)
  • Jump to an arbitrary slot (--warp-slot ...)

So, what’s a local validator?

A local validator acts as your personal node, providing a sandbox environment for testing applications without the need to connect to a live blockchain network. It operates a local test ledger, which is a simplified version of the Solana ledger, equipped with all native programs pre-installed and various features enabled. This ledger is fully customizable to meet your specific local testing needs!

From the source code of the Local Validator

You can set up your local validator by installing the Solana tool suite and running

solana-test-validator

Upon launch, the validator will be accessible at http://127.0.0.1:8899 so to establish a connection to your local validator, use the following code snippet:

import { Connection } from "@solana/web3.js";

(async () => {
// This will connect you to your local validator
const connection = new Connection("http://127.0.0.1:8899", "confirmed");
})();

At initialization, the local validator will generate a directory named test-ledger in your user folder. This directory holds all data pertinent to your validator, including accounts created and programs both deployed and imported. To reset your local validator, you may either:

  1. Delete the test-ledger folder
  2. Use the command solana-test-validator --reset

Additionally, the solana logs command is extremely useful for monitoring the msg!() output from on-chain programs.

Getting some Localnet Solana

To perform transactions, it’s essential to have SOL in our wallet.

On Devnet, you can receive SOL through a faucet or using the requestAirdrop function:

const airdropSignature = await connection.requestAirdrop(
keypair.publicKey,
LAMPORTS_PER_SOL
);

await connection.confirmTransaction(airdropSignature);

On localnet, SOL can be obtained this way too, but it’s important to note that the Solana getting airdropped into the account, is not coming from a faucet but from your CLI default keypair which is pre-loaded with 1000 Localnet SOL at the genesis of the validator.

So if you’re using your CLI keypair to test your program you don’t need any airdrop!

Managing Programs and Accounts

For tests that require specific programs and accounts found only on the mainnet, the Solana CLI facilitates both the downloading and loading of these elements to your local validator. Here’s how to manage this:

Downloading Accounts and Programs:

For accounts:

solana account -u <source cluster> --output <output format> --output-file <destination file name/path> <address of account to fetch>

For Programs:

solana program dump -u <source cluster> <address of account to fetch> <destination file name/path>

Loading Accounts and Programs:

For accounts:

solana-test-validator --account <address to load the account to> <path to account file> --reset

For programs

solana-test-validator --bpf-program <address to load the program to> <path to program file> --reset

Advanced Local Validator Setup

Having explored the basics of creating a custom local validator via the CLI, let’s explore more advanced techniques, such as crafting personalized bash scripts for running different instances of a Validator with different programs in it and configuring Anchor to utilize specific accounts and programs.

Part 1: Configuring Your Local Validator with Anchor

From the anchor-lang docs we can see that anchor allows extensive customization through its configuration file, anchor.toml. In this segmetn, we will focus on the [test] section, which is essential for setting up your local validator.

The startup_wait flag is a critical setting used to delay the startup of the solana-test-validator. This delay is especially beneficial when cloning multiple accounts as it extends the validator's startup time to accommodate the increased load:

[test]
startup_wait = 10000

The [test.validator] section allows you to modify fundamental aspects of the local validator. These settings are directly passed to the solana-test-validator CLI using the anchor test command:

[test.validator]
url = "https://api.mainnet-beta.solana.com" # This is the url of the cluster that accounts are cloned from (See `test.validator.clone`).
warp_slot = 1337 # Warp the ledger to `warp_slot` after starting the validator.
slots_per_epoch = 5 # Override the number of slots in an epoch.
rpc_port = 1337 # Set JSON RPC on this port, and the next port for the RPC websocket.
limit_ledger_size = 1337 # Keep this amount of shreds in root slots.
ledger = "test-ledger" # Set ledger location.
gossip_port = 1337 # Gossip port number for the validator.
gossip_host = "127.0.0.1" # Gossip DNS name or IP address for the validator to advertise in gossip.
faucet_sol = 1337 # Give the faucet address this much SOL in genesis.
faucet_port = 1337 # Enable the faucet on this port.
dynamic_port_range = "1337 - 13337" # Range to use for dynamically assigned ports.
bind_address = "0.0.0.0" # IP address to bind the validator ports.

After all this setup work we can finally start cloning our accounts and programs:

You can utilize the [test.validator.clone] section to clone accounts from the specified cluster to your test cluster. If the account is linked to a program managed by the "BPF upgradeable loader", Anchor automatically clones the associated program data account:

[test.validator]
url = "https://api.mainnet-beta.solana.com"

[[test.validator.clone]]
address = "7NL2qWArf2BbEBBH1vTRZCsoNqFATTddH6h8GkVvrLpG"
[[test.validator.clone]]
address = "2RaN5auQwMdg5efgCaVqpETBV8sacWGR8tkK4m9kjo5r"
[[test.validator.clone]]
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" # implicitly also clones PwDiXFxQsGra4sFFTT8r1QWRMd4vfumiWC1jfWNfdYT

To integrate local accounts from JSON files into your validator setup, the [test.validator.account] flag allows for seamless account uploads:

[[test.validator.account]]
address = "Ev8WSPQsGb4wfjybqff5eZNcS3n6HaMsBkMk9suAiuM"
filename = "some_account.json"

Part 2: Configuring multiple Local Validators using Bash Script

This is an advanced tutorial that requires a basic understanding of how bash scripts operate, if you are unfamiliar with bash scripting, please proceed with caution. But don’t worry if that’s the case for you since this is primarily a demonstration anticipating the release of the Valid8 CLI tool, which will enable the creation and management of personalized and multiple local validators directly from your terminal.

But let’s explore this configuration:

Start by creating a dedicated folder within your path to store the programs you intend to use with your local validator:

mkdir ~/.local/share/valid8

Next, download the program data from the specified address into the newly created directory:

solana program dump -u m <address of account to fetch> ~/.local/share/valid8/<destination file name>.so

Then open a new script file using:

sudo nano /usr/local/bin/<name-of-the-validator>

Note: (if the /user/local/bin directory doesn’t exist, you can create it using sudo mkdir -p -m 775 /usr/local/bin).

And paste in the following code into the editor and save it:

#!/bin/bash

# Validator command
COMMAND="solana-test-validator -r --bpf-program <address of account to fetch> ~/.local/share/valid8/<destination file name>.so"

# Append any additional arguments passed to the script
for arg in "$@"
do
COMMAND+=" $arg"
done

# Execute the command
eval $COMMAND

Note: If you need to include multiple programs, append the --bpf-program option as needed:

[...] --bpf-program <another program address> ~/.local/share/valid8/<another destination file name>.so" [...]

After this we need ensure the script can be executed, so we modify its permissions:

sudo chmod +x /usr/local/bin/<name-of-the-validator>

Finally, test your new validator script within your project folder:

<name-of-the-validator>

Example: Creating a Metaplex-Test-Validator

Let’s create a specialized bash script for a Metaplex-test-validator that incorporates the mpl-token-metadata program:

>> solana program dump -u m metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ~/.local/share/valid8/metadata.so

>> sudo nano /usr/local/bin/metaplex-test-validator

Use the same script structure as before, specifying the mpl-token-metadata program:

#!/bin/bash

# Validator command
COMMAND="solana-test-validator -r --bpf-program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ~/.local/share/valid8/metadata.so"

# Append any additional arguments passed to the script
for arg in "$@"
do
COMMAND+=" $arg"
done

# Execute the command
eval $COMMAND

Then finish it off by making it executable and testing it out:

>> sudo chmod +x /usr/local/bin/metaplex-test-validator

>> metaplex-test-validator

Congratulations! You are now a Local Validator wizard! I hope this tutorial has been helpful and that you learned something insightful!

A special thanks to Berg from the Valid8 Team, Dean for creating the original Bash Script for Valid8 (you can find the gist here), and Turbin3 which always tries to create cutting-edge concepts on Solana.

If you want to follow my journey and you don’t want to miss out on my next tutorial, follow me on Twitter!

--

--