Skip to content

Commit 692878f

Browse files
afckdeuszx
andauthored
Fix consensus issues related to timeouts, rounds, ownership. (Backport #4688) (#4701)
## Motivation #4688 fixes several consensus issues, some of which we already observed in Testnet Conway. ## Proposal Backport the fixes. ## Test Plan CI A regression test was added. ## Release Plan - These changes should be released in a new SDK, and - be released in a validator hotfix. ## Links - PR for main: #4688 - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist) --------- Co-authored-by: deuszx <[email protected]>
1 parent a29247e commit 692878f

File tree

19 files changed

+640
-203
lines changed

19 files changed

+640
-203
lines changed

Cargo.lock

Lines changed: 9 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ native-fungible = { path = "./examples/native-fungible" }
328328
non-fungible = { path = "./examples/non-fungible" }
329329
publish-read-data-blob = { path = "./examples/publish-read-data-blob" }
330330
social = { path = "./examples/social" }
331+
time-expiry = { path = "./examples/time-expiry" }
331332

332333
[workspace.dependencies.aws-config]
333334
default-features = false

examples/Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ members = [
2222
"publish-read-data-blob",
2323
"rfq",
2424
"social",
25+
"time-expiry",
2526
]
2627

2728
[workspace.dependencies]

examples/time-expiry/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "time-expiry"
3+
version = "0.1.0"
4+
authors = ["Linera <[email protected]>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
linera-sdk.workspace = true
9+
serde.workspace = true
10+
11+
[[bin]]
12+
name = "time_expiry_contract"
13+
path = "src/contract.rs"
14+
15+
[[bin]]
16+
name = "time_expiry_service"
17+
path = "src/service.rs"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg_attr(target_arch = "wasm32", no_main)]
5+
6+
mod state;
7+
8+
use linera_sdk::{
9+
linera_base_types::WithContractAbi,
10+
views::{RootView, View},
11+
Contract, ContractRuntime,
12+
};
13+
use time_expiry::{TimeExpiryAbi, TimeExpiryOperation};
14+
15+
use self::state::TimeExpiryState;
16+
17+
pub struct TimeExpiryContract {
18+
state: TimeExpiryState,
19+
runtime: ContractRuntime<Self>,
20+
}
21+
22+
linera_sdk::contract!(TimeExpiryContract);
23+
24+
impl WithContractAbi for TimeExpiryContract {
25+
type Abi = TimeExpiryAbi;
26+
}
27+
28+
impl Contract for TimeExpiryContract {
29+
type Message = ();
30+
type InstantiationArgument = ();
31+
type Parameters = ();
32+
type EventValue = ();
33+
34+
async fn load(runtime: ContractRuntime<Self>) -> Self {
35+
let state = TimeExpiryState::load(runtime.root_view_storage_context())
36+
.await
37+
.expect("Failed to load state");
38+
TimeExpiryContract { state, runtime }
39+
}
40+
41+
async fn instantiate(&mut self, _argument: ()) {
42+
self.runtime.application_parameters();
43+
}
44+
45+
async fn execute_operation(&mut self, operation: TimeExpiryOperation) {
46+
let TimeExpiryOperation::ExpireAfter(delta) = operation;
47+
48+
// Get the current block timestamp.
49+
let block_timestamp = self.runtime.system_time();
50+
51+
// Calculate the expiry timestamp.
52+
let expiry_timestamp = block_timestamp.saturating_add(delta);
53+
54+
// Assert that the validation happens before the expiry timestamp.
55+
self.runtime.assert_before(expiry_timestamp);
56+
}
57+
58+
async fn execute_message(&mut self, _message: ()) {
59+
panic!("TimeExpiry application doesn't support any cross-chain messages");
60+
}
61+
62+
async fn store(mut self) {
63+
self.state.save().await.expect("Failed to save state");
64+
}
65+
}

examples/time-expiry/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/*! ABI of the Time Expiry Test Application */
5+
6+
use linera_sdk::linera_base_types::{ContractAbi, ServiceAbi, TimeDelta};
7+
use serde::{Deserialize, Serialize};
8+
9+
pub struct TimeExpiryAbi;
10+
11+
#[derive(Debug, Deserialize, Serialize)]
12+
pub enum TimeExpiryOperation {
13+
/// Expire the operation after the given time delta from block timestamp.
14+
ExpireAfter(TimeDelta),
15+
}
16+
17+
impl ContractAbi for TimeExpiryAbi {
18+
type Operation = TimeExpiryOperation;
19+
type Response = ();
20+
}
21+
22+
impl ServiceAbi for TimeExpiryAbi {
23+
type Query = ();
24+
type QueryResponse = ();
25+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg_attr(target_arch = "wasm32", no_main)]
5+
6+
mod state;
7+
8+
use linera_sdk::{linera_base_types::WithServiceAbi, views::View, Service, ServiceRuntime};
9+
use time_expiry::TimeExpiryAbi;
10+
11+
use self::state::TimeExpiryState;
12+
13+
pub struct TimeExpiryService {
14+
#[allow(dead_code)]
15+
state: TimeExpiryState,
16+
}
17+
18+
linera_sdk::service!(TimeExpiryService);
19+
20+
impl WithServiceAbi for TimeExpiryService {
21+
type Abi = TimeExpiryAbi;
22+
}
23+
24+
impl Service for TimeExpiryService {
25+
type Parameters = ();
26+
27+
async fn new(runtime: ServiceRuntime<Self>) -> Self {
28+
let state = TimeExpiryState::load(runtime.root_view_storage_context())
29+
.await
30+
.expect("Failed to load state");
31+
TimeExpiryService { state }
32+
}
33+
34+
async fn handle_query(&self, _query: ()) {}
35+
}

examples/time-expiry/src/state.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use linera_sdk::views::{linera_views, RegisterView, RootView, ViewStorageContext};
5+
6+
/// The application state (empty for this test fixture, but needs at least one field).
7+
#[derive(RootView)]
8+
#[view(context = ViewStorageContext)]
9+
pub struct TimeExpiryState {
10+
/// A dummy field since RootView requires at least one field.
11+
pub dummy: RegisterView<u64>,
12+
}

linera-base/src/ownership.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ impl ChainOwnership {
178178
};
179179
Some(next_round)
180180
}
181+
182+
/// Returns whether the given owner is the only owner, and a super owner.
183+
pub fn is_single_super_owner(&self, owner: &AccountOwner) -> bool {
184+
self.owners.is_empty() && self.super_owners.len() == 1 && self.super_owners.contains(owner)
185+
}
181186
}
182187

183188
/// Errors that can happen when attempting to close a chain.

0 commit comments

Comments
 (0)