Skip to content

Commit ad89e29

Browse files
authored
Remove 2 deprecated CLI options of linera wallet init. (#3910)
## Motivation `--with-new-chain` and `--with-other-chains` are not necessary anymore; `request-chain` and `follow-chain` should be used instead. ## Proposal Remove them, and the `FaucetOption` enum. Also don't print the latest hash anymore: This was meant to avoid trusting the faucet or expired committees, but we already don't sync on `wallet init` anymore, so it's in the wrong place. I created #3909 to revisit this once we address the rest of the long-range attack issue. ## Test Plan The tests were updated to use `request-chain` instead. ## Release Plan - Before the next testnet, the Linera Manual needs to be updated. ## Links - Closes #3399. - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 61e7cf7 commit ad89e29

File tree

10 files changed

+54
-236
lines changed

10 files changed

+54
-236
lines changed

CLI.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,6 @@ Initialize a wallet from the genesis configuration
820820

821821
* `--genesis <GENESIS_CONFIG_PATH>` — The path to the genesis configuration for a Linera deployment. Either this or `--faucet` must be specified
822822
* `--faucet <FAUCET>` — The address of a faucet
823-
* `--with-new-chain` — Request a new chain from the faucet, credited with tokens. This requires `--faucet`
824-
* `--with-other-chains <WITH_OTHER_CHAINS>` — Other chains to follow
825823
* `--testing-prng-seed <TESTING_PRNG_SEED>` — Force this wallet to generate keys using a PRNG and a given seed. USE FOR TESTING ONLY
826824

827825

linera-service/src/benchmark.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use linera_base::{
1616
use linera_sdk::abis::fungible::{self, FungibleTokenAbi, InitialState, Parameters};
1717
use linera_service::cli_wrappers::{
1818
local_net::{PathProvider, ProcessInbox},
19-
ApplicationWrapper, ClientWrapper, Faucet, FaucetOption, Network, OnClientDrop,
19+
ApplicationWrapper, ClientWrapper, Faucet, Network, OnClientDrop,
2020
};
2121
use port_selector::random_free_tcp_port;
2222
use rand::{Rng as _, SeedableRng};
@@ -87,9 +87,8 @@ async fn benchmark_with_fungible(
8787
num_wallets,
8888
OnClientDrop::CloseChains,
8989
);
90-
publisher
91-
.wallet_init(&[], FaucetOption::NewChain(&faucet))
92-
.await?;
90+
publisher.wallet_init(Some(&faucet)).await?;
91+
publisher.request_chain(&faucet, true).await?;
9392
let clients = (0..num_wallets)
9493
.map(|n| {
9594
let path_provider = PathProvider::create_temporary_directory().unwrap();
@@ -102,11 +101,10 @@ async fn benchmark_with_fungible(
102101
))
103102
})
104103
.collect::<Result<Vec<_>, anyhow::Error>>()?;
105-
try_join_all(
106-
clients
107-
.iter()
108-
.map(|client| client.wallet_init(&[], FaucetOption::NewChain(&faucet))),
109-
)
104+
try_join_all(clients.iter().map(|client| async {
105+
client.wallet_init(Some(&faucet)).await?;
106+
client.request_chain(&faucet, true).await
107+
}))
110108
.await?;
111109

112110
info!("Synchronizing balances (sanity check)");

linera-service/src/cli_wrappers/local_kubernetes_net.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ use linera_client::client_options::ResourceControlPolicyConfig;
1616
use tempfile::{tempdir, TempDir};
1717
use tokio::process::Command;
1818
#[cfg(with_testing)]
19-
use {
20-
crate::cli_wrappers::wallet::FaucetOption, linera_base::command::current_binary_parent,
21-
tokio::sync::OnceCell,
22-
};
19+
use {linera_base::command::current_binary_parent, tokio::sync::OnceCell};
2320

2421
use crate::cli_wrappers::{
2522
docker::{BuildArg, DockerImage},
@@ -203,7 +200,7 @@ impl LineraNetConfig for SharedLocalKubernetesNetTestingConfig {
203200
let client = net.make_client().await;
204201
// The tests assume we've created a genesis config with 2
205202
// chains with 10 tokens each.
206-
client.wallet_init(&[], FaucetOption::None).await.unwrap();
203+
client.wallet_init(None).await.unwrap();
207204
for _ in 0..2 {
208205
initial_client
209206
.open_and_assign(&client, Amount::from_tokens(10))

linera-service/src/cli_wrappers/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ mod wallet;
3434
use anyhow::Result;
3535
use async_trait::async_trait;
3636
pub use linera_faucet_client::Faucet;
37-
pub use wallet::{
38-
ApplicationWrapper, ClientWrapper, FaucetOption, FaucetService, NodeService, OnClientDrop,
39-
};
37+
pub use wallet::{ApplicationWrapper, ClientWrapper, FaucetService, NodeService, OnClientDrop};
4038

4139
/// The information needed to start a Linera net of a particular kind.
4240
#[async_trait]

linera-service/src/cli_wrappers/remote_net.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use linera_client::persistent::{self, Persist};
1010
use tempfile::{tempdir, TempDir};
1111

1212
use super::{
13-
local_net::PathProvider, ClientWrapper, Faucet, FaucetOption, LineraNet, LineraNetConfig,
14-
Network, OnClientDrop,
13+
local_net::PathProvider, ClientWrapper, Faucet, LineraNet, LineraNetConfig, Network,
14+
OnClientDrop,
1515
};
1616

1717
pub struct RemoteNetTestingConfig {
@@ -50,10 +50,8 @@ impl LineraNetConfig for RemoteNetTestingConfig {
5050
let client = net.make_client().await;
5151
// The tests assume we've created a genesis config with 2
5252
// chains with 10 tokens each. We create the first chain here
53-
client
54-
.wallet_init(&[], FaucetOption::NewChain(&self.faucet))
55-
.await
56-
.unwrap();
53+
client.wallet_init(Some(&self.faucet)).await?;
54+
client.request_chain(&self.faucet, true).await?;
5755

5856
// And the remaining 2 here
5957
for _ in 0..2 {

linera-service/src/cli_wrappers/wallet.rs

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -267,52 +267,20 @@ impl ClientWrapper {
267267
Ok(())
268268
}
269269

270-
/// Runs `linera wallet init`.
271-
pub async fn wallet_init(
272-
&self,
273-
chain_ids: &[ChainId],
274-
faucet: FaucetOption<'_>,
275-
) -> Result<Option<(ClaimOutcome, AccountOwner)>> {
270+
/// Runs `linera wallet init`. The genesis config is read from `genesis.json`, or from the
271+
/// faucet if provided.
272+
pub async fn wallet_init(&self, faucet: Option<&'_ Faucet>) -> Result<()> {
276273
let mut command = self.command().await?;
277274
command.args(["wallet", "init"]);
278275
match faucet {
279-
FaucetOption::None => {
280-
command.args(["--genesis", "genesis.json"]);
281-
}
282-
FaucetOption::GenesisOnly(faucet) => {
283-
command.args(["--faucet", faucet.url()]);
284-
}
285-
FaucetOption::NewChain(faucet) => {
286-
command.args(["--with-new-chain", "--faucet", faucet.url()]);
287-
}
288-
}
276+
None => command.args(["--genesis", "genesis.json"]),
277+
Some(faucet) => command.args(["--faucet", faucet.url()]),
278+
};
289279
if let Some(seed) = self.testing_prng_seed {
290280
command.arg("--testing-prng-seed").arg(seed.to_string());
291281
}
292-
if !chain_ids.is_empty() {
293-
let ids = chain_ids.iter().map(ChainId::to_string);
294-
command.arg("--with-other-chains").args(ids);
295-
}
296-
let stdout = command.spawn_and_wait_for_stdout().await?;
297-
if matches!(faucet, FaucetOption::NewChain(_)) {
298-
let mut lines = stdout.split_whitespace();
299-
let chain_id_str = lines.next().context("missing chain ID")?;
300-
let certificate_hash_str = lines.next().context("missing certificate hash")?;
301-
let outcome = ClaimOutcome {
302-
chain_id: chain_id_str.parse().context("invalid chain ID")?,
303-
certificate_hash: certificate_hash_str
304-
.parse()
305-
.context("invalid certificate hash")?,
306-
};
307-
let owner = lines
308-
.next()
309-
.context("missing chain owner")?
310-
.parse()
311-
.context("invalid chain owner")?;
312-
Ok(Some((outcome, owner)))
313-
} else {
314-
Ok(None)
315-
}
282+
command.spawn_and_wait_for_stdout().await?;
283+
Ok(())
316284
}
317285

318286
/// Runs `linera wallet request-chain`.
@@ -1066,14 +1034,6 @@ impl Drop for ClientWrapper {
10661034
}
10671035
}
10681036

1069-
/// Whether `wallet_init` should use a faucet.
1070-
#[derive(Clone, Copy, Debug)]
1071-
pub enum FaucetOption<'a> {
1072-
None,
1073-
GenesisOnly(&'a Faucet),
1074-
NewChain(&'a Faucet),
1075-
}
1076-
10771037
#[cfg(with_testing)]
10781038
impl ClientWrapper {
10791039
pub async fn build_example(&self, name: &str) -> Result<(PathBuf, PathBuf)> {

linera-service/src/linera/command.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,14 +1032,6 @@ pub enum WalletCommand {
10321032
#[arg(long = "faucet")]
10331033
faucet: Option<String>,
10341034

1035-
/// Request a new chain from the faucet, credited with tokens. This requires `--faucet`.
1036-
#[arg(long)]
1037-
with_new_chain: bool,
1038-
1039-
/// Other chains to follow.
1040-
#[arg(long, num_args(0..))]
1041-
with_other_chains: Vec<ChainId>,
1042-
10431035
/// Force this wallet to generate keys using a PRNG and a given seed. USE FOR
10441036
/// TESTING ONLY.
10451037
#[arg(long)]

linera-service/src/linera/main.rs

Lines changed: 4 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#![deny(clippy::large_futures)]
77

88
use std::{
9-
collections::{BTreeMap, BTreeSet, HashMap},
9+
collections::{BTreeMap, BTreeSet},
1010
env,
1111
ops::Deref,
1212
path::PathBuf,
@@ -23,11 +23,11 @@ use command::{ClientCommand, DatabaseToolCommand, NetCommand, ProjectCommand, Wa
2323
use futures::{lock::Mutex, FutureExt as _, StreamExt};
2424
use linera_base::{
2525
bcs,
26-
crypto::{CryptoHash, InMemorySigner, Signer},
26+
crypto::{InMemorySigner, Signer},
2727
data_types::{
2828
ApplicationPermissions, ChainDescription, ChainOrigin, Epoch, InitialChainConfig, Timestamp,
2929
},
30-
identifiers::{AccountOwner, ChainId},
30+
identifiers::AccountOwner,
3131
listen_for_shutdown_signals,
3232
ownership::ChainOwnership,
3333
};
@@ -1235,48 +1235,6 @@ impl Runnable for Job {
12351235
);
12361236
}
12371237

1238-
Wallet(WalletCommand::Init {
1239-
faucet: Some(faucet_url),
1240-
with_new_chain: true,
1241-
with_other_chains,
1242-
..
1243-
}) => {
1244-
let start_time = Instant::now();
1245-
let public_key = signer.mutate(|s| s.generate_new()).await?;
1246-
let mut context = ClientContext::new(
1247-
storage.clone(),
1248-
options.inner.clone(),
1249-
wallet,
1250-
Box::new(signer.into_value()),
1251-
);
1252-
let owner: AccountOwner = public_key.into();
1253-
info!(
1254-
"Requesting a new chain for owner {owner} using the faucet at address \
1255-
{faucet_url}",
1256-
);
1257-
let faucet = cli_wrappers::Faucet::new(faucet_url);
1258-
let outcome = faucet.claim(&owner).await?;
1259-
println!("{}", outcome.chain_id);
1260-
println!("{}", outcome.certificate_hash);
1261-
println!("{}", owner);
1262-
context
1263-
.assign_new_chain_to_key(outcome.chain_id, owner)
1264-
.await?;
1265-
let admin_id = context.wallet().genesis_admin_chain();
1266-
let chains = with_other_chains
1267-
.into_iter()
1268-
.chain([admin_id, outcome.chain_id]);
1269-
Self::print_peg_certificate_hash(storage, chains, &context).await?;
1270-
context
1271-
.wallet_mut()
1272-
.mutate(|w| w.set_default_chain(outcome.chain_id))
1273-
.await??;
1274-
info!(
1275-
"Wallet initialized in {} ms",
1276-
start_time.elapsed().as_millis()
1277-
);
1278-
}
1279-
12801238
Wallet(WalletCommand::RequestChain {
12811239
faucet: faucet_url,
12821240
set_default,
@@ -1328,67 +1286,6 @@ impl Runnable for Job {
13281286
}
13291287
}
13301288

1331-
impl Job {
1332-
/// Prints a warning message to explain that the wallet has been initialized using data from
1333-
/// untrusted nodes, and gives instructions to verify that we are connected to the right
1334-
/// network.
1335-
async fn print_peg_certificate_hash<S>(
1336-
storage: S,
1337-
chain_ids: impl IntoIterator<Item = ChainId>,
1338-
context: &ClientContext<impl linera_core::Environment, impl Persist<Target = Wallet>>,
1339-
) -> anyhow::Result<()>
1340-
where
1341-
S: Storage + Clone + Send + Sync + 'static,
1342-
{
1343-
let mut chains = HashMap::new();
1344-
for chain_id in chain_ids {
1345-
if chains.contains_key(&chain_id) {
1346-
continue;
1347-
}
1348-
chains.insert(chain_id, storage.load_chain(chain_id).await?);
1349-
}
1350-
// Find a chain with the latest known epoch, preferably the admin chain.
1351-
let (peg_chain_id, _) = chains
1352-
.iter()
1353-
.filter_map(|(chain_id, chain)| {
1354-
let epoch = (*chain.execution_state.system.epoch.get())?;
1355-
let is_admin = Some(*chain_id) == *chain.execution_state.system.admin_id.get();
1356-
Some((*chain_id, (epoch, is_admin)))
1357-
})
1358-
.max_by_key(|(_, epoch)| *epoch)
1359-
.context("no active chain found")?;
1360-
let peg_chain = chains.remove(&peg_chain_id).unwrap();
1361-
// These are the still-trusted committees. Every chain tip should be signed by one of them.
1362-
let committees = peg_chain.execution_state.system.committees.get();
1363-
for (chain_id, chain) in &chains {
1364-
let Some(hash) = chain.tip_state.get().block_hash else {
1365-
continue; // This chain was created based on the genesis config.
1366-
};
1367-
let certificate = storage.read_certificate(hash).await?;
1368-
let committee = committees
1369-
.get(&certificate.block().header.epoch)
1370-
.ok_or_else(|| anyhow!("tip of chain {chain_id} is outdated."))?;
1371-
certificate.check(committee)?;
1372-
}
1373-
// This proves that once we have verified that the peg chain's tip is a block in the real
1374-
// network, we can be confident that all downloaded chains are.
1375-
let config_hash = CryptoHash::new(context.wallet.genesis_config());
1376-
let maybe_epoch = peg_chain.execution_state.system.epoch.get();
1377-
let epoch = maybe_epoch.context("missing epoch in peg chain")?.0;
1378-
info!(
1379-
"Initialized wallet based on data provided by the faucet.\n\
1380-
The current epoch is {epoch}.\n\
1381-
The genesis config hash is {config_hash}{}",
1382-
if let Some(peg_hash) = peg_chain.tip_state.get().block_hash {
1383-
format!("\nThe latest certificate on chain {peg_chain_id} is {peg_hash}.")
1384-
} else {
1385-
"".to_string()
1386-
}
1387-
);
1388-
Ok(())
1389-
}
1390-
}
1391-
13921289
#[derive(Clone, clap::Parser)]
13931290
#[command(
13941291
name = "linera",
@@ -2188,8 +2085,6 @@ async fn run(options: &ClientOptions) -> Result<i32, Error> {
21882085
WalletCommand::Init {
21892086
genesis_config_path,
21902087
faucet,
2191-
with_new_chain,
2192-
with_other_chains,
21932088
testing_prng_seed,
21942089
} => {
21952090
let start_time = Instant::now();
@@ -2222,27 +2117,10 @@ Make sure to use a Linera client compatible with this network.
22222117
}
22232118
(_, _) => bail!("Either --faucet or --genesis must be specified, but not both"),
22242119
};
2225-
let timestamp = genesis_config.timestamp;
22262120
let mut keystore = options.create_keystore(*testing_prng_seed)?;
22272121
keystore.persist().await?;
2228-
options
2229-
.create_wallet(genesis_config)?
2230-
.mutate(|wallet| {
2231-
wallet.extend(
2232-
with_other_chains
2233-
.iter()
2234-
.map(|chain_id| UserChain::make_other(*chain_id, timestamp)),
2235-
)
2236-
})
2237-
.await?;
2122+
options.create_wallet(genesis_config)?.persist().await?;
22382123
options.initialize_storage().boxed().await?;
2239-
if *with_new_chain {
2240-
ensure!(
2241-
faucet.is_some(),
2242-
"Using --with-new-chain requires --faucet to be set"
2243-
);
2244-
options.run_with_storage(Job(options.clone())).await??;
2245-
}
22462124
info!(
22472125
"Wallet initialized in {} ms",
22482126
start_time.elapsed().as_millis()

0 commit comments

Comments
 (0)