Trait SyncProtocol

Source
pub trait SyncProtocol<'a, T>
where Self: Send + Sync + Debug, T: TopicQuery,
{ // Required methods fn name(&self) -> &'static str; fn initiate<'async_trait>( self: Arc<Self>, topic_query: T, tx: Box<&'a mut (dyn AsyncWrite + Send + Unpin)>, rx: Box<&'a mut (dyn AsyncRead + Send + Unpin)>, app_tx: Box<&'a mut (dyn Sink<FromSync<T>, Error = SyncError> + Send + Unpin)>, ) -> Pin<Box<dyn Future<Output = Result<(), SyncError>> + Send + 'async_trait>> where Self: 'async_trait, 'a: 'async_trait; fn accept<'async_trait>( self: Arc<Self>, tx: Box<&'a mut (dyn AsyncWrite + Send + Unpin)>, rx: Box<&'a mut (dyn AsyncRead + Send + Unpin)>, app_tx: Box<&'a mut (dyn Sink<FromSync<T>, Error = SyncError> + Send + Unpin)>, ) -> Pin<Box<dyn Future<Output = Result<(), SyncError>> + Send + 'async_trait>> where Self: 'async_trait, 'a: 'async_trait; }
Expand description

Traits to implement a custom sync protocol.

Implementing a SyncProtocol trait needs extra care and is only required when designing custom low-level peer-to-peer protocols and data types. p2panda already comes with solutions which can be used “out of the box”, providing implementations for most applications and usecases.

§Design

Sync sessions take place when two peers connect to each other and follow the sync protocol. They are designed as a two-party protocol featuring an “initiator” and an “acceptor” role.

Each protocol usually follows two phases: 1) The “Handshake” phase, during which the “initiator” sends the “topic query” and any access control data to the “acceptor”, and 2) The “Sync” phase, where the requested application data is finally exchanged and validated.

§Privacy and Security

The SyncProtocol trait has been designed to allow privacy-respecting implementations where application data (via access control) and the topic query itself (for example via Diffie Hellmann) is securely exchanged without revealing any information to unknown peers unnecessarily. This usually takes place during the “Handshake” phase of the protocol.

The underlying transport layer should provide automatic authentication of the remote peer, a reliable connection and transport encryption. p2panda-net, for example, uses self-certified TLS 1.3 over QUIC.

§Streams

Three distinct data channels are provided by the underlying transport layer to each SyncProtocol implementation: tx for sending data to the remote peer, rx to receive data from the remote peer and app_tx to send received data to the higher-level application-, validation- and persistance-layers.

§Topic queries

Topics queries are generic data types which can be used to subjectively express interest in a particular subset of the data we want to sync over, like chat group identifiers or very specific “search queries”, for example “give me all documents containing the word ‘billy’.”

With the help of the TopicMap trait we can keep sync implementations agnostic to specific topic query implementations. The sync protocol only needs to feed the “topic query” into the “map” which will answer with the actual to-be-synced data entities (for example coming from a store). This allows application developers to re-use your SyncProtocol implementation for their custom TopicQuery requirements.

§Validation

Basic data-format and -encoding validation usually takes place during the “Sync” phase of the protocol.

Further validation which might require more knowledge of the application state or can only be applied after decrypting the payload should be handled outside the sync protocol, by sending it upstream to higher application layers.

§Errors

Protocol implementations operate on multiple layers at the same time, expressed in distinct error categories:

  1. Unexpected behaviour of the remote peer not following the implemented protocol
  2. Handling (rare) critical system failures

Required Methods§

Source

fn name(&self) -> &'static str

Custom identifier for this sync protocol implementation.

This is currently only used for debugging or logging purposes.

Source

fn initiate<'async_trait>( self: Arc<Self>, topic_query: T, tx: Box<&'a mut (dyn AsyncWrite + Send + Unpin)>, rx: Box<&'a mut (dyn AsyncRead + Send + Unpin)>, app_tx: Box<&'a mut (dyn Sink<FromSync<T>, Error = SyncError> + Send + Unpin)>, ) -> Pin<Box<dyn Future<Output = Result<(), SyncError>> + Send + 'async_trait>>
where Self: 'async_trait, 'a: 'async_trait,

Initiate a sync protocol session over the provided bi-directional stream for the given topic query.

During the “Handshake” phase the “initiator” usually requests access and informs the remote peer about the “topic query” they are interested in. Implementations for p2panda-net are required to send a SyncFrom::HandshakeSuccess message to the application layer (via app_tx) during this phase to inform the backend that we’ve successfully requested access, exchanged the topic query with the remote peer and are about to begin sync.

After the “Handshake” is complete the protocol enters the “Sync” phase, during which the actual application data is exchanged with the remote peer. It’s left up to each protocol implementation to decide whether data is exchanged in one or both directions. Synced data is forwarded to the application layers via the SyncFrom::Data message (via app_tx).

In case of a detected failure (either through a critical error on our end or an unexpected behaviour from the remote peer) a SyncError is returned.

Source

fn accept<'async_trait>( self: Arc<Self>, tx: Box<&'a mut (dyn AsyncWrite + Send + Unpin)>, rx: Box<&'a mut (dyn AsyncRead + Send + Unpin)>, app_tx: Box<&'a mut (dyn Sink<FromSync<T>, Error = SyncError> + Send + Unpin)>, ) -> Pin<Box<dyn Future<Output = Result<(), SyncError>> + Send + 'async_trait>>
where Self: 'async_trait, 'a: 'async_trait,

Accept a sync protocol session over the provided bi-directional stream.

During the “Handshake” phase the “acceptor” usually responds to the access request and learns about the “topic query” from the remote peer. Implementations for p2panda-net are required to send a SyncFrom::HandshakeSuccess message to the application layer (via app_tx) during this phase to inform the backend that the topic query has been successfully received from the remote peer and that data exchange is about to begin.

After the “Handshake” is complete the protocol enters the “Sync” phase, during which the actual application data is exchanged with the remote peer. It’s left up to each protocol implementation to decide whether data is exchanged in one or both directions. Synced data is forwarded to the application layers via the SyncFrom::Data message (via app_tx).

In case of a detected failure (either through a critical error on our end or an unexpected behaviour from the remote peer) a SyncError is returned.

Implementors§

Source§

impl<'a> SyncProtocol<'a, SyncTestTopic> for FailingProtocol

Source§

impl<'a> SyncProtocol<'a, SyncTestTopic> for DummyProtocol

Source§

impl<'a> SyncProtocol<'a, SyncTestTopic> for PingPongProtocol

Source§

impl<'a, T, TM, L, E, S> SyncProtocol<'a, T> for LogSyncProtocol<TM, L, E, S>
where T: TopicQuery, TM: TopicLogMap<T, L>, L: LogId + Send + Sync + for<'de> Deserialize<'de> + Serialize + 'a, E: Extensions + Send + Sync + 'a, S: Debug + Sync + LogStore<L, E>,