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
1 change: 1 addition & 0 deletions crates/sui-cluster-test/src/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ pub async fn new_wallet_context_from_cluster(
rpc: fullnode_url.into(),
ws: None,
basic_auth: None,
chain_id: None,
}],
active_address: Some(address),
active_env: Some("localnet".to_string()),
Expand Down
46 changes: 46 additions & 0 deletions crates/sui-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod verifier_signing_config;
pub use node::{ConsensusConfig, ExecutionCacheConfig, NodeConfig};
pub use rpc_config::{RpcConfig, RpcIndexInitConfig, RpcTlsConfig};
use sui_types::multiaddr::Multiaddr;
use tracing::debug;

const SUI_DIR: &str = ".sui";
pub const SUI_CONFIG_DIR: &str = "sui_config";
Expand Down Expand Up @@ -117,6 +118,47 @@ where
.with_context(|| format!("Unable to save config to {}", path.display()))?;
Ok(())
}

/// Load the config from the given path, acquiring a shared lock on the file during the read.
fn load_with_lock<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
let path = path.as_ref();
debug!("Reading config with lock from {}", path.display());
let file = fs::File::open(path)
.with_context(|| format!("Unable to load config from {}", path.display()))?;
file.lock_shared()?;
let config: Self = serde_yaml::from_reader(&file)?;
file.unlock()?;
Ok(config)
}

/// Save the config to the given path, acquiring an exclusive lock on the file during the
/// write.
fn save_with_lock<P: AsRef<Path>>(&self, path: P) -> Result<(), anyhow::Error> {
let path = path.as_ref();
debug!("Writing config with lock to {}", path.display());
let config_str = serde_yaml::to_string(&self)?;

let file = fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.with_context(|| {
format!(
"Unable to open config file for writing at {}",
path.display()
)
})?;

file.lock()
.with_context(|| format!("Unable to acquire exclusive lock on {}", path.display()))?;

fs::write(path, config_str)
.with_context(|| format!("Unable to save config to {}", path.display()))?;

file.unlock()?;
Ok(())
}
}

pub struct PersistedConfig<C> {
Expand All @@ -136,6 +178,10 @@ where
self.inner.save(&self.path)
}

pub fn save_with_lock(&self) -> Result<(), anyhow::Error> {
self.inner.save_with_lock(&self.path)
}

pub fn into_inner(self) -> C {
self.inner
}
Expand Down
25 changes: 25 additions & 0 deletions crates/sui-sdk/src/sui_client_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ impl SuiClientConfig {
self.envs.push(env)
}
}

/// Update the cached chain ID for the specified environment.
pub fn update_env_chain_id(
&mut self,
alias: &str,
chain_id: String,
) -> Result<(), anyhow::Error> {
let env = self
.envs
.iter_mut()
.find(|env| env.alias == alias)
.ok_or_else(|| anyhow!("Environment {} not found", alias))?;
env.chain_id = Some(chain_id);
Ok(())
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -73,6 +88,9 @@ pub struct SuiEnv {
pub ws: Option<String>,
/// Basic HTTP access authentication in the format of username:password, if needed.
pub basic_auth: Option<String>,
/// Cached chain identifier for this environment.
#[serde(skip_serializing_if = "Option::is_none")]
pub chain_id: Option<String>,
}

impl SuiEnv {
Expand Down Expand Up @@ -110,6 +128,7 @@ impl SuiEnv {
rpc: SUI_DEVNET_URL.into(),
ws: None,
basic_auth: None,
chain_id: None,
}
}
pub fn testnet() -> Self {
Expand All @@ -118,6 +137,7 @@ impl SuiEnv {
rpc: SUI_TESTNET_URL.into(),
ws: None,
basic_auth: None,
chain_id: None,
}
}

Expand All @@ -127,6 +147,7 @@ impl SuiEnv {
rpc: SUI_LOCAL_NETWORK_URL.into(),
ws: None,
basic_auth: None,
chain_id: None,
}
}
}
Expand All @@ -144,6 +165,10 @@ impl Display for SuiEnv {
writeln!(writer)?;
write!(writer, "Basic Auth: {}", basic_auth)?;
}
if let Some(chain_id) = &self.chain_id {
writeln!(writer)?;
write!(writer, "Chain ID: {}", chain_id)?;
}
write!(f, "{}", writer)
}
}
Expand Down
39 changes: 39 additions & 0 deletions crates/sui-sdk/src/wallet_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use sui_types::crypto::{Signature, SuiKeyPair};
use sui_types::gas_coin::GasCoin;
use sui_types::transaction::{Transaction, TransactionData, TransactionDataAPI};
use tokio::sync::RwLock;
use tracing::info;

pub struct WalletContext {
pub config: PersistedConfig<SuiClientConfig>,
Expand Down Expand Up @@ -127,10 +128,48 @@ impl WalletContext {
.get_active_env()?
.create_rpc_client(self.request_timeout, self.max_concurrent_requests)
.await?;

self.client.write().await.insert(client).clone()
})
}

/// Load the chain ID corresponding to the active environment, or fetch and cache it if not
/// present.
///
/// The chain ID is cached in the `client.yaml` file to avoid redundant network requests.
pub async fn load_or_cache_chain_id(
&self,
client: &SuiClient,
) -> Result<String, anyhow::Error> {
self.internal_load_or_cache_chain_id(client, false).await
}

/// Cache (or recache) chain ID for the active environment by fetching it from the
/// network
pub async fn cache_chain_id(&self, client: &SuiClient) -> Result<String, anyhow::Error> {
self.internal_load_or_cache_chain_id(client, true).await
}

async fn internal_load_or_cache_chain_id(
&self,
client: &SuiClient,
force_recache: bool,
) -> Result<String, anyhow::Error> {
let env = self.get_active_env()?;
if !force_recache && env.chain_id.is_some() {
let chain_id = env.chain_id.as_ref().unwrap();
info!("Found cached chain ID for env {}: {}", env.alias, chain_id);
return Ok(chain_id.clone());
}
let chain_id = client.read_api().get_chain_identifier().await?;
let path = self.config.path();
let mut config_result = SuiClientConfig::load_with_lock(path)?;

config_result.update_env_chain_id(&env.alias, chain_id.clone())?;
config_result.save_with_lock(path)?;
Ok(chain_id)
}

pub fn get_active_env(&self) -> Result<&SuiEnv, anyhow::Error> {
if self.env_override.is_some() {
self.config.get_env(&self.env_override).ok_or_else(|| {
Expand Down
Loading
Loading