Skip to content

Commit 552e63d

Browse files
authored
Extend reconfig test; sync on wallet init; fix blob fetching. (#4437)
## Motivation We want to make sure the network works fine even after many reconfigurations, and even if none of the genesis validators are left. ## Proposal At the end of the reconfiguration end-to-end test, initialize a new client, request a chain and publish a blob. This test actually failed! The following two changes make it pass: * In `wallet init`, synchronize the admin chain, trusting the validators provided by the faucet (part of #4434). * Fix a bug in `process_certificates`: Since we are handling a _certificate_, not a block proposal, we don't need additional proof for the missing blobs: The certificate itself is proof for them. So we should only download the blobs themselves, and not try to get another certificate for them. ## Test Plan The reconfiguration test was extended. ## Release Plan - These changes should be backported to the latest `testnet` branch, then - be released in a new SDK. ## Links - _Partly_ addresses #4434. - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 9162407 commit 552e63d

File tree

4 files changed

+82
-14
lines changed

4 files changed

+82
-14
lines changed

linera-core/src/client/mod.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -376,18 +376,11 @@ impl<Env: Environment> Client<Env> {
376376
let mut result = self.handle_certificate(certificate.clone()).await;
377377

378378
if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
379-
future::try_join_all(blob_ids.iter().map(|blob_id| async move {
380-
let blob_certificate =
381-
remote_node.download_certificate_for_blob(*blob_id).await?;
382-
self.receive_sender_certificate(
383-
blob_certificate,
384-
ReceiveCertificateMode::NeedsCheck,
385-
None,
386-
)
387-
.await?;
388-
Result::<(), ChainClientError>::Ok(())
379+
let blobs = future::join_all(blob_ids.iter().map(|blob_id| async move {
380+
remote_node.try_download_blob(*blob_id).await.unwrap()
389381
}))
390-
.await?;
382+
.await;
383+
self.local_node.store_blobs(&blobs).await?;
391384
result = self.handle_certificate(certificate.clone()).await;
392385
}
393386

@@ -930,11 +923,23 @@ impl<Env: Environment> Client<Env> {
930923
async fn synchronize_chain_state(
931924
&self,
932925
chain_id: ChainId,
926+
) -> Result<Box<ChainInfo>, ChainClientError> {
927+
let (_, committee) = self.admin_committee().await?;
928+
self.synchronize_chain_state_from_committee(chain_id, committee)
929+
.await
930+
}
931+
932+
/// Downloads and processes any certificates we are missing for the given chain, from the given
933+
/// committee.
934+
#[instrument(level = "trace", skip_all)]
935+
pub async fn synchronize_chain_state_from_committee(
936+
&self,
937+
chain_id: ChainId,
938+
committee: Committee,
933939
) -> Result<Box<ChainInfo>, ChainClientError> {
934940
#[cfg(with_metrics)]
935941
let _latency = metrics::SYNCHRONIZE_CHAIN_STATE_LATENCY.measure_latency();
936942

937-
let (_, committee) = self.admin_committee().await?;
938943
let validators = self.make_nodes(&committee)?;
939944
Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
940945
communicate_with_quorum(
@@ -2297,6 +2302,18 @@ impl<Env: Environment> ChainClient<Env> {
22972302
self.client.synchronize_chain_state(chain_id).await
22982303
}
22992304

2305+
/// Downloads and processes any certificates we are missing for this chain, from the given
2306+
/// committee.
2307+
#[instrument(level = "trace", skip_all)]
2308+
pub async fn synchronize_chain_state_from_committee(
2309+
&self,
2310+
committee: Committee,
2311+
) -> Result<Box<ChainInfo>, ChainClientError> {
2312+
self.client
2313+
.synchronize_chain_state_from_committee(self.chain_id, committee)
2314+
.await
2315+
}
2316+
23002317
/// Executes a list of operations.
23012318
#[instrument(level = "trace", skip(operations, blobs))]
23022319
pub async fn execute_operations(

linera-core/src/remote_node.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ impl<N: ValidatorNode> RemoteNode<N> {
210210
}
211211

212212
#[instrument(level = "trace")]
213-
async fn try_download_blob(&self, blob_id: BlobId) -> Option<Blob> {
213+
pub async fn try_download_blob(&self, blob_id: BlobId) -> Option<Blob> {
214214
match self.node.download_blob(blob_id).await {
215215
Ok(blob) => {
216216
let blob = Blob::new(blob);

linera-service/src/cli/main.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use chrono::Utc;
1919
use colored::Colorize;
2020
use futures::{lock::Mutex, FutureExt as _, StreamExt};
2121
use linera_base::{
22-
crypto::{InMemorySigner, Signer},
22+
crypto::{AccountPublicKey, InMemorySigner, Signer},
2323
data_types::{ApplicationPermissions, Timestamp},
2424
identifiers::{AccountOwner, ChainId},
2525
listen_for_shutdown_signals,
@@ -1610,6 +1610,43 @@ impl Runnable for Job {
16101610
);
16111611
}
16121612

1613+
Wallet(WalletCommand::Init { faucet, .. }) => {
1614+
let Some(faucet_url) = faucet else {
1615+
return Ok(());
1616+
};
1617+
let Some(network_description) = storage.read_network_description().await? else {
1618+
anyhow::bail!("Missing network description");
1619+
};
1620+
let context = ClientContext::new(
1621+
storage,
1622+
options.context_options.clone(),
1623+
wallet,
1624+
signer.into_value(),
1625+
);
1626+
let faucet = cli_wrappers::Faucet::new(faucet_url);
1627+
let validators = faucet.current_validators().await?;
1628+
let chain_client = context.make_chain_client(network_description.admin_chain_id);
1629+
// TODO(#4434): This is a quick workaround with an equal-weight committee. Instead,
1630+
// the faucet should provide the full committee including weights.
1631+
let committee = Committee::new(
1632+
validators
1633+
.into_iter()
1634+
.map(|(pub_key, network_address)| {
1635+
let state = ValidatorState {
1636+
network_address,
1637+
votes: 100,
1638+
account_public_key: AccountPublicKey::from_slice(&[0; 33]).unwrap(),
1639+
};
1640+
(pub_key, state)
1641+
})
1642+
.collect(),
1643+
Default::default(), // unused
1644+
);
1645+
chain_client
1646+
.synchronize_chain_state_from_committee(committee)
1647+
.await?;
1648+
}
1649+
16131650
Wallet(WalletCommand::FollowChain {
16141651
chain_id,
16151652
sync: true,
@@ -2431,6 +2468,7 @@ Make sure to use a Linera client compatible with this network.
24312468
keystore.persist().await?;
24322469
options.create_wallet(genesis_config)?.persist().await?;
24332470
options.initialize_storage().boxed().await?;
2471+
options.run_with_storage(Job(options.clone())).await??;
24342472
info!(
24352473
"Wallet initialized in {} ms",
24362474
start_time.elapsed().as_millis()

linera-service/tests/local_net_tests.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,19 @@ async fn test_end_to_end_reconfiguration(config: LocalNetConfig) -> Result<()> {
237237
);
238238
}
239239

240+
if matches!(network, Network::Grpc) {
241+
let client = net.make_client().await;
242+
client.wallet_init(Some(&faucet)).await?;
243+
let (chain_id, _owner) = client.request_chain(&faucet, true).await?;
244+
let port = get_node_port().await;
245+
let service = client
246+
.run_node_service(port, ProcessInbox::Automatic)
247+
.await?;
248+
service
249+
.publish_data_blob(&chain_id, b"blob bytes".to_vec())
250+
.await?;
251+
}
252+
240253
net.ensure_is_running().await?;
241254
net.terminate().await?;
242255

0 commit comments

Comments
 (0)