Skip to content

Commit 69731d3

Browse files
authored
revoke-epochs (#4070)
## Motivation `finalize-committee` currently revokes all but the latest epoch. But sometimes we may want to keep more than one epoch. ## Proposal Change this to `revoke-epochs <EPOCH>` and revoke only up to the specified one. ## Test Plan CI; tests were updated. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links - Closes #3948 - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent e3b1e5b commit 69731d3

File tree

8 files changed

+63
-26
lines changed

8 files changed

+63
-26
lines changed

CLI.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ This document contains the help content for the `linera` command-line program.
2222
* [`linera sync-validator`](#linera-sync-validator)
2323
* [`linera set-validator`](#linera-set-validator)
2424
* [`linera remove-validator`](#linera-remove-validator)
25-
* [`linera finalize-committee`](#linera-finalize-committee)
25+
* [`linera revoke-epochs`](#linera-revoke-epochs)
2626
* [`linera resource-control-policy`](#linera-resource-control-policy)
2727
* [`linera create-genesis-config`](#linera-create-genesis-config)
2828
* [`linera watch`](#linera-watch)
@@ -86,7 +86,7 @@ A Byzantine-fault tolerant sidechain with low-latency finality and high throughp
8686
* `sync-validator` — Synchronizes a validator with the local state of chains
8787
* `set-validator` — Add or modify a validator (admin only)
8888
* `remove-validator` — Remove a validator (admin only)
89-
* `finalize-committee` — Deprecates all committees except the last one
89+
* `revoke-epochs` — Deprecates all committees up to and including the specified one
9090
* `resource-control-policy` — View or update the resource control policy
9191
* `create-genesis-config` — Create genesis configuration for a Linera deployment. Create initial user chains and print information to be used for initialization of validator setup. This will also create an initial wallet for the owner of the initial "root" chains
9292
* `watch` — Watch the network for notifications
@@ -461,11 +461,15 @@ Remove a validator (admin only)
461461

462462

463463

464-
## `linera finalize-committee`
464+
## `linera revoke-epochs`
465465

466-
Deprecates all committees except the last one
466+
Deprecates all committees up to and including the specified one
467467

468-
**Usage:** `linera finalize-committee`
468+
**Usage:** `linera revoke-epochs <EPOCH>`
469+
470+
###### **Arguments:**
471+
472+
* `<EPOCH>`
469473

470474

471475

linera-base/src/data_types.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,13 @@ impl Epoch {
812812
Ok(Self(val))
813813
}
814814

815+
/// Tries to return an epoch with a number decreased by one. Returns an error if an underflow
816+
/// happens.
817+
pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
818+
let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
819+
Ok(Self(val))
820+
}
821+
815822
/// Tries to add one to this epoch's number. Returns an error if an overflow happens.
816823
#[inline]
817824
pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {

linera-core/src/client/mod.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,12 @@ pub enum ChainClientError {
15091509

15101510
#[error("signer error: {0:?}")]
15111511
Signer(#[source] Box<dyn std::error::Error + Send + Sync>),
1512+
1513+
#[error("Cannot revoke the current epoch {0}")]
1514+
CannotRevokeCurrentEpoch(Epoch),
1515+
1516+
#[error("Epoch is already revoked")]
1517+
EpochAlreadyRevoked,
15121518
}
15131519

15141520
impl From<Infallible> for ChainClientError {
@@ -3381,20 +3387,29 @@ impl<Env: Environment> ChainClient<Env> {
33813387
.await?)
33823388
}
33833389

3384-
/// Deprecates all the configurations of voting rights but the last one (admin chains
3390+
/// Deprecates all the configurations of voting rights up to the given one (admin chains
33853391
/// only). Currently, each individual chain is still entitled to wait before accepting
33863392
/// this command. However, it is expected that deprecated validators stop functioning
33873393
/// shortly after such command is issued.
33883394
#[instrument(level = "trace")]
3389-
pub async fn finalize_committee(
3395+
pub async fn revoke_epochs(
33903396
&self,
3397+
revoked_epoch: Epoch,
33913398
) -> Result<ClientOutcome<ConfirmedBlockCertificate>, ChainClientError> {
33923399
self.prepare_chain().await?;
33933400
let (current_epoch, committees) = self.epoch_and_committees().await?;
3401+
ensure!(
3402+
revoked_epoch < current_epoch,
3403+
ChainClientError::CannotRevokeCurrentEpoch(current_epoch)
3404+
);
3405+
ensure!(
3406+
committees.contains_key(&revoked_epoch),
3407+
ChainClientError::EpochAlreadyRevoked
3408+
);
33943409
let operations = committees
33953410
.keys()
33963411
.filter_map(|epoch| {
3397-
if *epoch != current_epoch {
3412+
if *epoch <= revoked_epoch {
33983413
Some(Operation::system(SystemOperation::Admin(
33993414
AdminOperation::RemoveCommittee { epoch: *epoch },
34003415
)))

linera-core/src/unit_tests/client_tests.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ where
11471147
user.synchronize_from_validators().await.unwrap();
11481148
user.process_inbox().await.unwrap();
11491149
assert_eq!(user.chain_info().await?.epoch, Epoch::from(1));
1150-
admin.finalize_committee().await.unwrap();
1150+
admin.revoke_epochs(Epoch::ZERO).await.unwrap();
11511151

11521152
// Create a new committee.
11531153
let committee = Committee::new(validators.clone(), ResourceControlPolicy::only_fuel());
@@ -1192,8 +1192,18 @@ where
11921192
user.process_inbox().await.unwrap();
11931193
assert_eq!(user.chain_info().await?.epoch, Epoch::from(2));
11941194

1195+
// Revoking the current or an already revoked epoch fails.
1196+
assert_matches!(
1197+
admin.revoke_epochs(Epoch::ZERO).await,
1198+
Err(ChainClientError::EpochAlreadyRevoked)
1199+
);
1200+
assert_matches!(
1201+
admin.revoke_epochs(Epoch::from(3)).await,
1202+
Err(ChainClientError::CannotRevokeCurrentEpoch(Epoch(2)))
1203+
);
1204+
11951205
// Have the admin chain deprecate the previous epoch.
1196-
admin.finalize_committee().await.unwrap();
1206+
admin.revoke_epochs(Epoch::from(1)).await.unwrap();
11971207

11981208
// Try to make a transfer back to the admin chain.
11991209
let cert = user

linera-service/src/cli/command.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{borrow::Cow, num::NonZeroU16, path::PathBuf};
66
use chrono::{DateTime, Utc};
77
use linera_base::{
88
crypto::{AccountPublicKey, CryptoHash, ValidatorPublicKey},
9-
data_types::Amount,
9+
data_types::{Amount, Epoch},
1010
identifiers::{Account, AccountOwner, ApplicationId, ChainId, ModuleId, StreamId},
1111
time::Duration,
1212
vm::VmRuntime,
@@ -309,8 +309,8 @@ pub enum ClientCommand {
309309
public_key: ValidatorPublicKey,
310310
},
311311

312-
/// Deprecates all committees except the last one.
313-
FinalizeCommittee,
312+
/// Deprecates all committees up to and including the specified one.
313+
RevokeEpochs { epoch: Epoch },
314314

315315
/// View or update the resource control policy
316316
ResourceControlPolicy {
@@ -902,7 +902,7 @@ impl ClientCommand {
902902
| ClientCommand::SetValidator { .. }
903903
| ClientCommand::RemoveValidator { .. }
904904
| ClientCommand::ResourceControlPolicy { .. }
905-
| ClientCommand::FinalizeCommittee
905+
| ClientCommand::RevokeEpochs { .. }
906906
| ClientCommand::CreateGenesisConfig { .. }
907907
| ClientCommand::PublishModule { .. }
908908
| ClientCommand::ListEventsFromIndex { .. }

linera-service/src/cli/main.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -691,28 +691,28 @@ impl Runnable for Job {
691691
info!("Operations confirmed after {} ms", time_total.as_millis());
692692
}
693693

694-
FinalizeCommittee => {
694+
RevokeEpochs { epoch } => {
695695
info!("Starting operations to remove old committees");
696696
let time_start = Instant::now();
697697
let mut context =
698698
ClientContext::new(storage, options.inner.clone(), wallet, signer.into_value());
699699

700700
let chain_client = context.make_chain_client(context.wallet.genesis_admin_chain());
701701

702-
// Remove the old committee.
703-
info!("Finalizing current committee");
702+
// Remove the old committees.
703+
info!("Revoking epochs");
704704
context
705705
.apply_client_command(&chain_client, |chain_client| {
706706
let chain_client = chain_client.clone();
707-
async move { chain_client.finalize_committee().await }
707+
async move { chain_client.revoke_epochs(epoch).await }
708708
})
709709
.await
710710
.context("Failed to finalize committee")?;
711711
context.save_wallet().await?;
712712

713713
let time_total = time_start.elapsed();
714714
info!(
715-
"Finalizing committee confirmed after {} ms",
715+
"Revoking committees confirmed after {} ms",
716716
time_total.as_millis()
717717
);
718718
}

linera-service/src/cli_wrappers/wallet.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -964,10 +964,11 @@ impl ClientWrapper {
964964
Ok(())
965965
}
966966

967-
pub async fn finalize_committee(&self) -> Result<()> {
967+
pub async fn revoke_epochs(&self, epoch: Epoch) -> Result<()> {
968968
self.command()
969969
.await?
970-
.arg("finalize-committee")
970+
.arg("revoke-epochs")
971+
.arg(epoch.to_string())
971972
.spawn_and_wait_for_stdout()
972973
.await?;
973974
Ok(())

linera-service/tests/local_net_tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,14 @@ async fn test_end_to_end_reconfiguration(config: LocalNetConfig) -> Result<()> {
178178
client.query_validators(Some(chain_1)).await?;
179179
if let Some(service) = &node_service_2 {
180180
service.process_inbox(&chain_2).await?;
181-
client.finalize_committee().await?;
181+
client.revoke_epochs(Epoch(2)).await?;
182182
service.process_inbox(&chain_2).await?;
183183
let committees = service.query_committees(&chain_2).await?;
184184
let epochs = committees.into_keys().collect::<Vec<_>>();
185185
assert_eq!(&epochs, &[Epoch(3)]);
186186
} else {
187187
client_2.process_inbox(chain_2).await?;
188-
client.finalize_committee().await?;
188+
client.revoke_epochs(Epoch(2)).await?;
189189
client_2.process_inbox(chain_2).await?;
190190
}
191191

@@ -195,14 +195,14 @@ async fn test_end_to_end_reconfiguration(config: LocalNetConfig) -> Result<()> {
195195
client.remove_validator(&validator_key.0).await?;
196196
if let Some(service) = &node_service_2 {
197197
service.process_inbox(&chain_2).await?;
198-
client.finalize_committee().await?;
198+
client.revoke_epochs(Epoch(3 + i as u32)).await?;
199199
service.process_inbox(&chain_2).await?;
200200
let committees = service.query_committees(&chain_2).await?;
201201
let epochs = committees.into_keys().collect::<Vec<_>>();
202202
assert_eq!(&epochs, &[Epoch(4 + i as u32)]);
203203
} else {
204204
client_2.process_inbox(chain_2).await?;
205-
client.finalize_committee().await?;
205+
client.revoke_epochs(Epoch(3 + i as u32)).await?;
206206
client_2.process_inbox(chain_2).await?;
207207
}
208208
net.remove_validator(i)?;
@@ -418,7 +418,7 @@ async fn test_end_to_end_receipt_of_old_remove_committee_messages(
418418

419419
// Ensure the faucet is on the new epoch before removing the old ones.
420420
faucet_client.process_inbox(faucet_chain).await?;
421-
client.finalize_committee().await?;
421+
client.revoke_epochs(Epoch::ZERO).await?;
422422
faucet_client.process_inbox(faucet_chain).await?;
423423

424424
if matches!(network, Network::Grpc) {

0 commit comments

Comments
 (0)