Skip to content

Commit

Permalink
Fix close_chain errors. (linera-io#2957)
Browse files Browse the repository at this point in the history
  • Loading branch information
afck authored Nov 26, 2024
1 parent 7a31be5 commit 1a21173
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 38 deletions.
1 change: 1 addition & 0 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ Show the contents of the wallet
###### **Options:**

* `--short` — Only print a non-formatted list of the wallet's chain IDs
* `--owned` — Print only the chains that we have a key pair for



Expand Down
3 changes: 3 additions & 0 deletions linera-client/src/client_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,9 @@ pub enum WalletCommand {
/// Only print a non-formatted list of the wallet's chain IDs.
#[arg(long)]
short: bool,
/// Print only the chains that we have a key pair for.
#[arg(long)]
owned: bool,
},

/// Change the wallet default chain.
Expand Down
7 changes: 7 additions & 0 deletions linera-client/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ impl Wallet {
self.chains.keys().copied().collect()
}

pub fn owned_chain_ids(&self) -> Vec<ChainId> {
self.chains
.iter()
.filter_map(|(chain_id, chain)| chain.key_pair.is_some().then_some(*chain_id))
.collect()
}

/// Returns the list of all chain IDs for which we have a secret key.
pub fn own_chain_ids(&self) -> Vec<ChainId> {
self.chains
Expand Down
15 changes: 12 additions & 3 deletions linera-core/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2685,12 +2685,21 @@ where
}

/// Closes the chain (and loses everything in it!!).
/// Returns `None` if the chain was already closed.
#[instrument(level = "trace")]
pub async fn close_chain(
&self,
) -> Result<ClientOutcome<ConfirmedBlockCertificate>, ChainClientError> {
self.execute_operation(Operation::System(SystemOperation::CloseChain))
.await
) -> Result<ClientOutcome<Option<ConfirmedBlockCertificate>>, ChainClientError> {
let operation = Operation::System(SystemOperation::CloseChain);
match self.execute_operation(operation).await {
Ok(outcome) => Ok(outcome.map(Some)),
Err(ChainClientError::LocalNodeError(LocalNodeError::WorkerError(
WorkerError::ChainError(chain_error),
))) if matches!(*chain_error, ChainError::ClosedChain) => {
Ok(ClientOutcome::Committed(None)) // Chain is already closed.
}
Err(error) => Err(error),
}
}

/// Publishes some bytecode.
Expand Down
6 changes: 5 additions & 1 deletion linera-core/src/unit_tests/client_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ where
.add_initial_chain(ChainDescription::Root(2), Amount::from_tokens(4))
.await?;

let certificate = client1.close_chain().await.unwrap().unwrap();
let certificate = client1.close_chain().await.unwrap().unwrap().unwrap();
assert_matches!(
certificate.executed_block().block.operations[..],
[Operation::System(SystemOperation::CloseChain)],
Expand Down Expand Up @@ -890,6 +890,10 @@ where
LocalNodeError::WorkerError(WorkerError::ChainError(error))
)) if matches!(*error, ChainError::ClosedChain)
);

// Trying to close the chain again returns None.
let maybe_certificate = client1.close_chain().await.unwrap().unwrap();
assert_matches!(maybe_certificate, None);
Ok(())
}

