Make a transaction trait (#98)

* Impl Transaction trait

* Impl Transaction for MockTransaction

* <ake carnot transaction a module

* Refactor consensus to use Transaction

* Fix tests

* Constrain Transaction::Hash

* Refactor redundant Carnot in CarnotTx
This commit is contained in:
Daniel Sanchez 2023-03-16 22:42:56 -07:00 committed by GitHub
parent 780276497f
commit 91ce4e6fa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 228 additions and 192 deletions

View File

@ -6,7 +6,7 @@ use tokio::sync::mpsc::Sender;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use tracing::error; use tracing::error;
// internal // internal
use crate::tx::{Tx, TxId}; use crate::tx::Tx;
use futures::future::join_all; use futures::future::join_all;
use multiaddr::Multiaddr; use multiaddr::Multiaddr;
use nomos_core::wire; use nomos_core::wire;
@ -27,15 +27,14 @@ pub fn mempool_metrics_bridge(
handle: overwatch_rs::overwatch::handle::OverwatchHandle, handle: overwatch_rs::overwatch::handle::OverwatchHandle,
) -> HttpBridgeRunner { ) -> HttpBridgeRunner {
Box::new(Box::pin(async move { Box::new(Box::pin(async move {
let (mempool_channel, mut http_request_channel) = build_http_bridge::< let (mempool_channel, mut http_request_channel) =
MempoolService<WakuAdapter<Tx>, MockPool<TxId, Tx>>, build_http_bridge::<MempoolService<WakuAdapter<Tx>, MockPool<Tx>>, AxumBackend, _>(
AxumBackend, handle,
_, HttpMethod::GET,
>( "metrics",
handle, HttpMethod::GET, "metrics" )
) .await
.await .unwrap();
.unwrap();
while let Some(HttpRequest { res_tx, .. }) = http_request_channel.recv().await { while let Some(HttpRequest { res_tx, .. }) = http_request_channel.recv().await {
if let Err(e) = handle_metrics_req(&mempool_channel, res_tx).await { if let Err(e) = handle_metrics_req(&mempool_channel, res_tx).await {
@ -50,17 +49,14 @@ pub fn mempool_add_tx_bridge(
handle: overwatch_rs::overwatch::handle::OverwatchHandle, handle: overwatch_rs::overwatch::handle::OverwatchHandle,
) -> HttpBridgeRunner { ) -> HttpBridgeRunner {
Box::new(Box::pin(async move { Box::new(Box::pin(async move {
let (mempool_channel, mut http_request_channel) = build_http_bridge::< let (mempool_channel, mut http_request_channel) =
MempoolService<WakuAdapter<Tx>, MockPool<TxId, Tx>>, build_http_bridge::<MempoolService<WakuAdapter<Tx>, MockPool<Tx>>, AxumBackend, _>(
AxumBackend, handle.clone(),
_, HttpMethod::POST,
>( "addtx",
handle.clone(), )
HttpMethod::POST, .await
"addtx", .unwrap();
)
.await
.unwrap();
while let Some(HttpRequest { while let Some(HttpRequest {
res_tx, payload, .. res_tx, payload, ..
@ -121,7 +117,7 @@ pub fn waku_add_conn_bridge(
} }
async fn handle_metrics_req( async fn handle_metrics_req(
mempool_channel: &OutboundRelay<MempoolMsg<Tx, TxId>>, mempool_channel: &OutboundRelay<MempoolMsg<Tx>>,
res_tx: Sender<HttpResponse>, res_tx: Sender<HttpResponse>,
) -> Result<(), overwatch_rs::DynError> { ) -> Result<(), overwatch_rs::DynError> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
@ -147,7 +143,7 @@ async fn handle_metrics_req(
async fn handle_add_tx_req( async fn handle_add_tx_req(
handle: &overwatch_rs::overwatch::handle::OverwatchHandle, handle: &overwatch_rs::overwatch::handle::OverwatchHandle,
mempool_channel: &OutboundRelay<MempoolMsg<Tx, TxId>>, mempool_channel: &OutboundRelay<MempoolMsg<Tx>>,
res_tx: Sender<HttpResponse>, res_tx: Sender<HttpResponse>,
payload: Option<Bytes>, payload: Option<Bytes>,
) -> Result<(), overwatch_rs::DynError> { ) -> Result<(), overwatch_rs::DynError> {

View File

@ -18,7 +18,7 @@ use overwatch_rs::{
}; };
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use tx::{Tx, TxId}; use tx::Tx;
/// Simple program to greet a person /// Simple program to greet a person
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -39,7 +39,7 @@ struct Config {
struct MockPoolNode { struct MockPoolNode {
logging: ServiceHandle<Logger>, logging: ServiceHandle<Logger>,
network: ServiceHandle<NetworkService<Waku>>, network: ServiceHandle<NetworkService<Waku>>,
mockpool: ServiceHandle<MempoolService<WakuAdapter<Tx>, MockPool<TxId, Tx>>>, mockpool: ServiceHandle<MempoolService<WakuAdapter<Tx>, MockPool<Tx>>>,
http: ServiceHandle<HttpService<AxumBackend>>, http: ServiceHandle<HttpService<AxumBackend>>,
bridges: ServiceHandle<HttpBridgeService>, bridges: ServiceHandle<HttpBridgeService>,
} }

View File

@ -1,42 +1,20 @@
use blake2::digest::{Update, VariableOutput}; use bytes::Bytes;
use blake2::Blake2bVar; use nomos_core::tx::{Transaction, TransactionHasher};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::hash::Hash; use std::hash::Hash;
#[derive(Clone, Debug, Serialize, Deserialize, Hash)] #[derive(Clone, Debug, Serialize, Deserialize, Hash)]
pub struct Tx(pub String); pub struct Tx(pub String);
#[derive(Debug, Eq, Hash, PartialEq, Ord, Clone, PartialOrd)] fn hash_tx(tx: &Tx) -> String {
pub struct TxId([u8; 32]); tx.0.clone()
impl From<&Tx> for TxId {
fn from(tx: &Tx) -> Self {
let mut hasher = Blake2bVar::new(32).unwrap();
hasher.update(
bincode::serde::encode_to_vec(tx, bincode::config::standard())
.unwrap()
.as_slice(),
);
let mut id = [0u8; 32];
hasher.finalize_variable(&mut id).unwrap();
Self(id)
}
} }
#[cfg(test)] impl Transaction for Tx {
mod test { const HASHER: TransactionHasher<Self> = hash_tx;
use super::*; type Hash = String;
#[test] fn as_bytes(&self) -> Bytes {
fn test_txid() { self.0.as_bytes().to_vec().into()
let tx = Tx("test".to_string());
let txid = TxId::from(&tx);
assert_eq!(
txid.0,
[
39, 227, 252, 176, 211, 134, 68, 39, 134, 158, 47, 7, 82, 40, 169, 232, 168, 118,
240, 103, 84, 146, 127, 64, 60, 196, 126, 142, 172, 156, 124, 78
]
);
} }
} }

View File

@ -10,6 +10,7 @@ authors = [
[dependencies] [dependencies]
async-trait = { version = "0.1" } async-trait = { version = "0.1" }
blake2 = { version = "0.10" }
bytes = "1.3" bytes = "1.3"
futures = "0.3" futures = "0.3"
nomos-network = { path = "../nomos-services/network", optional = true } nomos-network = { path = "../nomos-services/network", optional = true }
@ -19,7 +20,6 @@ thiserror = "1.0"
bincode = "1.3" bincode = "1.3"
once_cell = "1.0" once_cell = "1.0"
indexmap = { version = "1.9", features = ["serde-1"] } indexmap = { version = "1.9", features = ["serde-1"] }
blake2 = { version = "0.10", optional = true }
serde_json = { version = "1", optional = true } serde_json = { version = "1", optional = true }
[dev-dependencies] [dev-dependencies]
@ -30,4 +30,4 @@ tokio = { version = "1.23", features = ["macros", "rt"] }
[features] [features]
default = [] default = []
raptor = ["raptorq"] raptor = ["raptorq"]
mock = ["nomos-network/mock", "blake2", "serde_json"] mock = ["nomos-network/mock", "serde_json"]

View File

@ -10,7 +10,7 @@ pub type TxHash = [u8; 32];
/// A block /// A block
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Block<TxId: Eq + Hash> { pub struct Block<TxId: Clone + Eq + Hash> {
header: BlockHeader, header: BlockHeader,
transactions: IndexSet<TxId>, transactions: IndexSet<TxId>,
} }
@ -24,7 +24,7 @@ pub struct BlockHeader {
/// Identifier of a block /// Identifier of a block
pub type BlockId = [u8; 32]; pub type BlockId = [u8; 32];
impl<TxId: Eq + Hash> Block<TxId> { impl<TxId: Clone + Eq + Hash> Block<TxId> {
pub fn new(header: BlockHeader, txs: impl Iterator<Item = TxId>) -> Self { pub fn new(header: BlockHeader, txs: impl Iterator<Item = TxId>) -> Self {
Self { Self {
header, header,

View File

@ -1,10 +0,0 @@
// std
// crates
use serde::{Deserialize, Serialize};
// internal
pub use crate::tx::transaction::Transaction;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Tx {
Transfer(Transaction),
}

View File

@ -0,0 +1,35 @@
// std
// crates
use bytes::Bytes;
use serde::{Deserialize, Serialize};
// internal
pub use crate::tx::carnot::transaction::TransferTransaction;
use crate::tx::{Transaction, TransactionHasher};
mod transaction;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Tx {
Transfer(TransferTransaction),
}
// TODO: We should probably abstract the de/serialization of the transaction as it s done in transaction.rs
fn hash_carnot_tx(tx: &Tx) -> [u8; 32] {
use blake2::{
digest::{consts::U32, Digest},
Blake2b,
};
let mut hasher = Blake2b::<U32>::new();
hasher.update(<Tx as Transaction>::as_bytes(tx));
let res = hasher.finalize();
res.into()
}
impl Transaction for Tx {
const HASHER: TransactionHasher<Self> = hash_carnot_tx;
type Hash = [u8; 32];
fn as_bytes(&self) -> Bytes {
[].to_vec().into()
}
}

View File

@ -7,7 +7,7 @@ use crate::crypto::Signature;
/// but does not imply that it can be successfully applied /// but does not imply that it can be successfully applied
/// to the ledger. /// to the ledger.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Transaction { pub struct TransferTransaction {
pub from: AccountId, pub from: AccountId,
pub to: AccountId, pub to: AccountId,
pub value: u64, pub value: u64,
@ -26,26 +26,26 @@ mod serde {
// This would also allow to control ser/de independently from the Rust // This would also allow to control ser/de independently from the Rust
// representation. // representation.
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct WireTransaction { struct WireTransferTransaction {
from: AccountId, from: AccountId,
to: AccountId, to: AccountId,
value: u64, value: u64,
signature: Signature, signature: Signature,
} }
impl<'de> Deserialize<'de> for Transaction { impl<'de> Deserialize<'de> for TransferTransaction {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let WireTransaction { let WireTransferTransaction {
from, from,
to, to,
value, value,
signature, signature,
} = WireTransaction::deserialize(deserializer)?; } = WireTransferTransaction::deserialize(deserializer)?;
//TODO: check signature //TODO: check signature
Ok(Transaction { Ok(TransferTransaction {
from, from,
to, to,
value, value,
@ -54,12 +54,12 @@ mod serde {
} }
} }
impl Serialize for Transaction { impl Serialize for TransferTransaction {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
WireTransaction { WireTransferTransaction {
from: self.from.clone(), from: self.from.clone(),
to: self.to.clone(), to: self.to.clone(),
value: self.value, value: self.value,

View File

@ -1,8 +1,11 @@
use crate::tx::{Transaction, TransactionHasher};
use crate::wire;
use crate::wire::serialize; use crate::wire::serialize;
use blake2::{ use blake2::{
digest::{Update, VariableOutput}, digest::{Update, VariableOutput},
Blake2bVar, Blake2bVar,
}; };
use bytes::{Bytes, BytesMut};
use nomos_network::backends::mock::MockMessage; use nomos_network::backends::mock::MockMessage;
#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
@ -20,6 +23,27 @@ impl MockTransaction {
pub fn message(&self) -> &MockMessage { pub fn message(&self) -> &MockMessage {
&self.content &self.content
} }
pub fn id(&self) -> MockTxId {
self.id
}
fn as_bytes(&self) -> Bytes {
let mut buff = BytesMut::new();
wire::serializer_into_buffer(&mut buff)
.serialize_into(&self)
.expect("MockTransaction serialization to buffer failed");
buff.freeze()
}
}
impl Transaction for MockTransaction {
const HASHER: TransactionHasher<Self> = MockTransaction::id;
type Hash = MockTxId;
fn as_bytes(&self) -> Bytes {
MockTransaction::as_bytes(self)
}
} }
impl From<nomos_network::backends::mock::MockMessage> for MockTransaction { impl From<nomos_network::backends::mock::MockMessage> for MockTransaction {

View File

@ -1,4 +1,20 @@
use std::hash::Hash;
// std
// crates
use bytes::Bytes;
// internal
pub mod carnot; pub mod carnot;
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
pub mod mock; pub mod mock;
mod transaction;
pub type TransactionHasher<T> = fn(&T) -> <T as Transaction>::Hash;
pub trait Transaction {
const HASHER: TransactionHasher<Self>;
type Hash: Hash + Eq + Clone;
fn hash(&self) -> Self::Hash {
Self::HASHER(self)
}
fn as_bytes(&self) -> Bytes;
}

View File

@ -2,6 +2,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
// crates // crates
// internal // internal
use nomos_core::tx::Transaction;
use nomos_core::{block::BlockHeader, crypto::PrivateKey}; use nomos_core::{block::BlockHeader, crypto::PrivateKey};
use nomos_mempool::MempoolMsg; use nomos_mempool::MempoolMsg;
@ -12,12 +13,12 @@ struct Enclave {
key: PrivateKey, key: PrivateKey,
} }
pub struct Leadership<Tx, Id> { pub struct Leadership<Tx: Transaction> {
key: Enclave, key: Enclave,
mempool: OutboundRelay<MempoolMsg<Tx, Id>>, mempool: OutboundRelay<MempoolMsg<Tx>>,
} }
pub enum LeadershipResult<'view, TxId: Eq + core::hash::Hash> { pub enum LeadershipResult<'view, TxId: Clone + Eq + core::hash::Hash> {
Leader { Leader {
block: Block<TxId>, block: Block<TxId>,
_view: PhantomData<&'view u8>, _view: PhantomData<&'view u8>,
@ -27,12 +28,12 @@ pub enum LeadershipResult<'view, TxId: Eq + core::hash::Hash> {
}, },
} }
impl<Tx, Id> Leadership<Tx, Id> impl<Tx> Leadership<Tx>
where where
Id: Eq + core::hash::Hash, Tx: Transaction,
for<'t> &'t Tx: Into<Id>, // TODO: we should probably abstract this away but for now the constrain may do Tx::Hash: Debug,
{ {
pub fn new(key: PrivateKey, mempool: OutboundRelay<MempoolMsg<Tx, Id>>) -> Self { pub fn new(key: PrivateKey, mempool: OutboundRelay<MempoolMsg<Tx>>) -> Self {
Self { Self {
key: Enclave { key }, key: Enclave { key },
mempool, mempool,
@ -45,7 +46,7 @@ where
view: &'view View, view: &'view View,
tip: &Tip, tip: &Tip,
qc: Qc, qc: Qc,
) -> LeadershipResult<'view, Id> { ) -> LeadershipResult<'view, Tx::Hash> {
// TODO: get the correct ancestor for the tip // TODO: get the correct ancestor for the tip
// let ancestor_hint = todo!("get the ancestor from the tip"); // let ancestor_hint = todo!("get the ancestor from the tip");
let ancestor_hint = [0; 32]; let ancestor_hint = [0; 32];
@ -59,7 +60,10 @@ where
LeadershipResult::Leader { LeadershipResult::Leader {
_view: PhantomData, _view: PhantomData,
block: Block::new(BlockHeader::default(), iter.map(|ref tx| tx.into())), block: Block::new(
BlockHeader::default(),
iter.map(|ref tx| <Tx as Transaction>::hash(tx)),
),
} }
} else { } else {
LeadershipResult::NotLeader { _view: PhantomData } LeadershipResult::NotLeader { _view: PhantomData }

View File

@ -18,10 +18,11 @@ use serde::{Deserialize, Serialize};
// internal // internal
use crate::network::NetworkAdapter; use crate::network::NetworkAdapter;
use leadership::{Leadership, LeadershipResult}; use leadership::{Leadership, LeadershipResult};
use nomos_core::block::{Block, TxHash}; use nomos_core::block::Block;
use nomos_core::crypto::PublicKey; use nomos_core::crypto::PublicKey;
use nomos_core::fountain::FountainCode; use nomos_core::fountain::FountainCode;
use nomos_core::staking::Stake; use nomos_core::staking::Stake;
use nomos_core::tx::Transaction;
use nomos_core::vote::Tally; use nomos_core::vote::Tally;
use nomos_mempool::{backend::MemPool, network::NetworkAdapter as MempoolAdapter, MempoolService}; use nomos_mempool::{backend::MemPool, network::NetworkAdapter as MempoolAdapter, MempoolService};
use nomos_network::NetworkService; use nomos_network::NetworkService;
@ -79,9 +80,9 @@ where
M: MempoolAdapter<Tx = P::Tx>, M: MempoolAdapter<Tx = P::Tx>,
P: MemPool, P: MemPool,
T: Tally, T: Tally,
O: Overlay<A, F, T>, O: Overlay<A, F, T, <P::Tx as Transaction>::Hash>,
P::Tx: Debug + 'static, P::Tx: Transaction + Debug + 'static,
P::Id: Debug + 'static, <P::Tx as Transaction>::Hash: Debug,
A::Backend: 'static, A::Backend: 'static,
{ {
service_state: ServiceStateHandle<Self>, service_state: ServiceStateHandle<Self>,
@ -100,10 +101,10 @@ where
A: NetworkAdapter, A: NetworkAdapter,
P: MemPool, P: MemPool,
T: Tally, T: Tally,
P::Tx: Debug, P::Tx: Transaction + Debug,
P::Id: Debug, <P::Tx as Transaction>::Hash: Debug,
M: MempoolAdapter<Tx = P::Tx>, M: MempoolAdapter<Tx = P::Tx>,
O: Overlay<A, F, T>, O: Overlay<A, F, T, <P::Tx as Transaction>::Hash>,
{ {
const SERVICE_ID: ServiceId = "Carnot"; const SERVICE_ID: ServiceId = "Carnot";
type Settings = CarnotSettings<F, T>; type Settings = CarnotSettings<F, T>;
@ -123,18 +124,9 @@ where
T::Outcome: Send + Sync, T::Outcome: Send + Sync,
P::Settings: Send + Sync + 'static, P::Settings: Send + Sync + 'static,
P::Tx: Debug + Clone + serde::de::DeserializeOwned + Send + Sync + 'static, P::Tx: Debug + Clone + serde::de::DeserializeOwned + Send + Sync + 'static,
for<'t> &'t P::Tx: Into<TxHash>, <P::Tx as Transaction>::Hash: Debug + Send + Sync,
P::Id: Debug
+ Clone
+ serde::de::DeserializeOwned
+ for<'a> From<&'a P::Tx>
+ Eq
+ Hash
+ Send
+ Sync
+ 'static,
M: MempoolAdapter<Tx = P::Tx> + Send + Sync + 'static, M: MempoolAdapter<Tx = P::Tx> + Send + Sync + 'static,
O: Overlay<A, F, T, TxId = P::Id> + Send + Sync + 'static, O: Overlay<A, F, T, <P::Tx as Transaction>::Hash> + Send + Sync + 'static,
{ {
fn init(service_state: ServiceStateHandle<Self>) -> Result<Self, overwatch_rs::DynError> { fn init(service_state: ServiceStateHandle<Self>) -> Result<Self, overwatch_rs::DynError> {
let network_relay = service_state.overwatch_handle.relay(); let network_relay = service_state.overwatch_handle.relay();
@ -175,7 +167,7 @@ where
let fountain = F::new(fountain_settings); let fountain = F::new(fountain_settings);
let tally = T::new(tally_settings); let tally = T::new(tally_settings);
let leadership = Leadership::<P::Tx, P::Id>::new(private_key, mempool_relay.clone()); let leadership = Leadership::<P::Tx>::new(private_key, mempool_relay.clone());
// FIXME: this should be taken from config // FIXME: this should be taken from config
let mut cur_view = View { let mut cur_view = View {
seed: [0; 32], seed: [0; 32],
@ -244,15 +236,16 @@ impl View {
adapter: &A, adapter: &A,
fountain: &F, fountain: &F,
tally: &T, tally: &T,
leadership: &Leadership<Tx, O::TxId>, leadership: &Leadership<Tx>,
) -> Result<(Block<O::TxId>, View), Box<dyn std::error::Error + Send + Sync + 'static>> ) -> Result<(Block<Tx::Hash>, View), Box<dyn std::error::Error + Send + Sync + 'static>>
where where
A: NetworkAdapter + Send + Sync + 'static, A: NetworkAdapter + Send + Sync + 'static,
F: FountainCode, F: FountainCode,
for<'t> &'t Tx: Into<O::TxId>, Tx: Transaction,
Tx::Hash: Debug,
T: Tally + Send + Sync + 'static, T: Tally + Send + Sync + 'static,
T::Outcome: Send + Sync, T::Outcome: Send + Sync,
O: Overlay<A, F, T>, O: Overlay<A, F, T, Tx::Hash>,
{ {
let res = if self.is_leader(node_id) { let res = if self.is_leader(node_id) {
let block = self let block = self
@ -262,7 +255,7 @@ impl View {
let next_view = self.generate_next_view(&block); let next_view = self.generate_next_view(&block);
(block, next_view) (block, next_view)
} else { } else {
self.resolve_non_leader::<A, O, F, T>(node_id, adapter, fountain, tally) self.resolve_non_leader::<A, O, F, T, Tx>(node_id, adapter, fountain, tally)
.await .await
.unwrap() // FIXME: handle sad path .unwrap() // FIXME: handle sad path
}; };
@ -283,15 +276,16 @@ impl View {
adapter: &A, adapter: &A,
fountain: &F, fountain: &F,
tally: &T, tally: &T,
leadership: &Leadership<Tx, O::TxId>, leadership: &Leadership<Tx>,
) -> Result<Block<O::TxId>, ()> ) -> Result<Block<<Tx as Transaction>::Hash>, ()>
where where
A: NetworkAdapter + Send + Sync + 'static, A: NetworkAdapter + Send + Sync + 'static,
F: FountainCode, F: FountainCode,
T: Tally + Send + Sync + 'static, T: Tally + Send + Sync + 'static,
T::Outcome: Send + Sync, T::Outcome: Send + Sync,
for<'t> &'t Tx: Into<O::TxId>, Tx: Transaction,
O: Overlay<A, F, T>, Tx::Hash: Debug,
O: Overlay<A, F, T, Tx::Hash>,
{ {
let overlay = O::new(self, node_id); let overlay = O::new(self, node_id);
@ -309,18 +303,20 @@ impl View {
Ok(block) Ok(block)
} }
async fn resolve_non_leader<'view, A, O, F, T>( async fn resolve_non_leader<'view, A, O, F, T, Tx>(
&'view self, &'view self,
node_id: NodeId, node_id: NodeId,
adapter: &A, adapter: &A,
fountain: &F, fountain: &F,
tally: &T, tally: &T,
) -> Result<(Block<O::TxId>, View), ()> ) -> Result<(Block<Tx::Hash>, View), ()>
where where
A: NetworkAdapter + Send + Sync + 'static, A: NetworkAdapter + Send + Sync + 'static,
F: FountainCode, F: FountainCode,
T: Tally + Send + Sync + 'static, T: Tally + Send + Sync + 'static,
O: Overlay<A, F, T>, Tx: Transaction,
Tx::Hash: Debug,
O: Overlay<A, F, T, Tx::Hash>,
{ {
let overlay = O::new(self, node_id); let overlay = O::new(self, node_id);
// Consensus in Carnot is achieved in 2 steps from the point of view of a node: // Consensus in Carnot is achieved in 2 steps from the point of view of a node:
@ -360,12 +356,12 @@ impl View {
} }
// Verifies the block is new and the previous leader did not fail // Verifies the block is new and the previous leader did not fail
fn pipelined_safe_block<TxId: Eq + Hash>(&self, _: &Block<TxId>) -> bool { fn pipelined_safe_block<TxId: Clone + Eq + Hash>(&self, _: &Block<TxId>) -> bool {
// return b.view_n >= self.view_n && b.view_n == b.qc.view_n // return b.view_n >= self.view_n && b.view_n == b.qc.view_n
true true
} }
fn generate_next_view<TxId: Eq + Hash>(&self, _b: &Block<TxId>) -> View { fn generate_next_view<TxId: Clone + Eq + Hash>(&self, _b: &Block<TxId>) -> View {
let mut seed = self.seed; let mut seed = self.seed;
seed[0] += 1; seed[0] += 1;
View { View {

View File

@ -10,44 +10,38 @@ use crate::network::messages::ProposalChunkMsg;
use crate::network::NetworkAdapter; use crate::network::NetworkAdapter;
/// View of the tree overlay centered around a specific member /// View of the tree overlay centered around a specific member
pub struct Member<TxId: Eq + Hash, const C: usize> { pub struct Member<const C: usize> {
// id is not used now, but gonna probably used it for later checking later on // id is not used now, but gonna probably used it for later checking later on
#[allow(dead_code)] #[allow(dead_code)]
id: NodeId, id: NodeId,
committee: Committee, committee: Committee,
committees: Committees<TxId, C>, committees: Committees<C>,
view_n: u64, view_n: u64,
_marker: std::marker::PhantomData<TxId>,
} }
/// #Just a newtype index to be able to implement parent/children methods /// #Just a newtype index to be able to implement parent/children methods
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Committee(usize); pub struct Committee(usize);
pub struct Committees<TxId: Eq + Hash, const C: usize> { pub struct Committees<const C: usize> {
nodes: Box<[NodeId]>, nodes: Box<[NodeId]>,
_marker: std::marker::PhantomData<TxId>,
} }
impl<TxId: Eq + Hash, const C: usize> Committees<TxId, C> { impl<const C: usize> Committees<C> {
pub fn new(view: &View) -> Self { pub fn new(view: &View) -> Self {
let mut nodes = view.staking_keys.keys().cloned().collect::<Box<[NodeId]>>(); let mut nodes = view.staking_keys.keys().cloned().collect::<Box<[NodeId]>>();
let mut rng = rand_chacha::ChaCha20Rng::from_seed(view.seed); let mut rng = rand_chacha::ChaCha20Rng::from_seed(view.seed);
nodes.shuffle(&mut rng); nodes.shuffle(&mut rng);
Self { Self { nodes }
nodes,
_marker: std::marker::PhantomData,
}
} }
pub fn into_member(self, id: NodeId, view: &View) -> Option<Member<TxId, C>> { pub fn into_member(self, id: NodeId, view: &View) -> Option<Member<C>> {
let member_idx = self.nodes.iter().position(|m| m == &id)?; let member_idx = self.nodes.iter().position(|m| m == &id)?;
Some(Member { Some(Member {
committee: Committee(member_idx / C), committee: Committee(member_idx / C),
committees: self, committees: self,
id, id,
view_n: view.view_n, view_n: view.view_n,
_marker: std::marker::PhantomData,
}) })
} }
@ -92,7 +86,7 @@ impl Committee {
} }
} }
impl<TxId: Eq + Hash, const C: usize> Member<TxId, C> { impl<const C: usize> Member<C> {
/// Return other members of this committee /// Return other members of this committee
pub fn peers(&self) -> &[NodeId] { pub fn peers(&self) -> &[NodeId] {
self.committees self.committees
@ -117,15 +111,14 @@ impl<TxId: Eq + Hash, const C: usize> Member<TxId, C> {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl<Network, Fountain, VoteTally, TxId, const C: usize> Overlay<Network, Fountain, VoteTally> impl<Network, Fountain, VoteTally, TxId, const C: usize> Overlay<Network, Fountain, VoteTally, TxId>
for Member<TxId, C> for Member<C>
where where
Network: NetworkAdapter + Sync, Network: NetworkAdapter + Sync,
Fountain: FountainCode + Sync, Fountain: FountainCode + Sync,
VoteTally: Tally + Sync, VoteTally: Tally + Sync,
TxId: serde::de::DeserializeOwned + Eq + Hash + Clone + Send + Sync + 'static, TxId: serde::de::DeserializeOwned + Clone + Hash + Eq + Send + Sync + 'static,
{ {
type TxId = TxId;
// we still need view here to help us initialize // we still need view here to help us initialize
fn new(view: &View, node: NodeId) -> Self { fn new(view: &View, node: NodeId) -> Self {
let committees = Committees::new(view); let committees = Committees::new(view);
@ -137,13 +130,13 @@ where
view: &View, view: &View,
adapter: &Network, adapter: &Network,
fountain: &Fountain, fountain: &Fountain,
) -> Result<Block<Self::TxId>, FountainError> { ) -> Result<Block<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 committee = self.committee;
let message_stream = adapter.proposal_chunks_stream(committee, view).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<Self::TxId>>() .deserialize::<Block<TxId>>()
.map_err(|e| FountainError::from(e.to_string().as_str())) .map_err(|e| FountainError::from(e.to_string().as_str()))
}) })
} }
@ -151,7 +144,7 @@ where
async fn broadcast_block( async fn broadcast_block(
&self, &self,
view: &View, view: &View,
block: Block<Self::TxId>, block: Block<TxId>,
adapter: &Network, adapter: &Network,
fountain: &Fountain, fountain: &Fountain,
) { ) {
@ -180,7 +173,7 @@ where
async fn approve_and_forward( async fn approve_and_forward(
&self, &self,
view: &View, view: &View,
_block: &Block<Self::TxId>, _block: &Block<TxId>,
_adapter: &Network, _adapter: &Network,
_tally: &VoteTally, _tally: &VoteTally,
_next_view: &View, _next_view: &View,

View File

@ -25,7 +25,7 @@ pub struct Flat<TxId> {
_marker: std::marker::PhantomData<TxId>, _marker: std::marker::PhantomData<TxId>,
} }
impl<TxId: Eq + Hash> Flat<TxId> { impl<TxId: Clone + Eq + Hash> Flat<TxId> {
pub fn new(view_n: u64, node_id: NodeId) -> Self { pub fn new(view_n: u64, node_id: NodeId) -> Self {
Self { Self {
node_id, node_id,
@ -41,7 +41,7 @@ impl<TxId: Eq + Hash> Flat<TxId> {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl<Network, Fountain, VoteTally, TxId> Overlay<Network, Fountain, VoteTally> for Flat<TxId> impl<Network, Fountain, VoteTally, TxId> Overlay<Network, Fountain, VoteTally, TxId> for Flat<TxId>
where where
TxId: serde::de::DeserializeOwned + Clone + Eq + Hash + Send + Sync + 'static, TxId: serde::de::DeserializeOwned + Clone + Eq + Hash + Send + Sync + 'static,
Network: NetworkAdapter + Sync, Network: NetworkAdapter + Sync,
@ -49,8 +49,6 @@ where
VoteTally: Tally + Sync, VoteTally: Tally + Sync,
VoteTally::Vote: Serialize + DeserializeOwned + Send, VoteTally::Vote: Serialize + DeserializeOwned + Send,
{ {
type TxId = TxId;
fn new(view: &View, node: NodeId) -> Self { fn new(view: &View, node: NodeId) -> Self {
Flat::new(view.view_n, node) Flat::new(view.view_n, node)
} }
@ -60,12 +58,12 @@ where
view: &View, view: &View,
adapter: &Network, adapter: &Network,
fountain: &Fountain, fountain: &Fountain,
) -> Result<Block<Self::TxId>, FountainError> { ) -> Result<Block<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(FLAT_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<Self::TxId>>() .deserialize::<Block<TxId>>()
.map_err(|e| FountainError::from(e.to_string().as_str())) .map_err(|e| FountainError::from(e.to_string().as_str()))
}) })
} }
@ -73,7 +71,7 @@ where
async fn broadcast_block( async fn broadcast_block(
&self, &self,
view: &View, view: &View,
block: Block<Self::TxId>, block: Block<TxId>,
adapter: &Network, adapter: &Network,
fountain: &Fountain, fountain: &Fountain,
) { ) {
@ -93,7 +91,7 @@ where
async fn approve_and_forward( async fn approve_and_forward(
&self, &self,
view: &View, view: &View,
block: &Block<Self::TxId>, block: &Block<TxId>,
adapter: &Network, adapter: &Network,
_tally: &VoteTally, _tally: &VoteTally,
_next_view: &View, _next_view: &View,

View File

@ -3,6 +3,7 @@ mod flat;
// std // std
use std::error::Error; use std::error::Error;
use std::hash::Hash;
// crates // crates
// internal // internal
use super::{Approval, NodeId, View}; use super::{Approval, NodeId, View};
@ -14,9 +15,13 @@ use nomos_core::vote::Tally;
/// Dissemination overlay, tied to a specific view /// Dissemination overlay, tied to a specific view
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait Overlay<Network: NetworkAdapter, Fountain: FountainCode, VoteTally: Tally> { pub trait Overlay<
type TxId: serde::de::DeserializeOwned + Clone + Eq + core::hash::Hash + Send + Sync + 'static; Network: NetworkAdapter,
Fountain: FountainCode,
VoteTally: Tally,
TxId: Clone + Eq + Hash,
>
{
fn new(view: &View, node: NodeId) -> Self; fn new(view: &View, node: NodeId) -> Self;
async fn reconstruct_proposal_block( async fn reconstruct_proposal_block(
@ -24,11 +29,11 @@ pub trait Overlay<Network: NetworkAdapter, Fountain: FountainCode, VoteTally: Ta
view: &View, view: &View,
adapter: &Network, adapter: &Network,
fountain: &Fountain, fountain: &Fountain,
) -> Result<Block<Self::TxId>, FountainError>; ) -> Result<Block<TxId>, FountainError>;
async fn broadcast_block( async fn broadcast_block(
&self, &self,
view: &View, view: &View,
block: Block<Self::TxId>, block: Block<TxId>,
adapter: &Network, adapter: &Network,
fountain: &Fountain, fountain: &Fountain,
); );
@ -38,7 +43,7 @@ pub trait Overlay<Network: NetworkAdapter, Fountain: FountainCode, VoteTally: Ta
async fn approve_and_forward( async fn approve_and_forward(
&self, &self,
view: &View, view: &View,
block: &Block<Self::TxId>, block: &Block<TxId>,
adapter: &Network, adapter: &Network,
vote_tally: &VoteTally, vote_tally: &VoteTally,
next_view: &View, next_view: &View,

View File

@ -7,19 +7,20 @@ use std::{collections::BTreeMap, time::UNIX_EPOCH};
// internal // internal
use crate::backend::{MemPool, MempoolError}; use crate::backend::{MemPool, MempoolError};
use nomos_core::block::{BlockHeader, BlockId}; use nomos_core::block::{BlockHeader, BlockId};
use nomos_core::tx::Transaction;
/// A mock mempool implementation that stores all transactions in memory in the order received. /// A mock mempool implementation that stores all transactions in memory in the order received.
pub struct MockPool<Id, Tx> { pub struct MockPool<Tx: Transaction>
pending_txs: LinkedHashMap<Id, Tx>, where
Tx::Hash: Hash,
{
pending_txs: LinkedHashMap<Tx::Hash, Tx>,
in_block_txs: BTreeMap<BlockId, Vec<Tx>>, in_block_txs: BTreeMap<BlockId, Vec<Tx>>,
in_block_txs_by_id: BTreeMap<Id, BlockId>, in_block_txs_by_id: BTreeMap<Tx::Hash, BlockId>,
last_tx_timestamp: u64, last_tx_timestamp: u64,
} }
impl<Id, Tx> Default for MockPool<Id, Tx> impl<Tx: Transaction> Default for MockPool<Tx> {
where
Id: Eq + Hash,
{
fn default() -> Self { fn default() -> Self {
Self { Self {
pending_txs: LinkedHashMap::new(), pending_txs: LinkedHashMap::new(),
@ -30,30 +31,29 @@ where
} }
} }
impl<Id, Tx> MockPool<Id, Tx> impl<Tx: Transaction> MockPool<Tx>
where where
Id: Eq + Hash, Tx::Hash: Ord,
{ {
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
} }
impl<Id, Tx> MemPool for MockPool<Id, Tx> impl<Tx> MemPool for MockPool<Tx>
where where
Id: for<'t> From<&'t Tx> + PartialOrd + Ord + Eq + Hash + Clone, Tx: Transaction + Clone + Send + Sync + 'static + Hash,
Tx: Clone + Send + Sync + 'static + Hash, Tx::Hash: Ord,
{ {
type Settings = (); type Settings = ();
type Tx = Tx; type Tx = Tx;
type Id = Id;
fn new(_settings: Self::Settings) -> Self { fn new(_settings: Self::Settings) -> Self {
Self::new() Self::new()
} }
fn add_tx(&mut self, tx: Self::Tx) -> Result<(), MempoolError> { fn add_tx(&mut self, tx: Self::Tx) -> Result<(), MempoolError> {
let id = Id::from(&tx); let id = <Self::Tx as Transaction>::hash(&tx);
if self.pending_txs.contains_key(&id) || self.in_block_txs_by_id.contains_key(&id) { if self.pending_txs.contains_key(&id) || self.in_block_txs_by_id.contains_key(&id) {
return Err(MempoolError::ExistingTx); return Err(MempoolError::ExistingTx);
} }
@ -73,7 +73,7 @@ where
Box::new(pending_txs.into_iter()) Box::new(pending_txs.into_iter())
} }
fn mark_in_block(&mut self, txs: &[Self::Id], block: BlockHeader) { fn mark_in_block(&mut self, txs: &[<Self::Tx as Transaction>::Hash], block: BlockHeader) {
let mut txs_in_block = Vec::with_capacity(txs.len()); let mut txs_in_block = Vec::with_capacity(txs.len());
for tx_id in txs.iter() { for tx_id in txs.iter() {
if let Some(tx) = self.pending_txs.remove(tx_id) { if let Some(tx) = self.pending_txs.remove(tx_id) {
@ -96,7 +96,7 @@ where
}) })
} }
fn prune(&mut self, txs: &[Self::Id]) { fn prune(&mut self, txs: &[<Self::Tx as Transaction>::Hash]) {
for tx_id in txs { for tx_id in txs {
self.pending_txs.remove(tx_id); self.pending_txs.remove(tx_id);
} }

View File

@ -2,6 +2,7 @@
pub mod mockpool; pub mod mockpool;
use nomos_core::block::{BlockHeader, BlockId}; use nomos_core::block::{BlockHeader, BlockId};
use nomos_core::tx::Transaction;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum MempoolError { pub enum MempoolError {
@ -13,8 +14,7 @@ pub enum MempoolError {
pub trait MemPool { pub trait MemPool {
type Settings: Clone; type Settings: Clone;
type Tx; type Tx: Transaction;
type Id;
/// Construct a new empty pool /// Construct a new empty pool
fn new(settings: Self::Settings) -> Self; fn new(settings: Self::Settings) -> Self;
@ -30,7 +30,7 @@ pub trait MemPool {
fn view(&self, ancestor_hint: BlockId) -> Box<dyn Iterator<Item = Self::Tx> + Send>; fn view(&self, ancestor_hint: BlockId) -> Box<dyn Iterator<Item = Self::Tx> + Send>;
/// Record that a set of transactions were included in a block /// Record that a set of transactions were included in a block
fn mark_in_block(&mut self, txs: &[Self::Id], block: BlockHeader); fn mark_in_block(&mut self, txs: &[<Self::Tx as Transaction>::Hash], block: BlockHeader);
/// Returns all of the transactions for the block /// Returns all of the transactions for the block
#[cfg(test)] #[cfg(test)]
@ -41,7 +41,7 @@ pub trait MemPool {
/// Signal that a set of transactions can't be possibly requested anymore and can be /// Signal that a set of transactions can't be possibly requested anymore and can be
/// discarded. /// discarded.
fn prune(&mut self, txs: &[Self::Id]); fn prune(&mut self, txs: &[<Self::Tx as Transaction>::Hash]);
fn pending_tx_count(&self) -> usize; fn pending_tx_count(&self) -> usize;
fn last_tx_timestamp(&self) -> u64; fn last_tx_timestamp(&self) -> u64;

View File

@ -11,6 +11,7 @@ use tokio::sync::oneshot::Sender;
use crate::network::NetworkAdapter; use crate::network::NetworkAdapter;
use backend::MemPool; use backend::MemPool;
use nomos_core::block::{BlockHeader, BlockId}; use nomos_core::block::{BlockHeader, BlockId};
use nomos_core::tx::Transaction;
use nomos_network::NetworkService; use nomos_network::NetworkService;
use overwatch_rs::services::{ use overwatch_rs::services::{
handle::ServiceStateHandle, handle::ServiceStateHandle,
@ -25,7 +26,7 @@ where
P: MemPool, P: MemPool,
P::Settings: Clone, P::Settings: Clone,
P::Tx: Debug + 'static, P::Tx: Debug + 'static,
P::Id: Debug + 'static, <P::Tx as Transaction>::Hash: Debug,
{ {
service_state: ServiceStateHandle<Self>, service_state: ServiceStateHandle<Self>,
network_relay: Relay<NetworkService<N::Backend>>, network_relay: Relay<NetworkService<N::Backend>>,
@ -37,7 +38,7 @@ pub struct MempoolMetrics {
pub last_tx_timestamp: u64, pub last_tx_timestamp: u64,
} }
pub enum MempoolMsg<Tx, Id> { pub enum MempoolMsg<Tx: Transaction> {
AddTx { AddTx {
tx: Tx, tx: Tx,
reply_channel: Sender<Result<(), ()>>, reply_channel: Sender<Result<(), ()>>,
@ -47,7 +48,7 @@ pub enum MempoolMsg<Tx, Id> {
reply_channel: Sender<Box<dyn Iterator<Item = Tx> + Send>>, reply_channel: Sender<Box<dyn Iterator<Item = Tx> + Send>>,
}, },
Prune { Prune {
ids: Vec<Id>, ids: Vec<Tx::Hash>,
}, },
#[cfg(test)] #[cfg(test)]
BlockTransaction { BlockTransaction {
@ -55,7 +56,7 @@ pub enum MempoolMsg<Tx, Id> {
reply_channel: Sender<Option<Box<dyn Iterator<Item = Tx> + Send>>>, reply_channel: Sender<Option<Box<dyn Iterator<Item = Tx> + Send>>>,
}, },
MarkInBlock { MarkInBlock {
ids: Vec<Id>, ids: Vec<Tx::Hash>,
block: BlockHeader, block: BlockHeader,
}, },
Metrics { Metrics {
@ -63,7 +64,10 @@ pub enum MempoolMsg<Tx, Id> {
}, },
} }
impl<Tx: Debug, Id: Debug> Debug for MempoolMsg<Tx, Id> { impl<Tx: Transaction + Debug> Debug for MempoolMsg<Tx>
where
Tx::Hash: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self { match self {
Self::View { ancestor_hint, .. } => { Self::View { ancestor_hint, .. } => {
@ -86,7 +90,7 @@ impl<Tx: Debug, Id: Debug> Debug for MempoolMsg<Tx, Id> {
} }
} }
impl<Tx: 'static, Id: 'static> RelayMessage for MempoolMsg<Tx, Id> {} impl<Tx: Transaction + 'static> RelayMessage for MempoolMsg<Tx> {}
impl<N, P> ServiceData for MempoolService<N, P> impl<N, P> ServiceData for MempoolService<N, P>
where where
@ -94,13 +98,13 @@ where
P: MemPool, P: MemPool,
P::Settings: Clone, P::Settings: Clone,
P::Tx: Debug + 'static, P::Tx: Debug + 'static,
P::Id: Debug + 'static, <P::Tx as Transaction>::Hash: Debug,
{ {
const SERVICE_ID: ServiceId = "Mempool"; const SERVICE_ID: ServiceId = "Mempool";
type Settings = P::Settings; type Settings = P::Settings;
type State = NoState<Self::Settings>; type State = NoState<Self::Settings>;
type StateOperator = NoOperator<Self::State>; type StateOperator = NoOperator<Self::State>;
type Message = MempoolMsg<<P as MemPool>::Tx, <P as MemPool>::Id>; type Message = MempoolMsg<<P as MemPool>::Tx>;
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -108,8 +112,8 @@ impl<N, P> ServiceCore for MempoolService<N, P>
where where
P: MemPool + Send + 'static, P: MemPool + Send + 'static,
P::Settings: Clone + Send + Sync + 'static, P::Settings: Clone + Send + Sync + 'static,
P::Id: Debug + Send + 'static, P::Tx: Transaction + Clone + Debug + Send + Sync + 'static,
P::Tx: Clone + Debug + Send + Sync + 'static, <P::Tx as Transaction>::Hash: Debug + Send + Sync + 'static,
N: NetworkAdapter<Tx = P::Tx> + Send + Sync + 'static, N: NetworkAdapter<Tx = P::Tx> + Send + Sync + 'static,
{ {
fn init(service_state: ServiceStateHandle<Self>) -> Result<Self, overwatch_rs::DynError> { fn init(service_state: ServiceStateHandle<Self>) -> Result<Self, overwatch_rs::DynError> {

View File

@ -1,7 +1,4 @@
use nomos_core::{ use nomos_core::{block::BlockId, tx::mock::MockTransaction};
block::BlockId,
tx::mock::{MockTransaction, MockTxId},
};
use nomos_log::{Logger, LoggerSettings}; use nomos_log::{Logger, LoggerSettings};
use nomos_network::{ use nomos_network::{
backends::mock::{Mock, MockBackendMessage, MockConfig, MockMessage}, backends::mock::{Mock, MockBackendMessage, MockConfig, MockMessage},
@ -20,7 +17,7 @@ use nomos_mempool::{
struct MockPoolNode { struct MockPoolNode {
logging: ServiceHandle<Logger>, logging: ServiceHandle<Logger>,
network: ServiceHandle<NetworkService<Mock>>, network: ServiceHandle<NetworkService<Mock>>,
mockpool: ServiceHandle<MempoolService<MockAdapter, MockPool<MockTxId, MockTransaction>>>, mockpool: ServiceHandle<MempoolService<MockAdapter, MockPool<MockTransaction>>>,
} }
#[test] #[test]
@ -70,7 +67,7 @@ fn test_mockmempool() {
let network = app.handle().relay::<NetworkService<Mock>>(); let network = app.handle().relay::<NetworkService<Mock>>();
let mempool = app let mempool = app
.handle() .handle()
.relay::<MempoolService<MockAdapter, MockPool<MockTxId, MockTransaction>>>(); .relay::<MempoolService<MockAdapter, MockPool<MockTransaction>>>();
app.spawn(async move { app.spawn(async move {
let network_outbound = network.connect().await.unwrap(); let network_outbound = network.connect().await.unwrap();