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
use std::time::Duration;

use serde::{Deserialize, Serialize};

/// State machine of party involved in round-based protocol
pub trait StateMachine {
    /// Body of transmitting messages
    ///
    /// Actual type of transmitting messages will be `Msg<SM::MessageBody>` (see [Msg struct](Msg))
    type MessageBody;
    /// Error type used by StateMachine
    ///
    /// Errors are divided on critical and not critical to follow different error-handling strategies
    /// on appearing one of them. For more details, see method returning `Result`s, e.g.
    /// [handle_incoming](Self::handle_incoming) or [proceed](Self::proceed)
    type Err: IsCritical;
    /// Output of the protocol if it successfully terminates
    type Output;

    /// Process received message
    ///
    /// ## Returns
    /// Handling message might result in error, but it doesn't mean that computation should
    /// be aborted. Returned error needs to be examined whether it critical or not (by calling
    /// [is_critical](IsCritical::is_critical) method).
    ///
    /// If occurs:
    /// * Critical error: protocol must be aborted
    /// * Non-critical error: it should be reported, but protocol must continue
    ///
    /// Example of non-critical error is receiving message which we didn't expect to see. It could
    /// be either network lag or bug in implementation or attempt to sabotage the protocol, but
    /// protocol might be resistant to this, so it still has a chance to successfully complete.
    ///
    /// ## Blocking
    /// This method should not block or perform expensive computation. E.g. it might do
    /// deserialization (if needed) or cheap checks.
    fn handle_incoming(&mut self, msg: Msg<Self::MessageBody>) -> Result<(), Self::Err>;

    /// Queue of messages to be sent
    ///
    /// New messages can be appended to queue only as result of calling
    /// [proceed](StateMachine::proceed) or [handle_incoming](StateMachine::handle_incoming) methods.
    ///
    /// Messages can be sent in any order.
    fn message_queue(&mut self) -> &mut Vec<Msg<Self::MessageBody>>;

    /// Indicates whether StateMachine wants to perform some expensive computation
    fn wants_to_proceed(&self) -> bool;

    /// Performs some expensive computation
    ///
    /// If [`StateMachine`] is executed at green thread (in async environment), it will be typically
    /// moved to dedicated thread at thread pool before calling `.proceed()` method.
    ///
    /// ## Returns
    /// Returns `Ok(())` if either computation successfully completes or computation was not
    /// required (i.e. `self.wants_to_proceed() == false`).
    ///
    /// If it returns `Err(err)`, then `err` is examined whether it's critical or not (by
    /// calling [is_critical](IsCritical::is_critical) method).
    ///
    /// If occurs:
    /// * Critical error: protocol must be aborted
    /// * Non-critical error: it should be reported, but protocol must continue
    ///
    /// For example, in `.proceed()` at verification stage we could find some party trying to
    /// sabotage the protocol, but protocol might be designed to be resistant to such attack, so
    /// it's not a critical error, but obviously it should be reported.
    fn proceed(&mut self) -> Result<(), Self::Err>;

    /// Deadline for a particular round
    ///
    /// After reaching deadline (if set) [round_timeout_reached](Self::round_timeout_reached)
    /// will be called.
    ///
    /// After proceeding on the next round (increasing [current_round](Self::current_round)),
    /// timer will be reset, new timeout will be requested (by calling this method), and new
    /// deadline will be set.
    fn round_timeout(&self) -> Option<Duration>;

    /// Method is triggered after reaching [round_timeout](Self::round_timeout)
    ///
    /// Reaching timeout always aborts computation, no matter what error is returned: critical or not.
    fn round_timeout_reached(&mut self) -> Self::Err;

    /// Indicates whether protocol is finished and output can be obtained by calling
    /// [pick_output](Self::pick_output) method.
    fn is_finished(&self) -> bool;

    /// Obtains protocol output
    ///
    /// ## Returns
    /// * `None`, if protocol is not finished yet
    ///   i.e. `protocol.is_finished() == false`
    /// * `Some(Err(_))`, if protocol terminated with error
    /// * `Some(Ok(_))`, if protocol successfully terminated
    ///
    /// After `Some(_)` has been obtained via this method, StateMachine must be utilized (dropped).
    fn pick_output(&mut self) -> Option<Result<Self::Output, Self::Err>>;

    /// Sequential number of current round
    ///
    /// Can be increased by 1 as result of calling either [proceed](StateMachine::proceed) or
    /// [handle_incoming](StateMachine::handle_incoming) methods. Changing round number in any other way
    /// (or in any other method) might cause strange behaviour.
    fn current_round(&self) -> u16;

    /// Total amount of rounds (if known)
    fn total_rounds(&self) -> Option<u16>;

    /// Index of this party
    ///
    /// Must be in interval `[1; n]` where `n = self.parties()`
    fn party_ind(&self) -> u16;
    /// Number of parties involved in computation
    fn parties(&self) -> u16;
}

/// Represent a message transmitting between parties on wire
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Msg<B> {
    /// Index of the sender
    ///
    /// Lies in range `[1; n]` where `n` is number of parties involved in computation
    pub sender: u16,
    /// Index of receiver
    ///
    /// `None` indicates that it's broadcast message. Receiver index, if set, lies in range `[1; n]`
    /// where `n` is number of parties involved in computation
    pub receiver: Option<u16>,
    /// Message body
    pub body: B,
}

impl<B> Msg<B> {
    /// Applies closure to message body
    pub fn map_body<T, F>(self, f: F) -> Msg<T>
    where
        F: FnOnce(B) -> T,
    {
        Msg {
            sender: self.sender,
            receiver: self.receiver,
            body: f(self.body),
        }
    }
}

/// Distinguish a critical error from not critical
///
/// For semantic, see [StateMachine trait](StateMachine) (in particular,
/// [handle_incoming](StateMachine::handle_incoming) and [proceed](StateMachine::proceed)
/// methods)
pub trait IsCritical {
    /// Indicates whether an error critical or not
    fn is_critical(&self) -> bool;
}