Adapt carnot network adapter interfaces and implementations to latest spec (#121)
* Add happy-path consensus engine * tmp * Fit types from spec (#124) * Match types to spec * Remove Output import * Consensus engine rework (#126) * rework * fix test * clippy happy --------- Co-authored-by: Giacomo Pasini <Zeegomo@users.noreply.github.com> * Adapt carnot network adapter interfaces and implementations * Fix errors * Update network with engine types * Fit types yet again --------- Co-authored-by: Al Liu <scygliu1@gmail.com> Co-authored-by: Giacomo Pasini <g.pasini98@gmail.com> Co-authored-by: Giacomo Pasini <Zeegomo@users.noreply.github.com>
This commit is contained in:
parent
f8617d7331
commit
26d10856ff
|
@ -6,3 +6,8 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
serde = { version = "1.0", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
serde1 = ["serde"]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Carnot<O: Overlay> {
|
pub struct Carnot<O: Overlay> {
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub type Committee = HashSet<NodeId>;
|
||||||
/// This enum represents the different types of messages that can be sent from the perspective of consensus and
|
/// This enum represents the different types of messages that can be sent from the perspective of consensus and
|
||||||
/// can't be directly used in the network as they lack things like cryptographic signatures.
|
/// can't be directly used in the network as they lack things like cryptographic signatures.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub enum Payload {
|
pub enum Payload {
|
||||||
/// Vote for a block in a view
|
/// Vote for a block in a view
|
||||||
Vote(Vote),
|
Vote(Vote),
|
||||||
|
@ -23,11 +24,13 @@ pub enum Payload {
|
||||||
|
|
||||||
/// Returned
|
/// Returned
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
pub block: BlockId,
|
pub block: BlockId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Timeout {
|
pub struct Timeout {
|
||||||
pub view: View,
|
pub view: View,
|
||||||
pub sender: NodeId,
|
pub sender: NodeId,
|
||||||
|
@ -36,6 +39,7 @@ pub struct Timeout {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct NewView {
|
pub struct NewView {
|
||||||
pub view: View,
|
pub view: View,
|
||||||
pub sender: NodeId,
|
pub sender: NodeId,
|
||||||
|
@ -44,6 +48,7 @@ pub struct NewView {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct TimeoutQc {
|
pub struct TimeoutQc {
|
||||||
pub view: View,
|
pub view: View,
|
||||||
pub high_qc: Qc,
|
pub high_qc: Qc,
|
||||||
|
@ -51,6 +56,7 @@ pub struct TimeoutQc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub id: BlockId,
|
pub id: BlockId,
|
||||||
pub view: View,
|
pub view: View,
|
||||||
|
@ -76,6 +82,7 @@ pub enum Output {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct StandardQc {
|
pub struct StandardQc {
|
||||||
pub view: View,
|
pub view: View,
|
||||||
pub id: BlockId,
|
pub id: BlockId,
|
||||||
|
@ -91,12 +98,14 @@ impl StandardQc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct AggregateQc {
|
pub struct AggregateQc {
|
||||||
pub high_qc: StandardQc,
|
pub high_qc: StandardQc,
|
||||||
pub view: View,
|
pub view: View,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub enum Qc {
|
pub enum Qc {
|
||||||
Standard(StandardQc),
|
Standard(StandardQc),
|
||||||
Aggregated(AggregateQc),
|
Aggregated(AggregateQc),
|
||||||
|
|
|
@ -12,6 +12,7 @@ authors = [
|
||||||
async-trait = { version = "0.1" }
|
async-trait = { version = "0.1" }
|
||||||
blake2 = { version = "0.10" }
|
blake2 = { version = "0.10" }
|
||||||
bytes = "1.3"
|
bytes = "1.3"
|
||||||
|
consensus-engine = { path = "../consensus-engine"}
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
nomos-network = { path = "../nomos-services/network", optional = true }
|
nomos-network = { path = "../nomos-services/network", optional = true }
|
||||||
raptorq = { version = "1.7", optional = true }
|
raptorq = { version = "1.7", optional = true }
|
||||||
|
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
bytes = "1.3"
|
bytes = "1.3"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
consensus-engine = { path = "../../consensus-engine", features = ["serde1"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
nomos-network = { path = "../network" }
|
nomos-network = { path = "../network" }
|
||||||
nomos-mempool = { path = "../mempool" }
|
nomos-mempool = { path = "../mempool" }
|
||||||
|
|
|
@ -227,7 +227,7 @@ pub struct Approval;
|
||||||
pub struct View {
|
pub struct View {
|
||||||
seed: Seed,
|
seed: Seed,
|
||||||
staking_keys: BTreeMap<NodeId, Stake>,
|
staking_keys: BTreeMap<NodeId, Stake>,
|
||||||
pub view_n: u64,
|
pub view_n: consensus_engine::View,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
|
@ -357,8 +357,9 @@ impl View {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use consensus_engine::View instead
|
||||||
pub fn id(&self) -> u64 {
|
pub fn id(&self) -> u64 {
|
||||||
self.view_n
|
self.view_n.try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifies the block is new and the previous leader did not fail
|
// Verifies the block is new and the previous leader did not fail
|
||||||
|
|
|
@ -11,14 +11,15 @@ use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tokio_stream::{wrappers::BroadcastStream, Stream};
|
use tokio_stream::{wrappers::BroadcastStream, Stream};
|
||||||
|
|
||||||
|
use crate::network::messages::TimeoutQcMsg;
|
||||||
use crate::{
|
use crate::{
|
||||||
network::{
|
network::{
|
||||||
messages::{ProposalChunkMsg, VoteMsg},
|
messages::{ProposalChunkMsg, VoteMsg},
|
||||||
NetworkAdapter,
|
NetworkAdapter,
|
||||||
},
|
},
|
||||||
overlay::committees::Committee,
|
overlay::committees::Committee,
|
||||||
View,
|
|
||||||
};
|
};
|
||||||
|
use consensus_engine::{TimeoutQc, View};
|
||||||
|
|
||||||
const MOCK_PUB_SUB_TOPIC: &str = "MockPubSubTopic";
|
const MOCK_PUB_SUB_TOPIC: &str = "MockPubSubTopic";
|
||||||
const MOCK_BLOCK_CONTENT_TOPIC: MockContentTopic = MockContentTopic::new("MockSim", 1, "MockBlock");
|
const MOCK_BLOCK_CONTENT_TOPIC: MockContentTopic = MockContentTopic::new("MockSim", 1, "MockBlock");
|
||||||
|
@ -63,8 +64,7 @@ impl NetworkAdapter for MockAdapter {
|
||||||
|
|
||||||
async fn proposal_chunks_stream(
|
async fn proposal_chunks_stream(
|
||||||
&self,
|
&self,
|
||||||
_committee: Committee,
|
_view: View,
|
||||||
_view: &View,
|
|
||||||
) -> Box<dyn Stream<Item = Bytes> + Send + Sync + Unpin> {
|
) -> Box<dyn Stream<Item = Bytes> + Send + Sync + Unpin> {
|
||||||
let stream_channel = self
|
let stream_channel = self
|
||||||
.message_subscriber_channel()
|
.message_subscriber_channel()
|
||||||
|
@ -80,7 +80,9 @@ impl NetworkAdapter for MockAdapter {
|
||||||
== message.content_topic().content_topic_name
|
== message.content_topic().content_topic_name
|
||||||
{
|
{
|
||||||
let payload = message.payload();
|
let payload = message.payload();
|
||||||
Some(ProposalChunkMsg::from_bytes(payload.as_bytes()).chunk)
|
Some(Bytes::from(
|
||||||
|
ProposalChunkMsg::from_bytes(payload.as_bytes()).chunk,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -92,14 +94,9 @@ impl NetworkAdapter for MockAdapter {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broadcast_block_chunk(
|
async fn broadcast_block_chunk(&self, chunk_message: ProposalChunkMsg) {
|
||||||
&self,
|
|
||||||
_committee: Committee,
|
|
||||||
_view: &View,
|
|
||||||
chunk_message: ProposalChunkMsg,
|
|
||||||
) {
|
|
||||||
let message = MockMessage::new(
|
let message = MockMessage::new(
|
||||||
String::from_utf8_lossy(chunk_message.as_bytes()).to_string(),
|
String::from_utf8_lossy(&chunk_message.as_bytes()).to_string(),
|
||||||
MOCK_BLOCK_CONTENT_TOPIC,
|
MOCK_BLOCK_CONTENT_TOPIC,
|
||||||
1,
|
1,
|
||||||
chrono::Utc::now().timestamp() as usize,
|
chrono::Utc::now().timestamp() as usize,
|
||||||
|
@ -116,10 +113,21 @@ impl NetworkAdapter for MockAdapter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn broadcast_timeout_qc(&self, _timeout_qc_msg: TimeoutQcMsg) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn timeout_qc_stream(
|
||||||
|
&self,
|
||||||
|
_view: View,
|
||||||
|
) -> Box<dyn Stream<Item = TimeoutQc> + Send + Sync + Unpin> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn votes_stream<Vote: DeserializeOwned>(
|
async fn votes_stream<Vote: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
_committee: Committee,
|
_committee: Committee,
|
||||||
_view: &View,
|
_view: View,
|
||||||
) -> Box<dyn Stream<Item = Vote> + Send> {
|
) -> Box<dyn Stream<Item = Vote> + Send> {
|
||||||
let stream_channel = self
|
let stream_channel = self
|
||||||
.message_subscriber_channel()
|
.message_subscriber_channel()
|
||||||
|
@ -146,10 +154,10 @@ impl NetworkAdapter for MockAdapter {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn forward_approval<Vote: Serialize>(
|
async fn send_vote<Vote: Serialize>(
|
||||||
&self,
|
&self,
|
||||||
_committee: Committee,
|
_committee: Committee,
|
||||||
_view: &View,
|
_view: View,
|
||||||
approval_message: VoteMsg<Vote>,
|
approval_message: VoteMsg<Vote>,
|
||||||
) where
|
) where
|
||||||
Vote: Send,
|
Vote: Send,
|
||||||
|
|
|
@ -5,12 +5,13 @@ use bytes::Bytes;
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use tokio_stream::wrappers::BroadcastStream;
|
use tokio_stream::wrappers::BroadcastStream;
|
||||||
// internal
|
// internal
|
||||||
|
use crate::network::messages::TimeoutQcMsg;
|
||||||
use crate::network::{
|
use crate::network::{
|
||||||
messages::{ProposalChunkMsg, VoteMsg},
|
messages::{ProposalChunkMsg, VoteMsg},
|
||||||
NetworkAdapter,
|
NetworkAdapter,
|
||||||
};
|
};
|
||||||
use crate::overlay::committees::Committee;
|
use crate::overlay::committees::Committee;
|
||||||
use crate::View;
|
use consensus_engine::{TimeoutQc, View};
|
||||||
use nomos_network::{
|
use nomos_network::{
|
||||||
backends::waku::{EventKind, NetworkEvent, Waku, WakuBackendMessage},
|
backends::waku::{EventKind, NetworkEvent, Waku, WakuBackendMessage},
|
||||||
NetworkMsg, NetworkService,
|
NetworkMsg, NetworkService,
|
||||||
|
@ -109,6 +110,20 @@ impl WakuAdapter {
|
||||||
});
|
});
|
||||||
tokio_stream::StreamExt::merge(archive_stream, live_stream)
|
tokio_stream::StreamExt::merge(archive_stream, live_stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn broadcast(&self, bytes: Box<[u8]>, topic: WakuContentTopic) {
|
||||||
|
let message = WakuMessage::new(bytes, topic, 1, chrono::Utc::now().timestamp() as usize);
|
||||||
|
if let Err((_, _e)) = self
|
||||||
|
.network_relay
|
||||||
|
.send(NetworkMsg::Process(WakuBackendMessage::Broadcast {
|
||||||
|
message,
|
||||||
|
topic: Some(WAKU_CARNOT_PUB_SUB_TOPIC.clone()),
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
todo!("log error");
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -123,52 +138,65 @@ impl NetworkAdapter for WakuAdapter {
|
||||||
|
|
||||||
async fn proposal_chunks_stream(
|
async fn proposal_chunks_stream(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
view: View,
|
||||||
view: &View,
|
|
||||||
) -> Box<dyn Stream<Item = Bytes> + Send + Sync + Unpin> {
|
) -> Box<dyn Stream<Item = Bytes> + Send + Sync + Unpin> {
|
||||||
let content_topic = proposal_topic(committee, view);
|
|
||||||
Box::new(Box::pin(
|
Box::new(Box::pin(
|
||||||
self.cached_stream_with_content_topic(content_topic)
|
self.cached_stream_with_content_topic(PROPOSAL_CONTENT_TOPIC.clone())
|
||||||
.await
|
.await
|
||||||
.map(|message| {
|
.filter_map(move |message| {
|
||||||
let payload = message.payload();
|
let payload = message.payload();
|
||||||
ProposalChunkMsg::from_bytes(payload).chunk
|
let ProposalChunkMsg {
|
||||||
|
view: msg_view,
|
||||||
|
chunk,
|
||||||
|
} = ProposalChunkMsg::from_bytes(payload);
|
||||||
|
async move {
|
||||||
|
if view == msg_view {
|
||||||
|
Some(Bytes::from(chunk))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broadcast_block_chunk(
|
async fn broadcast_block_chunk(&self, chunk_message: ProposalChunkMsg) {
|
||||||
&self,
|
self.broadcast(chunk_message.as_bytes(), PROPOSAL_CONTENT_TOPIC)
|
||||||
committee: Committee,
|
|
||||||
view: &View,
|
|
||||||
chunk_message: ProposalChunkMsg,
|
|
||||||
) {
|
|
||||||
let content_topic = proposal_topic(committee, view);
|
|
||||||
|
|
||||||
let message = WakuMessage::new(
|
|
||||||
chunk_message.as_bytes(),
|
|
||||||
content_topic,
|
|
||||||
1,
|
|
||||||
chrono::Utc::now().timestamp() as usize,
|
|
||||||
);
|
|
||||||
if let Err((_, _e)) = self
|
|
||||||
.network_relay
|
|
||||||
.send(NetworkMsg::Process(WakuBackendMessage::Broadcast {
|
|
||||||
message,
|
|
||||||
topic: Some(WAKU_CARNOT_PUB_SUB_TOPIC.clone()),
|
|
||||||
}))
|
|
||||||
.await
|
.await
|
||||||
{
|
}
|
||||||
todo!("log error");
|
|
||||||
};
|
async fn broadcast_timeout_qc(&self, timeout_qc_msg: TimeoutQcMsg) {
|
||||||
|
self.broadcast(timeout_qc_msg.as_bytes(), TIMEOUT_QC_CONTENT_TOPIC)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn timeout_qc_stream(
|
||||||
|
&self,
|
||||||
|
view: View,
|
||||||
|
) -> Box<dyn Stream<Item = TimeoutQc> + Send + Sync + Unpin> {
|
||||||
|
Box::new(Box::pin(
|
||||||
|
self.cached_stream_with_content_topic(TIMEOUT_QC_CONTENT_TOPIC.clone())
|
||||||
|
.await
|
||||||
|
.filter_map(move |message| {
|
||||||
|
let payload = message.payload();
|
||||||
|
let qc = TimeoutQcMsg::from_bytes(payload).qc;
|
||||||
|
async move {
|
||||||
|
if qc.view > view {
|
||||||
|
Some(qc)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn votes_stream<Vote: DeserializeOwned>(
|
async fn votes_stream<Vote: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
committee: Committee,
|
||||||
view: &View,
|
view: View,
|
||||||
) -> Box<dyn Stream<Item = Vote> + Send> {
|
) -> Box<dyn Stream<Item = Vote> + Send> {
|
||||||
let content_topic = proposal_topic(committee, view);
|
let content_topic = votes_topic(committee, view);
|
||||||
Box::new(Box::pin(
|
Box::new(Box::pin(
|
||||||
self.cached_stream_with_content_topic(content_topic)
|
self.cached_stream_with_content_topic(content_topic)
|
||||||
.await
|
.await
|
||||||
|
@ -179,13 +207,13 @@ impl NetworkAdapter for WakuAdapter {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn forward_approval<Vote: Serialize + Send>(
|
async fn send_vote<Vote: Serialize + Send>(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
committee: Committee,
|
||||||
view: &View,
|
view: View,
|
||||||
approval_message: VoteMsg<Vote>,
|
approval_message: VoteMsg<Vote>,
|
||||||
) {
|
) {
|
||||||
let content_topic = approval_topic(committee, view);
|
let content_topic = votes_topic(committee, view);
|
||||||
|
|
||||||
let message = WakuMessage::new(
|
let message = WakuMessage::new(
|
||||||
approval_message.as_bytes(),
|
approval_message.as_bytes(),
|
||||||
|
@ -206,20 +234,16 @@ impl NetworkAdapter for WakuAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn approval_topic(committee: Committee, view: &View) -> WakuContentTopic {
|
fn votes_topic(committee: Committee, view: View) -> WakuContentTopic {
|
||||||
WakuContentTopic {
|
WakuContentTopic {
|
||||||
application_name: Cow::Borrowed(APPLICATION_NAME),
|
application_name: Cow::Borrowed(APPLICATION_NAME),
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
content_topic_name: Cow::Owned(format!("approval-{}-{}", committee.id(), view.id())),
|
content_topic_name: Cow::Owned(format!("votes-{}-{}", committee.id(), view)),
|
||||||
encoding: Encoding::Proto,
|
encoding: Encoding::Proto,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proposal_topic(committee: Committee, view: &View) -> WakuContentTopic {
|
const PROPOSAL_CONTENT_TOPIC: WakuContentTopic =
|
||||||
WakuContentTopic {
|
WakuContentTopic::new(APPLICATION_NAME, VERSION, "proposal", Encoding::Proto);
|
||||||
application_name: Cow::Borrowed(APPLICATION_NAME),
|
const TIMEOUT_QC_CONTENT_TOPIC: WakuContentTopic =
|
||||||
version: VERSION,
|
WakuContentTopic::new(APPLICATION_NAME, VERSION, "timeout-qc", Encoding::Proto);
|
||||||
content_topic_name: Cow::Owned(format!("proposal-{}-{}", committee.id(), view.id())),
|
|
||||||
encoding: Encoding::Proto,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
// std
|
// std
|
||||||
// crates
|
// crates
|
||||||
use bytes::Bytes;
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// internal
|
// internal
|
||||||
use crate::NodeId;
|
use crate::NodeId;
|
||||||
|
use consensus_engine::{TimeoutQc, View};
|
||||||
use nomos_core::wire;
|
use nomos_core::wire;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct ProposalChunkMsg {
|
pub struct ProposalChunkMsg {
|
||||||
pub chunk: Bytes,
|
pub chunk: Box<[u8]>,
|
||||||
|
pub view: View,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProposalChunkMsg {
|
impl ProposalChunkMsg {
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> Box<[u8]> {
|
||||||
&self.chunk
|
wire::serialize(self).unwrap().into_boxed_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(data: &[u8]) -> Self {
|
pub fn from_bytes(data: &[u8]) -> Self {
|
||||||
Self {
|
wire::deserialize(data).unwrap()
|
||||||
chunk: Bytes::from(data.to_vec()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,3 +46,19 @@ where
|
||||||
wire::deserialize(data).unwrap()
|
wire::deserialize(data).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct TimeoutQcMsg {
|
||||||
|
pub source: NodeId,
|
||||||
|
pub qc: TimeoutQc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeoutQcMsg {
|
||||||
|
pub fn as_bytes(&self) -> Box<[u8]> {
|
||||||
|
wire::serialize(self).unwrap().into_boxed_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(data: &[u8]) -> Self {
|
||||||
|
wire::deserialize(data).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ use bytes::Bytes;
|
||||||
// crates
|
// crates
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
// internal
|
// internal
|
||||||
use crate::network::messages::{ProposalChunkMsg, VoteMsg};
|
use crate::network::messages::{ProposalChunkMsg, TimeoutQcMsg, VoteMsg};
|
||||||
use crate::overlay::committees::Committee;
|
use crate::overlay::committees::Committee;
|
||||||
use crate::View;
|
use consensus_engine::{TimeoutQc, View};
|
||||||
use nomos_network::backends::NetworkBackend;
|
use nomos_network::backends::NetworkBackend;
|
||||||
use nomos_network::NetworkService;
|
use nomos_network::NetworkService;
|
||||||
use overwatch_rs::services::relay::OutboundRelay;
|
use overwatch_rs::services::relay::OutboundRelay;
|
||||||
|
@ -24,24 +24,23 @@ pub trait NetworkAdapter {
|
||||||
) -> Self;
|
) -> Self;
|
||||||
async fn proposal_chunks_stream(
|
async fn proposal_chunks_stream(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
view: View,
|
||||||
view: &View,
|
|
||||||
) -> Box<dyn Stream<Item = Bytes> + Send + Sync + Unpin>;
|
) -> Box<dyn Stream<Item = Bytes> + Send + Sync + Unpin>;
|
||||||
async fn broadcast_block_chunk(
|
async fn broadcast_block_chunk(&self, chunk_msg: ProposalChunkMsg);
|
||||||
|
async fn broadcast_timeout_qc(&self, timeout_qc_msg: TimeoutQcMsg);
|
||||||
|
async fn timeout_qc_stream(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
view: View,
|
||||||
view: &View,
|
) -> Box<dyn Stream<Item = TimeoutQc> + Send + Sync + Unpin>;
|
||||||
chunk_msg: ProposalChunkMsg,
|
|
||||||
);
|
|
||||||
async fn votes_stream<Vote: DeserializeOwned>(
|
async fn votes_stream<Vote: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
committee: Committee,
|
||||||
view: &View,
|
view: View,
|
||||||
) -> Box<dyn Stream<Item = Vote> + Send>;
|
) -> Box<dyn Stream<Item = Vote> + Send>;
|
||||||
async fn forward_approval<Vote: Serialize + Send>(
|
async fn send_vote<Vote: Serialize + Send>(
|
||||||
&self,
|
&self,
|
||||||
committee: Committee,
|
committee: Committee,
|
||||||
view: &View,
|
view: View,
|
||||||
approval: VoteMsg<Vote>,
|
vote: VoteMsg<Vote>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub struct Member<const C: usize> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
committee: Committee,
|
committee: Committee,
|
||||||
committees: Committees<C>,
|
committees: Committees<C>,
|
||||||
view_n: u64,
|
view_n: consensus_engine::View,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// #Just a newtype index to be able to implement parent/children methods
|
/// #Just a newtype index to be able to implement parent/children methods
|
||||||
|
@ -133,8 +133,7 @@ where
|
||||||
fountain: &Fountain,
|
fountain: &Fountain,
|
||||||
) -> Result<Block<VoteTally::Qc, TxId>, FountainError> {
|
) -> Result<Block<VoteTally::Qc, TxId>, FountainError> {
|
||||||
assert_eq!(view.view_n, self.view_n, "view_n mismatch");
|
assert_eq!(view.view_n, self.view_n, "view_n mismatch");
|
||||||
let committee = self.committee;
|
let message_stream = adapter.proposal_chunks_stream(view.view_n).await;
|
||||||
let message_stream = adapter.proposal_chunks_stream(committee, view).await;
|
|
||||||
fountain.decode(message_stream).await.and_then(|b| {
|
fountain.decode(message_stream).await.and_then(|b| {
|
||||||
deserializer(&b)
|
deserializer(&b)
|
||||||
.deserialize::<Block<VoteTally::Qc, TxId>>()
|
.deserialize::<Block<VoteTally::Qc, TxId>>()
|
||||||
|
@ -150,23 +149,15 @@ where
|
||||||
fountain: &Fountain,
|
fountain: &Fountain,
|
||||||
) {
|
) {
|
||||||
assert_eq!(view.view_n, self.view_n, "view_n mismatch");
|
assert_eq!(view.view_n, self.view_n, "view_n mismatch");
|
||||||
let (left_child, right_child) = self.children_committes();
|
|
||||||
let block_bytes = block.as_bytes();
|
let block_bytes = block.as_bytes();
|
||||||
let encoded_stream = fountain.encode(&block_bytes);
|
let encoded_stream = fountain.encode(&block_bytes);
|
||||||
encoded_stream
|
encoded_stream
|
||||||
.for_each_concurrent(None, |chunk| async move {
|
.for_each_concurrent(None, |chunk| async move {
|
||||||
let message = ProposalChunkMsg { chunk };
|
let message = ProposalChunkMsg {
|
||||||
let r_child = right_child
|
chunk: chunk.to_vec().into_boxed_slice(),
|
||||||
.map(|right_child| {
|
view: view.view_n,
|
||||||
adapter.broadcast_block_chunk(right_child, view, message.clone())
|
};
|
||||||
})
|
adapter.broadcast_block_chunk(message.clone()).await;
|
||||||
.into_iter();
|
|
||||||
let l_child = left_child
|
|
||||||
.map(|left_child| {
|
|
||||||
adapter.broadcast_block_chunk(left_child, view, message.clone())
|
|
||||||
})
|
|
||||||
.into_iter();
|
|
||||||
futures::future::join_all(r_child.chain(l_child)).await;
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,11 @@ const FLAT_COMMITTEE: Committee = Committee::root();
|
||||||
pub struct Flat {
|
pub struct Flat {
|
||||||
// TODO: this should be a const param, but we can't do that yet
|
// TODO: this should be a const param, but we can't do that yet
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
view_n: u64,
|
view_n: consensus_engine::View,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flat {
|
impl Flat {
|
||||||
pub fn new(view_n: u64, node_id: NodeId) -> Self {
|
pub fn new(view_n: consensus_engine::View, node_id: NodeId) -> Self {
|
||||||
Self { node_id, view_n }
|
Self { node_id, view_n }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ where
|
||||||
fountain: &Fountain,
|
fountain: &Fountain,
|
||||||
) -> Result<Block<VoteTally::Qc, TxId>, FountainError> {
|
) -> Result<Block<VoteTally::Qc, TxId>, FountainError> {
|
||||||
assert_eq!(view.view_n, self.view_n, "view_n mismatch");
|
assert_eq!(view.view_n, self.view_n, "view_n mismatch");
|
||||||
let message_stream = adapter.proposal_chunks_stream(FLAT_COMMITTEE, view).await;
|
let message_stream = adapter.proposal_chunks_stream(view.view_n).await;
|
||||||
fountain.decode(message_stream).await.and_then(|b| {
|
fountain.decode(message_stream).await.and_then(|b| {
|
||||||
deserializer(&b)
|
deserializer(&b)
|
||||||
.deserialize::<Block<VoteTally::Qc, TxId>>()
|
.deserialize::<Block<VoteTally::Qc, TxId>>()
|
||||||
|
@ -76,10 +76,11 @@ where
|
||||||
let encoded_stream = fountain.encode(&block_bytes);
|
let encoded_stream = fountain.encode(&block_bytes);
|
||||||
encoded_stream
|
encoded_stream
|
||||||
.for_each_concurrent(None, |chunk| async move {
|
.for_each_concurrent(None, |chunk| async move {
|
||||||
let message = ProposalChunkMsg { chunk };
|
let message = ProposalChunkMsg {
|
||||||
adapter
|
chunk: chunk.to_vec().into_boxed_slice(),
|
||||||
.broadcast_block_chunk(FLAT_COMMITTEE, view, message)
|
view: view.view_n,
|
||||||
.await;
|
};
|
||||||
|
adapter.broadcast_block_chunk(message).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -96,9 +97,9 @@ where
|
||||||
// in the flat overlay, there's no need to wait for anyone before approving the block
|
// in the flat overlay, there's no need to wait for anyone before approving the block
|
||||||
let approval = self.approve(block);
|
let approval = self.approve(block);
|
||||||
adapter
|
adapter
|
||||||
.forward_approval(
|
.send_vote(
|
||||||
FLAT_COMMITTEE,
|
FLAT_COMMITTEE,
|
||||||
view,
|
view.view_n,
|
||||||
VoteMsg {
|
VoteMsg {
|
||||||
vote: approval,
|
vote: approval,
|
||||||
source: self.node_id,
|
source: self.node_id,
|
||||||
|
@ -113,11 +114,12 @@ where
|
||||||
|
|
||||||
// for now, let's pretend that consensus is reached as soon as the
|
// for now, let's pretend that consensus is reached as soon as the
|
||||||
// block is approved by a share of the nodes
|
// block is approved by a share of the nodes
|
||||||
let stream = Box::into_pin(adapter.votes_stream(FLAT_COMMITTEE, view).await);
|
let stream = Box::into_pin(adapter.votes_stream(FLAT_COMMITTEE, view.view_n).await);
|
||||||
|
|
||||||
// Shadow the original binding so that it can't be directly accessed
|
// Shadow the original binding so that it can't be directly accessed
|
||||||
// ever again.
|
// ever again.
|
||||||
if let Ok((qc, _)) = tally.tally(view.view_n, stream).await {
|
// TODO: Remove the `try_into` call when tally is refactored to use with latest consensus engine types
|
||||||
|
if let Ok((qc, _)) = tally.tally(view.view_n.try_into().unwrap(), stream).await {
|
||||||
qc
|
qc
|
||||||
} else {
|
} else {
|
||||||
unimplemented!("consensus not reached")
|
unimplemented!("consensus not reached")
|
||||||
|
|
Loading…
Reference in New Issue