hotstuff_rs/block_sync/
messages.rs

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
/*
    Copyright © 2023, ParallelChain Lab
    Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
*/

//! Definitions for structured messages that are sent between replicas as part of the Block Sync
//! protocol.
//!
//! ## Messages
//!
//! The Block Sync protocol defines two categories of messages:
//!
//! 1. Block Sync Protocol messages ([`BlockSyncMessage`]): exchanged between a sync client and sync
//!    server when the client is trying to sync with the server.
//! 2. Block Sync Server Advertisements ([`BlockSyncAdvertiseMessage`]): periodically broadcasted by
//!    sync servers to update the sync clients on:
//!     1. Their availability and commitment to providing blocks at least up to a given height
//!        ([`AdvertiseBlock`]).
//!     2. Whether the quorum is making progress in a future view, as evidenced by the server's local
//!        Highest PC ([`AdvertisePC`]).

use std::mem;

use borsh::{BorshDeserialize, BorshSerialize};

use crate::{
    hotstuff::types::PhaseCertificate,
    types::{block::*, crypto_primitives::Keypair, data_types::*, signed_messages::SignedMessage},
};

/// Messages exchanged between a sync server and a sync client when syncing.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub enum BlockSyncMessage {
    BlockSyncRequest(BlockSyncRequest),
    BlockSyncResponse(BlockSyncResponse),
}

impl BlockSyncMessage {
    pub fn block_sync_request(
        chain_id: ChainID,
        start_height: BlockHeight,
        limit: u32,
    ) -> BlockSyncMessage {
        BlockSyncMessage::BlockSyncRequest(BlockSyncRequest {
            chain_id,
            start_height,
            limit,
        })
    }

    pub fn block_sync_response(
        blocks: Vec<Block>,
        highest_pc: PhaseCertificate,
    ) -> BlockSyncMessage {
        BlockSyncMessage::BlockSyncResponse(BlockSyncResponse { blocks, highest_pc })
    }
}

/// Sync request sent by a sync client to a sync server. The request includes:
/// 1. Chain ID: for identifying the blockchain the client is interested in,
/// 2. Start height: the height starting from which the client wants to obtain blocks.
/// 3. Limit: Max. number of blocks that the client wants to obtain in a response.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct BlockSyncRequest {
    pub chain_id: ChainID,
    pub start_height: BlockHeight,
    pub limit: u32,
}

/// Sync response sent by a sync server to a sync client requesting blocks. The response includes:
/// 1. Blocks: entire [`Block`]s that the client can validate and insert into their blockchain,
/// 2. HighestPC: highest-viewed [`PhaseCertificate`] known to the server, which the client can
///    validate and use to find out what is the latest consensus decision is.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct BlockSyncResponse {
    pub blocks: Vec<Block>,
    pub highest_pc: PhaseCertificate,
}

/// Messages periodically broadcasted by the sync server to update clients about their availability,
/// commitment to providing blocks up to a given height, and knowledge of the latest consensus decision.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub enum BlockSyncAdvertiseMessage {
    AdvertiseBlock(AdvertiseBlock),
    AdvertisePC(AdvertisePC),
}

impl BlockSyncAdvertiseMessage {
    pub(crate) fn advertise_block(
        me: &Keypair,
        chain_id: ChainID,
        highest_committed_block_height: BlockHeight,
    ) -> Self {
        let message = &(chain_id, highest_committed_block_height)
            .try_to_vec()
            .unwrap();
        let signature = me.sign(message);
        BlockSyncAdvertiseMessage::AdvertiseBlock(AdvertiseBlock {
            chain_id,
            highest_committed_block_height,
            signature,
        })
    }

    pub(crate) fn advertise_pc(highest_pc: PhaseCertificate) -> Self {
        BlockSyncAdvertiseMessage::AdvertisePC(AdvertisePC { highest_pc })
    }

    pub fn chain_id(&self) -> ChainID {
        match self {
            BlockSyncAdvertiseMessage::AdvertiseBlock(msg) => msg.chain_id,
            BlockSyncAdvertiseMessage::AdvertisePC(msg) => msg.highest_pc.chain_id,
        }
    }

    pub fn size(&self) -> u64 {
        match self {
            BlockSyncAdvertiseMessage::AdvertiseBlock(_) => mem::size_of::<AdvertiseBlock>() as u64,
            BlockSyncAdvertiseMessage::AdvertisePC(_) => mem::size_of::<AdvertisePC>() as u64,
        }
    }
}

/// A message periodically broadcasted by the sync server to:
/// 1. Let clients know that the server is available,
/// 2. Commit to providing blocks at least up to the highest committed block height, as included in this
///    message, in case a client decides to sync with the sync server.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct AdvertiseBlock {
    pub chain_id: ChainID,
    pub highest_committed_block_height: BlockHeight,
    pub signature: SignatureBytes,
}

impl SignedMessage for AdvertiseBlock {
    fn message_bytes(&self) -> Vec<u8> {
        (self.chain_id, self.highest_committed_block_height)
            .try_to_vec()
            .unwrap()
    }

    fn signature_bytes(&self) -> SignatureBytes {
        self.signature
    }
}

/// A message periodically broadcasted by the sync server to let clients know about the Highest
/// `PhaseCertificate` the server knows.
///
/// This information may serve as an evidence for the fact that a client is lagging behind, and thus
/// make the client trigger the sync process with some server.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct AdvertisePC {
    pub highest_pc: PhaseCertificate,
}