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
12 changes: 6 additions & 6 deletions Cargo.lock

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

13 changes: 10 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ bincode = "1.3.3"
blake3 = { version = "1.8.2", default-features = false }
borsh = { version = "1.5.5", default-features = false }
bs58 = { version = "0.5.0", default-features = false }
chrono = { version = "0.4.26", features = ["alloc"], default-features = false }
chrono = { version = "0.4.42", features = [
"alloc",
"clock",
], default-features = false }
clap = { version = "4.5.48", default-features = false }
clap_complete = { version = "4.5.48", default-features = false }
convert_case = "0.8.0"
Expand All @@ -57,7 +60,9 @@ fern = { version = "0.7.1", default-features = false }
fork = "0.2.0"
futures = { version = "0.3.22", default-features = false }
hex = { version = "0.4.3", default-features = false }
hiro-system-kit = { version = "0.3.4", default-features = false }
hiro-system-kit = { version = "0.3.4", default-features = false, features = [
"tokio_helpers",
] }
include_dir = "0.7.4"
indicatif = { version = "0.18.0", default-features = false }
ipc-channel = "0.19.0"
Expand Down Expand Up @@ -123,7 +128,9 @@ solana-rpc-client = { version = "2.3.8", default-features = false }
solana-rpc-client-api = { version = "2.3.8", default-features = false }
solana-runtime = { version = "2.3.8", default-features = false }
solana-sdk-ids = { version = "2.2.1", default-features = false }
solana-signature = { version = "2.3.0", default-features = false, features = ["rand"] }
solana-signature = { version = "2.3.0", default-features = false, features = [
"rand",
] }
solana-signer = { version = "2.2.1", default-features = false }
solana-slot-hashes = { version = "2.2.1", default-features = false }
solana-system-interface = { version = "1.0.0", default-features = false }
Expand Down
81 changes: 65 additions & 16 deletions crates/core/src/rpc/surfnet_cheatcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use solana_transaction::versioned::VersionedTransaction;
use spl_associated_token_account::get_associated_token_address_with_program_id;
use surfpool_types::{
ClockCommand, GetSurfnetInfoResponse, Idl, ResetAccountConfig, RpcProfileResultConfig,
SimnetCommand, SimnetEvent, UiKeyedProfileResult,
SimnetCommand, SimnetEvent, StreamAccountConfig, UiKeyedProfileResult,
types::{AccountUpdate, SetSomeAccount, SupplyUpdate, TokenAccountUpdate, UuidOrSignature},
};

