use std::collections::BTreeMap;
use futures_channel::mpsc;
use futures_util::{FutureExt, SinkExt, Stream, StreamExt};
use p2panda_core::{Body, Hash, Header, Operation, SeqNum, SigningKey, Topic, VerifyingKey};
use p2panda_store::logs::LogStore;
use p2panda_store::operations::OperationStore;
use p2panda_store::topics::TopicStore;
use p2panda_store::{SqliteStore, Transaction, tx_unwrap};
use rand::RngExt;
use rand::rngs::StdRng;
use tokio::join;
use tokio::sync::broadcast;
use crate::ToSync;
use crate::manager::TopicSyncManager;
use crate::protocols::{
LogSync, LogSyncError, LogSyncEvent, LogSyncMessage, Logs, TopicLogSync, TopicLogSyncError,
TopicLogSyncEvent, TopicLogSyncMessage,
};
use crate::traits::Protocol;
pub type TestLogId = usize;
pub type TestExtensions = TestLogId;
pub type TestLogSyncMessage = LogSyncMessage<TestLogId>;
pub type TestLogSyncEvent = LogSyncEvent<TestExtensions>;
pub type TestLogSync = LogSync<TestLogId, TestExtensions, SqliteStore, TestLogSyncEvent>;
pub type TestLogSyncError = LogSyncError;
pub type TestTopicSyncMessage = TopicLogSyncMessage<TestLogId, TestExtensions>;
pub type TestTopicSyncEvent = TopicLogSyncEvent<TestExtensions>;
pub type TestTopicSync = TopicLogSync<Topic, SqliteStore, TestLogId, TestExtensions>;
pub type TestTopicSyncError = TopicLogSyncError;
pub type TestTopicSyncManager = TopicSyncManager<Topic, SqliteStore, TestLogId, TestExtensions>;
pub struct Peer {
pub store: SqliteStore,
pub signing_key: SigningKey,
}
impl Peer {
pub async fn new(peer_id: u64) -> Self {
let store = SqliteStore::temporary().await;
let mut rng = <StdRng as rand::SeedableRng>::seed_from_u64(peer_id);
let signing_key = SigningKey::from_bytes(&rng.random());
Self { store, signing_key }
}
pub fn id(&self) -> VerifyingKey {
self.signing_key.verifying_key()
}
pub fn topic_sync_protocol(
&mut self,
topic: Topic,
live_mode: bool,
) -> (
TestTopicSync,
broadcast::Receiver<TestTopicSyncEvent>,
mpsc::Sender<ToSync<Operation<TestExtensions>>>,
) {
let (event_tx, event_rx) = broadcast::channel(512);
let (live_tx, live_rx) = mpsc::channel(512);
let live_rx = if live_mode { Some(live_rx) } else { None };
let session = TopicLogSync::new(topic, self.store.clone(), live_rx, event_tx);
(session, event_rx, live_tx)
}
pub fn log_sync_protocol(
&mut self,
logs: &Logs<TestLogId>,
) -> (TestLogSync, broadcast::Receiver<TestLogSyncEvent>) {
let (event_tx, event_rx) = broadcast::channel(512);
let session = LogSync::new(self.store.clone(), logs.clone(), event_tx);
(session, event_rx)
}
pub async fn create_operation(
&mut self,
body: &Body,
log_id: TestLogId,
) -> (Header<TestExtensions>, Vec<u8>) {
let (header, header_bytes) = self.create_operation_no_insert(body, log_id).await;
let id = header.hash();
let operation = Operation {
hash: header.hash(),
header: header.clone(),
body: Some(body.to_owned()),
};
tx_unwrap!(&self.store, {
self.store
.insert_operation(&id, &operation, &log_id)
.await
.unwrap();
});
(header, header_bytes)
}
pub async fn create_operation_no_insert(
&mut self,
body: &Body,
log_id: TestLogId,
) -> (Header<TestExtensions>, Vec<u8>) {
let (seq_num, backlink) = <SqliteStore as LogStore<
Operation<TestExtensions>,
VerifyingKey,
TestLogId,
SeqNum,
p2panda_core::Hash,
>>::get_latest_entry(
&self.store, &self.signing_key.verifying_key(), &log_id
)
.await
.unwrap()
.map(|operation| (operation.header.seq_num + 1, Some(operation.hash)))
.unwrap_or((0, None));
let (header, header_bytes) =
create_operation(&self.signing_key, body, seq_num, backlink, log_id);
(header, header_bytes)
}
pub async fn associate(
&mut self,
topic: &Topic,
logs: &BTreeMap<VerifyingKey, Vec<TestLogId>>,
) {
let permit = self.store.begin().await.unwrap();
for (author, logs) in logs {
for log_id in logs {
self.store.associate(topic, author, log_id).await.unwrap();
}
}
self.store.commit(permit).await.unwrap();
}
}
pub async fn run_protocol<P>(
session_local: P,
session_remote: P,
) -> Result<(P::Output, P::Output), P::Error>
where
P: Protocol + Send + 'static,
{
let (mut local_message_tx, local_message_rx) = mpsc::channel(512);
let (mut remote_message_tx, remote_message_rx) = mpsc::channel(512);
let mut local_message_rx = local_message_rx.map(Ok::<_, ()>);
let mut remote_message_rx = remote_message_rx.map(Ok::<_, ()>);
let (local_result, remote_result) = join!(
session_local.run(&mut local_message_tx, &mut remote_message_rx),
session_remote.run(&mut remote_message_tx, &mut local_message_rx)
);
let local_output = local_result?;
let remote_output = remote_result?;
Ok((local_output, remote_output))
}
pub async fn run_protocol_uni<P>(
protocol: P,
messages: &[P::Message],
) -> Result<(P::Output, mpsc::Receiver<P::Message>), P::Error>
where
P: Protocol,
P::Message: Clone,
{
let (mut local_message_tx, remote_message_rx) = mpsc::channel(512);
let (mut remote_message_tx, local_message_rx) = mpsc::channel(512);
let mut local_message_rx = local_message_rx.map(Ok::<_, ()>);
for message in messages {
remote_message_tx.send(message.to_owned()).await.unwrap();
}
let result = protocol
.run(&mut local_message_tx, &mut local_message_rx)
.await?;
Ok((result, remote_message_rx))
}
pub fn drain_stream<S>(mut stream: S) -> Vec<S::Item>
where
S: Stream + Unpin,
{
let mut items = Vec::new();
while let Some(Some(item)) = stream.next().now_or_never() {
items.push(item);
}
items
}
pub fn create_operation(
signing_key: &SigningKey,
body: &Body,
seq_num: SeqNum,
backlink: Option<Hash>,
log_id: TestLogId,
) -> (Header<TestExtensions>, Vec<u8>) {
let mut header = Header::<TestExtensions> {
version: 1,
verifying_key: signing_key.verifying_key(),
signature: None,
payload_size: body.size(),
payload_hash: Some(body.hash()),
seq_num,
backlink,
extensions: log_id,
};
header.sign(signing_key);
let header_bytes = header.to_bytes();
(header, header_bytes)
}