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 mytestimport ("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 payerpriv, 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 balancelamports, 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:
| Grupo | Métodos |
|---|---|
| Cuentas | Airdrop, Balance, GetAccount, SetAccount, MinimumBalanceForRentExemption |
| Transacciones | SendLegacyTransaction, SendVersionedTransaction, SimulateLegacyTransaction, SimulateVersionedTransaction, GetTransaction |
| Blockhash | LatestBlockhash, ExpireBlockhash |
| Programas | AddProgram, AddProgramFromFile, AddProgramWithLoader |
| Tiempo y sysvars | WarpToSlot, Clock, SetClock, Rent, SetRent, EpochSchedule, SetEpochSchedule, EpochRewards, ... |
| Configuración | SetSigverify, SetBlockhashCheck, SetTransactionHistory, SetLogBytesLimit, SetSysvars, SetBuiltins, ... |
| Cómputo y características | ComputeBudget, 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 mytestimport ("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?