Expand Down
4 changes: 2 additions & 2 deletions linera-service-graphql-client/gql/service_schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -628,9 +628,9 @@ type MutationRoot {
fallbackDurationMs: Int! = 86400000
): ChainId!
"""
Closes the chain.
Closes the chain. Returns `None` if it was already closed.
"""
closeChain(chainId: ChainId!): CryptoHash!
closeChain(chainId: ChainId!): CryptoHash
"""
Changes the authentication key of the chain.
"""
Expand Down
2 changes: 1 addition & 1 deletion linera-service/src/cli_wrappers/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ impl Drop for ClientWrapper {

let Ok(wallet_show_output) = wallet_show_command
.current_dir(working_directory)
.args(["wallet", "show", "--short"])
.args(["wallet", "show", "--short", "--owned"])
.output()
else {
warn!("Failed to execute `wallet show --short` to list chains to close");
Expand Down
31 changes: 25 additions & 6 deletions linera-service/src/linera/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,20 @@ impl Runnable for Job {
let chain_client = context.make_chain_client(chain_id)?;
info!("Closing chain {}", chain_id);
let time_start = Instant::now();
let certificate = context
let result = context
.apply_client_command(&chain_client, |chain_client| {
let chain_client = chain_client.clone();
async move { chain_client.close_chain().await }
})
.await
.context("Failed to close chain")?;
.await;
let certificate = match result {
Ok(Some(certificate)) => certificate,
Ok(None) => {
tracing::info!("Chain is already closed; nothing to do.");
return Ok(());
}
Err(error) => Err(error).context("Failed to close chain")?,
};
let time_total = time_start.elapsed();
info!(
"Closing chain confirmed after {} ms",
Expand Down Expand Up @@ -1708,14 +1715,26 @@ async fn run(options: &ClientOptions) -> Result<i32, anyhow::Error> {
}

ClientCommand::Wallet(wallet_command) => match wallet_command {
WalletCommand::Show { chain_id, short } => {
WalletCommand::Show {
chain_id,
short,
owned,
} => {
let start_time = Instant::now();
let chain_ids = if let Some(chain_id) = chain_id {
ensure!(!owned, "Cannot specify both --owned and a chain ID");
vec![*chain_id]
} else if *owned {
options.wallet().await?.owned_chain_ids()
} else {
options.wallet().await?.chain_ids()
};
if *short {
for chain_id in options.wallet().await?.chains.keys() {
for chain_id in chain_ids {
println!("{chain_id}");
}
} else {
wallet::pretty_print(&*options.wallet().await?, *chain_id);
wallet::pretty_print(&*options.wallet().await?, chain_ids);
}
info!("Wallet shown in {} ms", start_time.elapsed().as_millis());
Ok(0)
Expand Down
13 changes: 8 additions & 5 deletions linera-service/src/node_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ use linera_base::{
ownership::{ChainOwnership, TimeoutConfig},
BcsHexParseError,
};
use linera_chain::{types::HashedCertificateValue, ChainStateView};
use linera_chain::{
types::{GenericCertificate, HashedCertificateValue},
ChainStateView,
};
use linera_client::chain_listener::{ChainListener, ChainListenerConfig, ClientContext};
use linera_core::{
client::{ChainClient, ChainClientError},
Expand Down Expand Up @@ -423,15 +426,15 @@ where
Ok(ChainId::child(message_id))
}

/// Closes the chain.
async fn close_chain(&self, chain_id: ChainId) -> Result<CryptoHash, Error> {
let certificate = self
/// Closes the chain. Returns `None` if it was already closed.
async fn close_chain(&self, chain_id: ChainId) -> Result<Option<CryptoHash>, Error> {
let maybe_cert = self
.apply_client_command(&chain_id, |client| async move {
let result = client.close_chain().await.map_err(Error::from);
(result, client)
})
.await?;
Ok(certificate.hash())
Ok(maybe_cert.as_ref().map(GenericCertificate::hash))
}

/// Changes the authentication key of the chain.
Expand Down
30 changes: 10 additions & 20 deletions linera-service/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use comfy_table::{
use linera_base::identifiers::{ChainId, Owner};
pub use linera_client::wallet::*;

pub fn pretty_print(wallet: &Wallet, chain_id: Option<ChainId>) {
pub fn pretty_print(wallet: &Wallet, chain_ids: impl IntoIterator<Item = ChainId>) {
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
Expand All @@ -18,26 +18,16 @@ pub fn pretty_print(wallet: &Wallet, chain_id: Option<ChainId>) {
Cell::new("Chain ID").add_attribute(Attribute::Bold),
Cell::new("Latest Block").add_attribute(Attribute::Bold),
]);
if let Some(chain_id) = chain_id {
if let Some(user_chain) = wallet.chains.get(&chain_id) {
update_table_with_chain(
&mut table,
chain_id,
user_chain,
Some(chain_id) == wallet.default,
);
} else {
for chain_id in chain_ids {
let Some(user_chain) = wallet.chains.get(&chain_id) else {
panic!("Chain {} not found.", chain_id);
}
} else {
for (chain_id, user_chain) in &wallet.chains {
update_table_with_chain(
&mut table,
*chain_id,
user_chain,
Some(chain_id) == wallet.default.as_ref(),
);
}
};
update_table_with_chain(
&mut table,
chain_id,
user_chain,
Some(chain_id) == wallet.default,
);
}
println!("{}", table);
}
Expand Down

0 comments on commit 1a21173

Please sign in to comment.