Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 5 additions & 6 deletions crates/cli/src/cli/simnet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,14 @@ fn log_events(
SimnetEvent::RunbookStarted(runbook_id) => {
deployment_completed = false;
info!("Runbook '{}' execution started", runbook_id);
let _ =
simnet_commands_tx.send(SimnetCommand::SetInstructionProfiling(false));
let _ = simnet_commands_tx
.send(SimnetCommand::StartRunbookExecution(runbook_id));
}
SimnetEvent::RunbookCompleted(runbook_id) => {
SimnetEvent::RunbookCompleted(runbook_id, errors) => {
deployment_completed = true;
info!("Runbook '{}' execution completed", runbook_id);
let _ =
simnet_commands_tx.send(SimnetCommand::SetInstructionProfiling(true));
let _ = simnet_commands_tx
.send(SimnetCommand::CompleteRunbookExecution(runbook_id, errors));
}
},
Err(_e) => {
Expand Down Expand Up @@ -412,7 +412,6 @@ fn log_events(
},
Err(_e) => {
deployment_completed = true;
let _ = simnet_commands_tx.send(SimnetCommand::SetInstructionProfiling(true));
}
},
}
Expand Down
6 changes: 5 additions & 1 deletion crates/cli/src/runbook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,18 @@ pub async fn execute_runbook(
if cmd.unsupervised {
let _ = simnet_events_tx.send(SimnetEvent::RunbookStarted(runbook_id.clone()));
let res = start_unsupervised_runbook_runloop(&mut runbook, &progress_tx).await;
let diags = res
.as_ref()
.map_err(|ds| ds.iter().map(|d| d.message.clone()).collect())
.err();
process_runbook_execution_output(
res,
&mut runbook,
runbook_state_location,
&simnet_events_tx,
cmd.output_json,
);
let _ = simnet_events_tx.send(SimnetEvent::RunbookCompleted(runbook_id));
let _ = simnet_events_tx.send(SimnetEvent::RunbookCompleted(runbook_id, diags));
} else {
let (kill_supervised_execution_tx, block_store_handle) =
configure_supervised_execution(runbook, runbook_state_location, &cmd, simnet_events_tx)
Expand Down
16 changes: 8 additions & 8 deletions crates/cli/src/tui/simnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,18 +550,21 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<(
));
let _ = app
.simnet_commands_tx
.send(SimnetCommand::SetInstructionProfiling(false));
.send(SimnetCommand::StartRunbookExecution(runbook_id.clone()));
}
SimnetEvent::RunbookCompleted(runbook_id) => {
SimnetEvent::RunbookCompleted(runbook_id, errors) => {
deployment_completed = true;
new_events.push((
EventType::Success,
Local::now(),
format!("Runbook '{}' execution completed", runbook_id),
));
let _ = app
.simnet_commands_tx
.send(SimnetCommand::SetInstructionProfiling(true));
let _ = app.simnet_commands_tx.send(
SimnetCommand::CompleteRunbookExecution(
runbook_id.clone(),
errors.clone(),
),
);
app.status_bar_message = None;
}
},
Expand Down Expand Up @@ -657,9 +660,6 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<(
},
Err(_) => {
deployment_completed = true;
let _ = app
.simnet_commands_tx
.send(SimnetCommand::SetInstructionProfiling(true));
break;
}
},
Expand Down
60 changes: 58 additions & 2 deletions crates/core/src/rpc/surfnet_cheatcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use solana_sdk::{program_option::COption, transaction::VersionedTransaction};
use solana_system_interface::program as system_program;
use spl_associated_token_account::get_associated_token_address_with_program_id;
use surfpool_types::{
ClockCommand, Idl, ResetAccountConfig, RpcProfileResultConfig, SimnetCommand, SimnetEvent,
UiKeyedProfileResult,
ClockCommand, GetSurfnetInfoResponse, Idl, ResetAccountConfig, RpcProfileResultConfig,
SimnetCommand, SimnetEvent, UiKeyedProfileResult,
types::{AccountUpdate, SetSomeAccount, SupplyUpdate, TokenAccountUpdate, UuidOrSignature},
};

Expand Down Expand Up @@ -754,6 +754,50 @@ pub trait SurfnetCheatcodes {
pubkey_str: String,
config: Option<ResetAccountConfig>,
) -> Result<RpcResponse<()>>;