Expand Down Expand Up @@ -137,7 +137,6 @@ pub trait SurfnetCheatcodes {
/// and rent epoch of a given account.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `pubkey`: The public key of the account to be updated, as a base-58 encoded string.
/// - `update`: The `AccountUpdate` struct containing the fields to update the account.
///
Expand Down Expand Up @@ -183,7 +182,6 @@ pub trait SurfnetCheatcodes {
/// including the token amount, delegate, state, delegated amount, and close authority.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `owner`: The base-58 encoded public key of the token account's owner.
/// - `mint`: The base-58 encoded public key of the token mint (e.g., the token type).
/// - `update`: The `TokenAccountUpdate` struct containing the fields to update the token account.
Expand Down Expand Up @@ -242,7 +240,6 @@ pub trait SurfnetCheatcodes {
/// potential errors.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request.
/// - `transaction_data`: A base64 encoded string of the `VersionedTransaction`.
/// - `tag`: An optional tag for the transaction.
/// - `encoding`: An optional encoding for returned account data.
Expand Down Expand Up @@ -271,7 +268,6 @@ pub trait SurfnetCheatcodes {
/// Retrieves all profiling results for a given tag.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request.
/// - `tag`: The tag to retrieve profiling results for.
///
/// ## Returns
Expand All @@ -291,10 +287,9 @@ pub trait SurfnetCheatcodes {
/// by the `getSupply` RPC method.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `update`: The `SupplyUpdate` struct containing the optional fields to update:
/// - `total`: Optional total supply in lamports
/// - `circulating`: Optional circulating supply in lamports
/// - `circulating`: Optional circulating supply in lamports
/// - `non_circulating`: Optional non-circulating supply in lamports
/// - `non_circulating_accounts`: Optional list of non-circulating account addresses
///
Expand Down Expand Up @@ -344,7 +339,6 @@ pub trait SurfnetCheatcodes {
/// This method allows developers to directly patch the upgrade authority of a program's ProgramData account.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `program_id`: The base-58 encoded public key of the program.
/// - `new_authority`: The base-58 encoded public key of the new authority. If omitted, the program will have no upgrade authority.
///
Expand Down Expand Up @@ -383,7 +377,6 @@ pub trait SurfnetCheatcodes {
/// A cheat code to get the transaction profile for a given signature or UUID.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `signature_or_uuid`: The transaction signature (as a base-58 string) or a UUID (as a string) for which to retrieve the profile.
///
/// ## Returns
Expand Down Expand Up @@ -424,7 +417,6 @@ pub trait SurfnetCheatcodes {
/// A cheat code to register an IDL for a given program in memory.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `idl`: The full IDL object to be registered in memory. The `address` field should match the program's public key.
/// - `slot` (optional): The slot at which to register the IDL. If omitted, uses the latest slot.
///
Expand Down Expand Up @@ -522,7 +514,6 @@ pub trait SurfnetCheatcodes {
/// A cheat code to get the registered IDL for a given program ID.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `program_id`: The base-58 encoded public key of the program whose IDL is being requested.
/// - `slot` (optional): The slot at which to query the IDL. If omitted, uses the latest slot.
///
Expand Down Expand Up @@ -717,7 +708,6 @@ pub trait SurfnetCheatcodes {
/// A cheat code to reset an account on the local network.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `pubkey_str`: The base-58 encoded public key of the account to reset.
/// - `config`: A `ResetAccountConfig` specifying how to reset the account. If omitted, the account will be reset without cascading to owned accounts.
///
Expand All @@ -730,7 +720,7 @@ pub trait SurfnetCheatcodes {
/// "jsonrpc": "2.0",
/// "id": 1,
/// "method": "surfnet_resetAccount",
/// "params": [ "4EXSeLGxVBpAZwq7vm6evLdewpcvE2H56fpqL2pPiLFa", { "recursive": true } ]
/// "params": [ "4EXSeLGxVBpAZwq7vm6evLdewpcvE2H56fpqL2pPiLFa", { "includeOwnedAccounts": true } ]
/// }
/// ```
///
Expand All @@ -756,10 +746,50 @@ pub trait SurfnetCheatcodes {
config: Option<ResetAccountConfig>,
) -> Result<RpcResponse<()>>;

/// A cheat code to get Surfnet network information.
/// A cheat code to simulate account streaming.
/// When a transaction is processed, the accounts that are accessed are downloaded from the datasource and cached in the SVM.
/// With this method, you can simulate the streaming of accounts by providing a pubkey.
///
/// ## Parameters
/// - `meta`: Metadata passed with the request, such as the client's request context.
/// - `pubkey_str`: The base-58 encoded public key of the account to stream.
/// - `config`: A `StreamAccountConfig` specifying how to stream the account. If omitted, the account will be streamed without cascading to owned accounts.
///
/// ## Returns
/// An `RpcResponse<()>` indicating whether the account stream registration was successful.
///
/// ## Example Request
/// ```json
/// {
/// "jsonrpc": "2.0",
/// "id": 1,
/// "method": "surfnet_streamAccount",
/// "params": [ "4EXSeLGxVBpAZwq7vm6evLdewpcvE2H56fpqL2pPiLFa", { "includeOwnedAccounts": true } ]
/// }
/// ```
///
/// ## Example Response
/// ```json
/// {
/// "jsonrpc": "2.0",
/// "result": {
/// "context": {
/// "slot": 123456789,
/// "apiVersion": "2.3.8"
/// },
/// "value": null
/// },
/// "id": 1
/// }
/// ```
#[rpc(meta, name = "surfnet_streamAccount")]
fn stream_account(
&self,
meta: Self::Metadata,
pubkey_str: String,
config: Option<StreamAccountConfig>,
) -> Result<RpcResponse<()>>;

/// A cheat code to get Surfnet network information.
///
/// ## Returns
/// A `RpcResponse<GetSurfnetInfoResponse>` containing the Surfnet network information.
Expand Down Expand Up @@ -1320,7 +1350,26 @@ impl SurfnetCheatcodes for SurfnetCheatcodesRpc {
) -> Result<RpcResponse<()>> {
let svm_locker = meta.get_svm_locker()?;
let pubkey = verify_pubkey(&pubkey)?;
svm_locker.reset_account(pubkey, config.unwrap_or_default())?;
let config = config.unwrap_or_default();
let include_owned_accounts = config.include_owned_accounts.unwrap_or_default();
svm_locker.reset_account(pubkey, include_owned_accounts)?;
Ok(RpcResponse {
context: RpcResponseContext::new(svm_locker.get_latest_absolute_slot()),
value: (),
})
}

fn stream_account(
&self,
meta: Self::Metadata,
pubkey_str: String,
config: Option<StreamAccountConfig>,
) -> Result<RpcResponse<()>> {
let svm_locker = meta.get_svm_locker()?;
let pubkey = verify_pubkey(&pubkey_str)?;
let config = config.unwrap_or_default();
let include_owned_accounts = config.include_owned_accounts.unwrap_or_default();
svm_locker.stream_account(pubkey, include_owned_accounts)?;
Ok(RpcResponse {
context: RpcResponseContext::new(svm_locker.get_latest_absolute_slot()),
value: (),
Expand Down
63 changes: 34 additions & 29 deletions crates/core/src/surfnet/locker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ use solana_transaction_status::{
};
use surfpool_types::{
ComputeUnitsEstimationResult, ExecutionCapture, Idl, KeyedProfileResult, ProfileResult,
ResetAccountConfig, RpcProfileResultConfig, RunbookExecutionStatusReport, SimnetCommand,
SimnetEvent, TransactionConfirmationStatus, TransactionStatusEvent, UiKeyedProfileResult,
UuidOrSignature, VersionedIdl,
RpcProfileResultConfig, RunbookExecutionStatusReport, SimnetCommand, SimnetEvent,
TransactionConfirmationStatus, TransactionStatusEvent, UiKeyedProfileResult, UuidOrSignature,
VersionedIdl,
};
use tokio::sync::RwLock;
use txtx_addon_kit::indexmap::IndexSet;
Expand Down Expand Up @@ -1643,34 +1643,39 @@ impl SurfnetSvmLocker {
/// This function coordinates the reset of accounts by calling the SVM's reset_account method.
/// It handles program accounts (including their program data accounts) and can optionally
/// cascade the reset to all accounts owned by a program.
pub fn reset_account(&self, pubkey: Pubkey, config: ResetAccountConfig) -> SurfpoolResult<()> {
let cascade_to_owned = config.recursive.unwrap_or_default();
pub fn reset_account(
&self,
pubkey: Pubkey,
include_owned_accounts: bool,
) -> SurfpoolResult<()> {
let simnet_events_tx = self.simnet_events_tx();
let _ = simnet_events_tx.send(SimnetEvent::info(format!(
"Account {} will be reset",
pubkey
)));
self.with_svm_writer(move |svm_writer| {
if let Some(account) = svm_writer.get_account(&pubkey) {
// Check if this is an executable account (program)
if account.executable {
// Handle upgradeable program - also reset the program data account
if account.owner == solana_sdk_ids::bpf_loader_upgradeable::id() {
let program_data_address =
solana_loader_v3_interface::get_program_data_address(&pubkey);

// Reset the program data account first
svm_writer.reset_account(&program_data_address)?;
}
}
if cascade_to_owned {
let owned_accounts = svm_writer.get_account_owned_by(pubkey);
for (owned_pubkey, _) in owned_accounts {
// Avoid infinite recursion by not cascading further
svm_writer.reset_account(&owned_pubkey)?;
}
}
// Reset the account itself
svm_writer.reset_account(&pubkey)?;
}
Ok(())
svm_writer.reset_account(&pubkey, include_owned_accounts)
})
}

/// Streams an account by its pubkey.
pub fn stream_account(
&self,
pubkey: Pubkey,
include_owned_accounts: bool,
) -> SurfpoolResult<()> {
let simnet_events_tx = self.simnet_events_tx();
let _ = simnet_events_tx.send(SimnetEvent::info(format!(
"Account {} changes will be streamed",
pubkey
)));
self.with_svm_writer(|svm_writer| {
svm_writer
.streamed_accounts
.insert(pubkey, include_owned_accounts);
});
Ok(())
}
}

/// Token account related functions
Expand Down Expand Up @@ -2681,7 +2686,7 @@ impl SurfnetSvmLocker {
filters: Option<Vec<RpcFilterType>>,
) -> SurfpoolContextualizedResult<Vec<RpcKeyedAccount>> {
let res = self.with_svm_reader(|svm_reader| {
let res = svm_reader.get_account_owned_by(*program_id);
let res = svm_reader.get_account_owned_by(program_id);

let mut filtered = vec![];
for (pubkey, account) in &res {
Expand Down
Loading