1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//! Overlord Consensus Protocol is a Byzantine fault tolerance (BFT) consensus algorithm aiming to
//! support thousands of transactions per second under hundreds of consensus nodes, with transaction
//! delays of no more than a few seconds. Simply put, it is a high-performance consensus algorithm
//! able to meets most of the real business needs.

#![deny(missing_docs)]
#![feature(test)]

/// A module that impl rlp encodable and decodable trait for types that need to save wal.
mod codec;
/// Overlord error module.
pub mod error;
/// Create and run the overlord consensus process.
pub mod overlord;
/// State machine replicas module to do state changes.
mod smr;
/// The state module to storage proposals and votes.
mod state;
/// The timer module to ensure the protocol liveness.
mod timer;
/// Message types using in the overlord consensus protocol.
pub mod types;
/// Some utility functions.
mod utils;
/// Write ahead log module.
mod wal;

pub use self::overlord::Overlord;
pub use self::overlord::OverlordHandler;
pub use creep::Context;

use std::error::Error;
use std::fmt::Debug;

use async_trait::async_trait;
use bytes::Bytes;
use serde::{Deserialize, Serialize};

use crate::error::ConsensusError;
use crate::types::{Address, Commit, Hash, Node, OverlordMsg, Signature, Status};

/// Overlord consensus result.
pub type ConsensusResult<T> = ::std::result::Result<T, ConsensusError>;

const INIT_HEIGHT: u64 = 0;
const INIT_ROUND: u64 = 0;

/// Trait for some functions that consensus needs.
#[async_trait]
pub trait Consensus<T: Codec>: Send + Sync {
    /// Get a block of the given height and return the block with its hash.
    async fn get_block(
        &self,
        ctx: Context,
        height: u64,
    ) -> Result<(T, Hash), Box<dyn Error + Send>>;

    /// Check the correctness of a block. If is passed, return the integrated transcations to do
    /// data persistence.
    async fn check_block(
        &self,
        ctx: Context,
        height: u64,
        hash: Hash,
        block: T,
    ) -> Result<(), Box<dyn Error + Send>>;

    /// Commit a given height to execute and return the rich status.
    async fn commit(
        &self,
        ctx: Context,
        height: u64,
        commit: Commit<T>,
    ) -> Result<Status, Box<dyn Error + Send>>;

    /// Get an authority list of the given height.
    async fn get_authority_list(
        &self,
        ctx: Context,
        height: u64,
    ) -> Result<Vec<Node>, Box<dyn Error + Send>>;

    /// Broadcast a message to other replicas.
    async fn broadcast_to_other(
        &self,
        ctx: Context,
        msg: OverlordMsg<T>,
    ) -> Result<(), Box<dyn Error + Send>>;

    /// Transmit a message to the Relayer, the third argument is the relayer's address.
    async fn transmit_to_relayer(
        &self,
        ctx: Context,
        addr: Address,
        msg: OverlordMsg<T>,
    ) -> Result<(), Box<dyn Error + Send>>;
}

/// Trait for doing serialize and deserialize.
pub trait Codec: Clone + Debug + Send + PartialEq + Eq {
    /// Serialize self into bytes.
    fn encode(&self) -> Result<Bytes, Box<dyn Error + Send>>;

    /// Deserialize date into self.
    fn decode(data: Bytes) -> Result<Self, Box<dyn Error + Send>>;
}

/// Trait for save and load wal information.
#[async_trait]
pub trait Wal {
    /// Save wal information.
    async fn save(&self, info: Bytes) -> Result<(), Box<dyn Error + Send>>;

    /// Load wal information.
    async fn load(&self) -> Result<Option<Bytes>, Box<dyn Error + Send>>;
}

/// Trait for some crypto methods.
pub trait Crypto: Send {
    /// Hash a message bytes.
    fn hash(&self, msg: Bytes) -> Hash;

    /// Sign to the given hash by private key and return the signature if success.
    fn sign(&self, hash: Hash) -> Result<Signature, Box<dyn Error + Send>>;

    /// Aggregate the given signatures into an aggregated signature according to the given bitmap.
    fn aggregate_signatures(
        &self,
        signatures: Vec<Signature>,
        voters: Vec<Address>,
    ) -> Result<Signature, Box<dyn Error + Send>>;

    /// Verify a signature and return the recovered address.
    fn verify_signature(
        &self,
        signature: Signature,
        hash: Hash,
        voter: Address,
    ) -> Result<(), Box<dyn Error + Send>>;

    /// Verify an aggregated signature.
    fn verify_aggregated_signature(
        &self,
        aggregate_signature: Signature,
        msg_hash: Hash,
        voters: Vec<Address>,
    ) -> Result<(), Box<dyn Error + Send>>;
}

/// The setting of the timeout interval of each step.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct DurationConfig {
    /// The proportion of propose timeout to the height interval.
    pub propose_ratio: u64,
    /// The proportion of prevote timeout to the height interval.
    pub prevote_ratio: u64,
    /// The proportion of precommit timeout to the height interval.
    pub precommit_ratio: u64,
    /// The proportion of retry choke message timeout to the height interval.
    pub brake_ratio: u64,
}

impl Default for DurationConfig {
    fn default() -> Self {
        DurationConfig {
            propose_ratio:   0u64,
            prevote_ratio:   0u64,
            precommit_ratio: 0u64,
            brake_ratio:     0u64,
        }
    }
}

impl DurationConfig {
    /// Create a consensus timeout configuration.
    pub fn new(
        propose_ratio: u64,
        prevote_ratio: u64,
        precommit_ratio: u64,
        brake_ratio: u64,
    ) -> Self {
        DurationConfig {
            propose_ratio,
            prevote_ratio,
            precommit_ratio,
            brake_ratio,
        }
    }

    pub(crate) fn get_propose_config(&self) -> (u64, u64) {
        (self.propose_ratio, 10u64)
    }

    pub(crate) fn get_prevote_config(&self) -> (u64, u64) {
        (self.prevote_ratio, 10u64)
    }

    pub(crate) fn get_precommit_config(&self) -> (u64, u64) {
        (self.precommit_ratio, 10u64)
    }

    pub(crate) fn get_brake_config(&self) -> (u64, u64) {
        (self.brake_ratio, 10u64)
    }
}

#[cfg(test)]
mod test {
    use super::DurationConfig;

    #[test]
    fn test_duration_config() {
        let config = DurationConfig::new(1, 2, 3, 4);
        assert_eq!(config.get_propose_config(), (1, 10));
        assert_eq!(config.get_prevote_config(), (2, 10));
        assert_eq!(config.get_precommit_config(), (3, 10));
        assert_eq!(config.get_brake_config(), (4, 10));
    }
}