Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
304f41e
initial definition of `Storage` trait
MicaiahReid Dec 16, 2025
893c1f3
add implementation of `Storage` crate for sqlite storage
MicaiahReid Dec 16, 2025
82c813f
update dependencies to have sqlite/postgres features
MicaiahReid Dec 16, 2025
c7648c2
publicize Storage mod; add error conversion
MicaiahReid Dec 16, 2025
ff4c82e
replace `HashMap` with `dyn Storage` for `blocks` store in svm
MicaiahReid Dec 16, 2025
ffe5c8c
implement conversion from LiteSVMError to SurfpoolError
MicaiahReid Dec 19, 2025
4e6ca32
refactor error handling in Storage and Sqlite modules; enhance error …
MicaiahReid Dec 19, 2025
f730cf1
refactor logging level from debug to trace in SqliteStorage methods
MicaiahReid Dec 19, 2025
0c9b647
refactor StorageConstructor connect method to require a non-optional …
MicaiahReid Dec 19, 2025
1aa70b6
implement Storage trait for HashMap
MicaiahReid Dec 19, 2025
b834a8a
create SurfnetLiteSvm struct with database integration
MicaiahReid Dec 19, 2025
7971d87
add `db` flag to CLI and use to pass db url to surfnet instantiation
MicaiahReid Dec 19, 2025
c0ea22e
implement db storage for `blocks` + `chain_tip`
MicaiahReid Dec 19, 2025
6baf027
update `svm.rs` to use `SurfnetLiteSvm` for `inner`
MicaiahReid Dec 19, 2025
3b200ca
propagate changes in `inner` in `svm.rs` to all tests by adding unwra…
MicaiahReid Dec 19, 2025
38cf26a
add test helpers to storage mod to assist in augmenting existing test…
MicaiahReid Dec 19, 2025
059d8b2
implement svm `get_all_accounts` to get vec of all accounts from db
MicaiahReid Jan 2, 2026
9ee5017
sort results for and document `get_all_accounts` method
MicaiahReid Jan 2, 2026
801013b
clean up tests
MicaiahReid Jan 2, 2026
17ddb31
upgrade svm/integration tests to use all db types for tests
MicaiahReid Jan 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[alias]
surfpool-install = "install --path crates/cli --locked --force --features supervisor_ui --features version_check"
surfpool-install-dev = "install --path crates/cli --locked --force --features supervisor_ui"
surfpool-install = "install --path crates/cli --locked --force --features supervisor_ui --features version_check --features sqlite"
surfpool-install-dev = "install --path crates/cli --locked --force --features supervisor_ui --features sqlite"
# useful for local builds that point to local txtx crates - prevents conflicts with the supervisor_ui feature
surfpool-install-minimal = "install --path crates/cli --locked --force"
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ solana-signer = { version = "3.0.0", default-features = false }
solana-slot-hashes = { version = "3.0.0", default-features = false }
solana-system-interface = { version = "2.0.0", default-features = false }
solana-sysvar = { version = "3.0.0", default-features = false }
solana-sysvar-id = { version = "3.0.0", default-features = false }
solana-transaction = { version = "3.0.0", default-features = false }
solana-transaction-context = { version = "3.0.0", default-features = false }
solana-transaction-error = { version = "3.0.0", default-features = false }
Expand All @@ -147,7 +148,9 @@ solana-version = { version = "3.0.0", default-features = false }
spl-associated-token-account-interface = { version = "2.0.0", default-features = false }
spl-token-2022-interface = { version = "2.0.0", default-features = false }
spl-token-interface = { version = "2.0.0", default-features = false }
tempfile = "3.23.0"
test-case = "^3.3.1"
thiserror = "2.0"
tokio = { version = "1.43.0", default-features = false }
tokio-tungstenite = { version = "=0.20.1", default-features = false }
toml = { version = "0.8.23", default-features = false }
Expand Down
4 changes: 2 additions & 2 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ cli = ["clap/derive", "clap/env", "clap_complete", "toml", "ctrlc", "hiro-system
supervisor_ui = ["txtx-supervisor-ui/crates_build"]
explorer = []
geyser_plugin = ["surfpool-core/geyser_plugin"]
sqlite = ["surfpool-gql/sqlite"]
postgres = ["surfpool-gql/postgres"]
sqlite = ["surfpool-gql/sqlite", "surfpool-core/sqlite"]
postgres = ["surfpool-gql/postgres", "surfpool-core/postgres"]
version_check = []
subgraph = ["surfpool-core/subgraph"]

Expand Down
3 changes: 3 additions & 0 deletions crates/cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ pub struct StartSimnet {
/// A set of inputs to use for the runbook (eg. surfpool start --runbook-input myInputs.json)
#[arg(long = "runbook-input", short = 'i')]
pub runbook_input: Vec<String>,
/// Surfnet database connection URL for persistent Surfnets. For an in-memory sqlite database, use ":memory:". For an on-disk sqlite database, use a filename ending in '.sqlite'.
#[arg(long = "db")]
pub db: Option<String>,
}

fn parse_svm_feature(s: &str) -> Result<SvmFeature, String> {
Expand Down
4 changes: 3 additions & 1 deletion crates/cli/src/cli/simnet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ pub async fn handle_start_local_surfnet_command(
}

// We start the simnet as soon as possible, as it needs to be ready for deployments
let (mut surfnet_svm, simnet_events_rx, geyser_events_rx) = SurfnetSvm::new();
let (mut surfnet_svm, simnet_events_rx, geyser_events_rx) =
SurfnetSvm::new_with_db(cmd.db.as_deref())
.map_err(|e| format!("Failed to initialize Surfnet SVM: {}", e))?;

// Apply feature configuration from CLI flags
let feature_config = cmd.feature_config();
Expand Down
7 changes: 7 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,19 @@ solana-signer = { workspace = true }
solana-slot-hashes = { workspace = true }
solana-system-interface = { workspace = true }
solana-sysvar = { workspace = true }
solana-sysvar-id = { workspace = true }
solana-transaction = { workspace = true }
solana-transaction-error = { workspace = true }
solana-transaction-status = { workspace = true }
solana-version = { workspace = true }
spl-associated-token-account-interface = { workspace = true }
spl-token-interface = { workspace = true }
spl-token-2022-interface = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
uuid = { workspace = true }

surfpool-db = { workspace = true }
surfpool-subgraph = { workspace = true, optional = true }
surfpool-types = { workspace = true }

Expand All @@ -99,8 +102,12 @@ txtx-addon-network-svm = { workspace = true }
[dev-dependencies]
test-case = { workspace = true }
env_logger = "*"
tempfile = { workspace = true }

[features]
default = ["sqlite"]
sqlite = ["surfpool-db/sqlite"]
postgres = ["surfpool-db/postgres"]
ignore_tests_ci = []
geyser_plugin = ["solana-geyser-plugin-manager"]
subgraph = ["surfpool-subgraph"]
19 changes: 19 additions & 0 deletions crates/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ use std::{fmt::Display, future::Future, pin::Pin};

use crossbeam_channel::TrySendError;
use jsonrpc_core::{Error, Result};
use litesvm::error::LiteSVMError;
use serde::Serialize;
use serde_json::json;
use solana_client::{client_error::ClientError, rpc_request::TokenAccountsFilter};
use solana_clock::Slot;
use solana_pubkey::Pubkey;
use solana_transaction_status::EncodeError;

use crate::storage::StorageError;

pub type SurfpoolResult<T> = std::result::Result<T, SurfpoolError>;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -447,3 +450,19 @@ impl SurfpoolError {
Self(error)
}
}

impl From<StorageError> for SurfpoolError {
fn from(e: StorageError) -> Self {
let mut error = Error::internal_error();
error.data = Some(json!(format!("Storage error: {}", e.to_string())));
SurfpoolError(error)
}
}

impl From<LiteSVMError> for SurfpoolError {
fn from(e: LiteSVMError) -> Self {
let mut error = Error::internal_error();
error.data = Some(json!(format!("LiteSVM error: {}", e.to_string())));
SurfpoolError(error)
}
}
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod helpers;
pub mod rpc;
pub mod runloops;
pub mod scenarios;
pub mod storage;
pub mod surfnet;
pub mod types;

Expand Down
40 changes: 24 additions & 16 deletions crates/core/src/rpc/accounts_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,12 +473,13 @@ impl AccountsData for SurfpoolAccountsDataRpc {
// get the info we need and free up lock before validation
let (current_slot, block_exists) = meta
.with_svm_reader(|svm_reader| {
(
svm_reader.get_latest_absolute_slot(),
svm_reader.blocks.contains_key(&block),
)
svm_reader
.blocks
.contains_key(&block)
.map_err(SurfpoolError::from)
.map(|exists| (svm_reader.get_latest_absolute_slot(), exists))
})
.map_err(Into::<jsonrpc_core::Error>::into)?;
.map_err(Into::<jsonrpc_core::Error>::into)??;

// block is valid if it exists in our block history or it's not too far in the future
if !block_exists && block > current_slot {
Expand Down Expand Up @@ -816,17 +817,20 @@ mod tests {
setup.context.svm_locker.with_svm_writer(|svm_writer| {
use crate::surfnet::BlockHeader;

svm_writer.blocks.insert(
test_slot,
BlockHeader {
hash: SyntheticBlockhash::new(test_slot).to_string(),
previous_blockhash: SyntheticBlockhash::new(test_slot - 1).to_string(),
parent_slot: test_slot - 1,
block_time: chrono::Utc::now().timestamp_millis(),
block_height: test_slot,
signatures: vec![],
},
);
svm_writer
.blocks
.store(
test_slot,
BlockHeader {
hash: SyntheticBlockhash::new(test_slot).to_string(),
previous_blockhash: SyntheticBlockhash::new(test_slot - 1).to_string(),
parent_slot: test_slot - 1,
block_time: chrono::Utc::now().timestamp_millis(),
block_height: test_slot,
signatures: vec![],
},
)
.unwrap();
});

let result = setup
Expand Down Expand Up @@ -1078,13 +1082,15 @@ mod tests {
.context
.svm_locker
.airdrop(&fee_payer.pubkey(), 1_000_000_000)
.unwrap()
.unwrap();

// Airdrop 1 SOL to recipient for rent exemption
client
.context
.svm_locker
.airdrop(&recipient.pubkey(), 1_000_000_000)
.unwrap()
.unwrap();

// Generate keypair to use as address of mint
Expand Down Expand Up @@ -1291,13 +1297,15 @@ mod tests {
.context
.svm_locker
.airdrop(&fee_payer.pubkey(), 1_000_000_000)
.unwrap()
.unwrap();

// Airdrop 1 SOL to recipient for rent exemption
client
.context
.svm_locker
.airdrop(&recipient.pubkey(), 1_000_000_000)
.unwrap()
.unwrap();

// Generate keypair to use as address of mint
Expand Down
11 changes: 8 additions & 3 deletions crates/core/src/rpc/bank_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,14 @@ impl BankData for SurfpoolBankDataRpc {
}

fn get_inflation_rate(&self, meta: Self::Metadata) -> Result<RpcInflationRate> {
meta.with_svm_reader(|svm_reader| {
let inflation_activation_slot =
svm_reader.blocks.keys().min().copied().unwrap_or_default();
meta.with_svm_reader(|svm_reader| -> RpcInflationRate {
let inflation_activation_slot = svm_reader
.blocks
.keys()
.unwrap_or_default()
.into_iter()
.min()
.unwrap_or_default();
let epoch_schedule = svm_reader.inner.get_sysvar::<EpochSchedule>();
let inflation_start_slot = epoch_schedule.get_first_slot_in_epoch(
epoch_schedule
Expand Down
Loading
Loading