Primeros pasos

Los conjuntos de pruebas de Solana que lanzan solana-test-validator tardan segundos por prueba: estás esperando el inicio del proceso, las conexiones RPC y los viajes de red de ida y vuelta. LiteSVM ejecuta las mismas pruebas en milisegundos. Sin procesos externos, sin red, todo en memoria dentro de tu ejecutor de pruebas.

litesvm-go es el binding oficial de Go. Los tipos principales (PublicKey, Hash, Signature) provienen directamente de gagliardetto/solana-go, por lo que los valores circulan de forma natural entre litesvm-go y el resto del ecosistema Go de Solana.

Requisitos: Go 1.24+. No se necesita toolchain de Rust - los archivos estáticos precompilados están incluidos en el módulo y se seleccionan automáticamente según GOOS / GOARCH. Plataformas compatibles: macOS (amd64, arm64), Linux (amd64, arm64; glibc o musl), Windows (amd64).

Un handle LiteSVM recién creado expone airdrops, transacciones (legacy + v0), simulación, lectura/escritura de cuentas, sysvars, presupuesto de cómputo, feature gates, viaje en el tiempo, programas personalizados e historial de transacciones, todo respaldado por el mismo núcleo en Rust que impulsa los SDKs de Rust y TypeScript.

Inicio rápido

Instalar el módulo

go get resuelve las dependencias en un módulo de Go, así que inicializa uno primero si aún no tienes un go.mod:

go mod init mytest

Luego agrega el módulo:

go get github.com/LiteSVM/litesvm-go

También incluye solana-go para la construcción de keypair, instrucciones y transacciones:

go get github.com/gagliardetto/solana-go

Usuarios de Alpine / musl: añade -tags musl a tu invocación de go build / go test para que se enlace el archivo vendored correcto.

Crear tu SVM

package mytest
import (
"testing"
litesvm "github.com/LiteSVM/litesvm-go"
solana "github.com/gagliardetto/solana-go"
)
func TestSetup(t *testing.T) {
svm, err := litesvm.New()
if err != nil {
t.Fatal(err)
}
defer svm.Close()
// Fund a payer
priv, err := solana.NewRandomPrivateKey()
if err != nil {
t.Fatal(err)
}
payer := priv.PublicKey()
if err := svm.Airdrop(payer, 5_000_000_000); err != nil {
t.Fatal(err)
}
// Check the balance
lamports, ok, err := svm.Balance(payer)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("payer account missing")
}
t.Logf("balance: %d lamports", lamports)
}

litesvm.New() devuelve un *LiteSVM y un error. Cada punto de entrada en litesvm-go sigue la misma forma (value, error); los panics del lado de Rust son capturados y convertidos en errores. Usa siempre defer svm.Close() para liberar el handle de Rust subyacente, o confía en el finalizador, aunque se prefiere el Close explícito para una limpieza predecible.

Entendiendo el handle

Tras litesvm.New(), el *LiteSVM devuelto expone estos grupos de capacidades:

GrupoMétodos
CuentasAirdrop, Balance, GetAccount, SetAccount, MinimumBalanceForRentExemption
TransaccionesSendLegacyTransaction, SendVersionedTransaction, SimulateLegacyTransaction, SimulateVersionedTransaction, GetTransaction
BlockhashLatestBlockhash, ExpireBlockhash
ProgramasAddProgram, AddProgramFromFile, AddProgramWithLoader
Tiempo y sysvarsWarpToSlot, Clock, SetClock, Rent, SetRent, EpochSchedule, SetEpochSchedule, EpochRewards, ...
ConfiguraciónSetSigverify, SetBlockhashCheck, SetTransactionHistory, SetLogBytesLimit, SetSysvars, SetBuiltins, ...
Cómputo y característicasComputeBudget, SetComputeBudget, SetFeatureSet

Un LiteSVM recién creado incluye los programas principales de Solana (System Program, SPL Token, etc.) precargados, por lo que las transferencias simples funcionan de inmediato.

Enviar transacciones

Construye instrucciones con solana-go, serializa la transacción y envía los bytes. litesvm-go acepta los bytes codificados en bincode producidos por (*solana.Transaction).MarshalBinary:

