Початок роботи

Тестові набори Solana, що запускають solana-test-validator, витрачають секунди на кожен тест — ви очікуєте запуску процесу, RPC-з'єднань і мережевих запитів. LiteSVM виконує ті самі тести за мілісекунди. Жодних зовнішніх процесів, жодної мережі — усе в пам'яті всередині вашого тест-ранера.

litesvm-go — це офіційне прив'язання для Go. Основні типи (PublicKey, Hash, Signature) беруться безпосередньо з gagliardetto/solana-go, тому значення природно передаються між litesvm-go і рештою екосистеми Go Solana.

Вимоги: Go 1.24+. Інструментальний ланцюжок Rust не потрібен — попередньо зібрані статичні архіви вендоруються в модулі та автоматично вибираються за GOOS / GOARCH. Підтримувані платформи: macOS (amd64, arm64), Linux (amd64, arm64; glibc або musl), Windows (amd64).

Новий дескриптор LiteSVM надає доступ до аірдропів, транзакцій (legacy + v0), симуляції, читання/запису акаунтів, sysvar-ів, бюджету обчислень, feature gates, переміщення в часі, кастомних програм і історії транзакцій — усе на основі того самого ядра Rust, яке живить SDK для Rust і TypeScript.

Швидкий старт

Встановлення модуля

go get розв'язує залежності в Go-модуль, тому спочатку ініціалізуйте його, якщо у вас ще немає go.mod:

go mod init mytest

Потім додайте модуль:

go get github.com/LiteSVM/litesvm-go

Також підключіть solana-go для роботи з keypair, інструкціями та побудовою транзакцій:

go get github.com/gagliardetto/solana-go

Користувачі Alpine / musl: додайте -tags musl до виклику go build / go test, щоб підключився правильний вендорований архів.

Створення вашого 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() повертає *LiteSVM і error. Кожна точка входу в litesvm-go дотримується однієї форми (value, error); паніки на стороні Rust перехоплюються і перетворюються на помилки. Завжди використовуйте defer svm.Close(), щоб звільнити дескриптор Rust — або покладайтеся на фіналайзер, хоча явний Close є кращим варіантом для передбачуваного очищення ресурсів.

Розуміння дескриптора

Після litesvm.New() повернутий *LiteSVM надає такі групи можливостей:

ГрупаМетоди
АкаунтиAirdrop, Balance, GetAccount, SetAccount, MinimumBalanceForRentExemption
ТранзакціїSendLegacyTransaction, SendVersionedTransaction, SimulateLegacyTransaction, SimulateVersionedTransaction, GetTransaction
БлокхешLatestBlockhash, ExpireBlockhash
ПрограмиAddProgram, AddProgramFromFile, AddProgramWithLoader
Час і sysvar-иWarpToSlot, Clock, SetClock, Rent, SetRent, EpochSchedule, SetEpochSchedule, EpochRewards, ...
КонфігураціяSetSigverify, SetBlockhashCheck, SetTransactionHistory, SetLogBytesLimit, SetSysvars, SetBuiltins, ...
Обчислення та функціїComputeBudget, SetComputeBudget, SetFeatureSet

Новий LiteSVM постачається з попередньо завантаженими основними програмами Solana (System Program, SPL Token тощо), тому прості перекази працюють одразу після налаштування.

Надсилання транзакцій

Будуйте інструкції за допомогою solana-go, серіалізуйте транзакцію та надсилайте байти. litesvm-go приймає байти в кодуванні bincode, що їх створює (*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 і SendVersionedTransaction обидва повертають *TxOutcome незалежно від того, чи транзакція успішна, чи ні. Викликайте IsOk() перед зверненням до полів, доступних лише при успіху, і завжди використовуйте defer out.Close() для звільнення дескриптора Rust.

Робота з TxOutcome

Кожна точка входу send / simulate повертає *TxOutcome. Той самий дескриптор містить метадані як для успіху, так і для помилки:

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
}

Для SimulateLegacyTransaction / SimulateVersionedTransaction той самий *TxOutcome додатково надає PostAccounts() — стан акаунтів після гіпотетичного виконання:

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()
}

Конфігурація

litesvm-go надає ті самі перемикачі конструктора, що й крейт Rust, у вигляді методів 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

Примітки

Безпека потоків. Дескриптор *LiteSVM не є безпечним для одночасного використання з кількох горутин (базовий тип Rust не є Sync, і більшість методів змінюють внутрішній стан). Обмежуйте дескриптор однією горутиною або захищайте його за допомогою sync.Mutex.

Поведінка при паніці. Вендоровані архіви релізів зібрані з immediate-abort: будь-яка паніка в Rust одразу завершує хост-процес без розмотування стека. Це навмисний компроміс заради менших архівів. Якщо ви колись натрапите на таке на практиці, будь ласка, відкрийте issue з відтворювачем.

Що далі

Це охоплює основний дескриптор — створення акаунтів, надсилання інструкцій і читання стану. Для повного довідника методів дивіться документацію API. Для виконуваних прикладів (переказ SOL, налаштування акаунтів, тестування програм, логіка на основі часу) дивіться Приклади.

Is this page helpful?