/// A cheat code to get Surfnet network information.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
///
/// ## Returns
/// A `RpcResponse<GetSurfnetInfoResponse>` containing the Surfnet network information.
///
/// ## Example Request
/// ```json
/// {
/// "jsonrpc": "2.0",
/// "id": 1,
/// "method": "surfnet_getSurfnetInfo"
/// }
/// ```
///
/// ## Example Response
/// ```json
/// {
/// "jsonrpc": "2.0",
/// "result": {
/// "context": {
/// "slot": 369027326,
/// "apiVersion": "2.3.8"
/// },
/// "value": {
/// "runbookExecutions": [
/// {
/// "startedAt": 1758747828,
/// "completedAt": 1758747828,
/// "runbookId": "deployment"
/// }
/// ]
/// }
/// },
/// "id": 1
/// }
/// ```
///
#[rpc(meta, name = "surfnet_getSurfnetInfo")]
fn get_surfnet_info(&self, meta: Self::Metadata)
-> Result<RpcResponse<GetSurfnetInfoResponse>>;
}

#[derive(Clone)]
Expand Down Expand Up @@ -1281,6 +1325,18 @@ impl SurfnetCheatcodes for SurfnetCheatcodesRpc {
value: (),
})
}

fn get_surfnet_info(
&self,
meta: Self::Metadata,
) -> Result<RpcResponse<GetSurfnetInfoResponse>> {
let svm_locker = meta.get_svm_locker()?;
let runbook_executions = svm_locker.runbook_executions();
Ok(RpcResponse {
context: RpcResponseContext::new(svm_locker.get_latest_absolute_slot()),
value: GetSurfnetInfoResponse::new(runbook_executions),
})
}
}

#[cfg(test)]
Expand Down
9 changes: 5 additions & 4 deletions crates/core/src/runloops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,11 @@ pub async fn start_block_production_runloop(
let _ = svm_locker.simnet_events_tx().send(SimnetEvent::Aborted("Terminated due to inactivity.".to_string()));
break;
}
SimnetCommand::SetInstructionProfiling(enabled) => {
svm_locker.with_svm_writer(|svm_writer| {
svm_writer.instruction_profiling_enabled = enabled;
});
SimnetCommand::StartRunbookExecution(runbook_id) => {
svm_locker.start_runbook_execution(runbook_id);
}
SimnetCommand::CompleteRunbookExecution(runbook_id, error) => {
svm_locker.complete_runbook_execution(runbook_id, error);
}
}
},
Expand Down
30 changes: 27 additions & 3 deletions crates/core/src/surfnet/locker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ use solana_transaction_status::{
};
use surfpool_types::{
ComputeUnitsEstimationResult, ExecutionCapture, Idl, KeyedProfileResult, ProfileResult,
ResetAccountConfig, RpcProfileResultConfig, SimnetCommand, SimnetEvent,
TransactionConfirmationStatus, TransactionStatusEvent, UiKeyedProfileResult, UuidOrSignature,
VersionedIdl,
ResetAccountConfig, RpcProfileResultConfig, RunbookExecutionStatusReport, SimnetCommand,
SimnetEvent, TransactionConfirmationStatus, TransactionStatusEvent, UiKeyedProfileResult,
UuidOrSignature, VersionedIdl,
};
use tokio::sync::RwLock;
use txtx_addon_kit::indexmap::IndexSet;
Expand Down Expand Up @@ -2940,6 +2940,30 @@ impl SurfnetSvmLocker {
svm_writer.subscribe_for_logs_updates(commitment_level, filter)
})
}

pub fn runbook_executions(&self) -> Vec<RunbookExecutionStatusReport> {
self.with_svm_reader(|svm_reader| svm_reader.runbook_executions.clone())
}

pub fn start_runbook_execution(&self, runbook_id: String) {
self.with_svm_writer(|svm_writer| {
svm_writer.instruction_profiling_enabled = false;
svm_writer.start_runbook_execution(runbook_id);
});
}

pub fn complete_runbook_execution(&self, runbook_id: String, error: Option<Vec<String>>) {
self.with_svm_writer(|svm_writer| {
svm_writer.complete_runbook_execution(&runbook_id, error);
let some_runbook_executing = svm_writer
.runbook_executions
.iter()
.any(|e| e.completed_at.is_none());
if !some_runbook_executing {
svm_writer.instruction_profiling_enabled = true;
}
});
}
}