package mytest
import (
"testing"
litesvm "github.com/LiteSVM/litesvm-go"
solana "github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/system"
)
func TestTransfer(t *testing.T) {
svm, err := litesvm.New()
if err != nil {
t.Fatal(err)
}
defer svm.Close()
priv, err := solana.NewRandomPrivateKey()
if err != nil {
t.Fatal(err)
}
payer := priv.PublicKey()
recipient := solana.NewWallet().PublicKey()
if err := svm.Airdrop(payer, 2_000_000_000); err != nil {
t.Fatal(err)
}
blockhash, err := svm.LatestBlockhash()
if err != nil {
t.Fatal(err)
}
ix := system.NewTransferInstruction(1_000_000_000, payer, recipient).Build()
tx, err := solana.NewTransaction(
[]solana.Instruction{ix},
blockhash,
solana.TransactionPayer(payer),
)
if err != nil {
t.Fatal(err)
}
if _, err := tx.Sign(func(k solana.PublicKey) *solana.PrivateKey {
if k.Equals(payer) {
return &priv
}
return nil
}); err != nil {
t.Fatal(err)
}
txBytes, err := tx.MarshalBinary()
if err != nil {
t.Fatal(err)
}
out, err := svm.SendLegacyTransaction(txBytes)
if err != nil {
t.Fatal(err)
}
defer out.Close()
if !out.IsOk() {
t.Fatalf("tx failed: %s\nlogs: %v", out.Error(), out.Logs())
}
lamports, _, err := svm.Balance(recipient)
if err != nil {
t.Fatal(err)
}
if lamports != 1_000_000_000 {
t.Fatalf("recipient balance = %d, want 1_000_000_000", lamports)
}
}

SendLegacyTransaction y SendVersionedTransaction devuelven ambos un *TxOutcome independientemente de si la transacción fue exitosa o no. Llama a IsOk() antes de leer los campos exclusivos de éxito, y usa siempre defer out.Close() para liberar el handle de Rust.

Trabajar con TxOutcome

Cada punto de entrada de envío / simulación devuelve un *TxOutcome. El mismo handle contiene metadatos tanto para el éxito como para el fracaso:

out, err := svm.SendLegacyTransaction(txBytes)
if err != nil {
t.Fatal(err)
}
defer out.Close()
if !out.IsOk() {
t.Fatalf("error: %s", out.Error())
}
_ = out.Signature() // solana.Signature
_ = out.ComputeUnits() // uint64
_ = out.Fee() // uint64
_ = out.Logs() // []string
_ = out.InnerInstructions()
// Programs that call set_return_data expose it here.
if pid, data, ok := out.ReturnData(); ok {
_ = pid
_ = data
}

Para SimulateLegacyTransaction / SimulateVersionedTransaction, el mismo *TxOutcome expone adicionalmente PostAccounts() - el estado de cuenta que habría resultado tras la ejecución:

sim, err := svm.SimulateLegacyTransaction(txBytes)
if err != nil {
t.Fatal(err)
}
defer sim.Close()
posts, err := sim.PostAccounts()
if err != nil {
t.Fatal(err)
}
for _, p := range posts {
_ = p.Address
_ = p.Account.Lamports()
p.Account.Close()
}

Configuración

litesvm-go expone los mismos modificadores del constructor que el crate de Rust, disponibles como métodos Set*:

// Each setter returns an error. In tests where the values are known good,
// the calls are infallible and assigning to _ keeps the example readable;
// in production code, check the error or wrap with require.NoError(t, ...).
_ = svm.SetSigverify(false) // accept unsigned / badly-signed txs
_ = svm.SetBlockhashCheck(false) // skip recent-blockhash enforcement
_ = svm.SetTransactionHistory(0) // 0 disables dedup; any N caps history
_ = svm.SetLogBytesLimit(-1) // negative = unlimited
_ = svm.SetLamports(1 << 40) // default lamports for new accounts
_ = svm.SetSysvars() // reset sysvars to defaults
_ = svm.SetBuiltins() // reload built-in programs
_ = svm.SetDefaultPrograms() // reload SPL Token, Memo, etc.
_ = svm.SetPrecompiles() // enable ed25519 / secp256k1 precompiles
_ = svm.WithNativeMints() // seed wrapped-SOL mint

Notas

Seguridad en hilos. Un handle *LiteSVM no es seguro para uso concurrente desde múltiples goroutines (el tipo de Rust subyacente no es Sync, y la mayoría de los métodos mutan el estado interno). Confina un handle a una única goroutine, o protégelo con un sync.Mutex.

Comportamiento ante panics. Los archivos de versión incluidos se compilan con immediate-abort: cualquier panic dentro de Rust aborta el proceso anfitrión directamente, sin desenrollar la pila. Esto es un intercambio deliberado por archivos más pequeños. Si alguna vez encuentras uno en la práctica, por favor abre un issue con un caso reproductor.

¿Qué sigue?

Esto cubre el handle principal: creación de cuentas, envío de instrucciones y lectura de estado. Para una referencia completa método a método, consulta la documentación de la API. Para ejemplos ejecutables (transferencia de SOL, configuración de cuentas, pruebas de programas, lógica basada en tiempo) consulta Ejemplos.

Is this page helpful?

Tabla de Contenidos

Editar Página