// Helper function to apply filters
Expand Down
25 changes: 21 additions & 4 deletions crates/core/src/surfnet/svm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use chrono::Utc;
use convert_case::Casing;
use crossbeam_channel::{Receiver, Sender, unbounded};
use litesvm::{
LiteSVM,
LiteSVM, error,
types::{
FailedTransactionMetadata, SimulatedTransactionInfo, TransactionMetadata, TransactionResult,
},
Expand Down Expand Up @@ -48,9 +48,9 @@ use spl_token_2022::extension::{
};
use surfpool_types::{
AccountChange, AccountProfileState, DEFAULT_PROFILING_MAP_CAPACITY, DEFAULT_SLOT_TIME_MS,
FifoMap, Idl, ProfileResult, RpcProfileDepth, RpcProfileResultConfig, SimnetEvent,
TransactionConfirmationStatus, TransactionStatusEvent, UiAccountChange, UiAccountProfileState,
UiProfileResult, VersionedIdl,
FifoMap, Idl, ProfileResult, RpcProfileDepth, RpcProfileResultConfig,
RunbookExecutionStatusReport, SimnetEvent, TransactionConfirmationStatus,
TransactionStatusEvent, UiAccountChange, UiAccountProfileState, UiProfileResult, VersionedIdl,
types::{
ComputeUnitsEstimationResult, KeyedProfileResult, UiKeyedProfileResult, UuidOrSignature,
},
Expand Down Expand Up @@ -139,6 +139,7 @@ pub struct SurfnetSvm {
pub feature_set: FeatureSet,
pub instruction_profiling_enabled: bool,
pub max_profiles: usize,
pub runbook_executions: Vec<RunbookExecutionStatusReport>,
}

pub const FEATURE: Feature = Feature {
Expand Down Expand Up @@ -211,6 +212,7 @@ impl SurfnetSvm {
feature_set,
instruction_profiling_enabled: true,
max_profiles: DEFAULT_PROFILING_MAP_CAPACITY,
runbook_executions: Vec::new(),
};

// Generate the initial synthetic blockhash
Expand Down Expand Up @@ -1663,6 +1665,21 @@ impl SurfnetSvm {
pub fn iter_accounts(&self) -> std::collections::hash_map::Iter<'_, Pubkey, AccountSharedData> {
self.inner.accounts_db().inner.iter()
}

pub fn start_runbook_execution(&mut self, runbook_id: String) {
self.runbook_executions
.push(RunbookExecutionStatusReport::new(runbook_id));
}

pub fn complete_runbook_execution(&mut self, runbook_id: &str, error: Option<Vec<String>>) {
if let Some(execution) = self
.runbook_executions
.iter_mut()
.find(|e| e.runbook_id.eq(runbook_id) && e.completed_at.is_none())
{
execution.mark_completed(error);
}
}
}

#[cfg(test)]
Expand Down
39 changes: 37 additions & 2 deletions crates/types/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ pub enum SimnetEvent {
timestamp: DateTime<Local>,
},
RunbookStarted(String),
RunbookCompleted(String),
RunbookCompleted(String, Option<Vec<String>>),
}

impl SimnetEvent {
Expand Down Expand Up @@ -464,7 +464,8 @@ pub enum SimnetCommand {
bool,
),
Terminate(Option<(Hash, String)>),
SetInstructionProfiling(bool),
StartRunbookExecution(String),
CompleteRunbookExecution(String, Option<Vec<String>>),
}

#[derive(Debug)]
Expand Down Expand Up @@ -919,6 +920,40 @@ impl Default for ResetAccountConfig {
}
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetSurfnetInfoResponse {
runbook_executions: Vec<RunbookExecutionStatusReport>,
}
impl GetSurfnetInfoResponse {
pub fn new(runbook_executions: Vec<RunbookExecutionStatusReport>) -> Self {
Self { runbook_executions }
}
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunbookExecutionStatusReport {
pub started_at: u32,
pub completed_at: Option<u32>,
pub runbook_id: String,
pub errors: Option<Vec<String>>,
}
impl RunbookExecutionStatusReport {
pub fn new(runbook_id: String) -> Self {
Self {
started_at: Local::now().timestamp() as u32,
completed_at: None,
runbook_id,
errors: None,
}
}
pub fn mark_completed(&mut self, error: Option<Vec<String>>) {
self.completed_at = Some(Local::now().timestamp() as u32);
self.errors = error;
}
}

#[cfg(test)]
mod tests {
use serde_json::json;
Expand Down