Protocol Revisions
Table of Contents
Development of the reference implementation for the protocol ended up stalling somewhat due to the complexity of the initial protocol draft. To address this, major revisions to the protocol were made for a second draft in order to cut down the functionality into a more manageable scope for the project. Many of the capabilities described by the initial draft were more complex than could feasibly be implemented during the course, this expanded functionality could instead be provided by later extensions to the protocol.
The Revised Protocol #
The initial drafts of the protocol allowed for peers to be able to host multiple playlists, communicate playlist state between all peers, and for peers to request modifications to playlists beyond simply adding songs (removal, re-ordering, etc.) While simple to design at the protocol level, this was more complex than expected to implement, as it would require systems in place to ensure all peers correctly shared and communicated playlist state without de-sync and some form of unique ID allocation for playlists and songs to ensure peers could properly communicate playlist modification requests to each other. Furthermore, this was also beyond the scope of deliverables from the project proposal, which only detailed the capacity for peers to request songs be added to playlist.
As such, the protocol has been cut back to only provide the core capabilities set out by the proposal, defined by three message types below. Note these are formatted using Rust syntax as they appear in the reference implementation and do not correspond to the actual message formats.
- Request messages are sent between individual peers to request certain
actions or information, these include:
Request::Playlist
, which requests the metadata of the playlist hosted by the receiver, if it is hosting a playlist.Request::AddSong(SongMetadata)
is used by peers to request that the receiver add the song described by theSongMetadata
object to their playlist.SongMetadata
is ajson
dict containing enough information to uniquely identify the song. (Song title, artist, album, release date, etc.) The receiver is responsible for using this data to identify whether it has access to this song in it’s own library or not.Request::SongFile(SongMetadata)
is used to request the actual audio file of a song from a peer. This is used by hosting peers, who would otherwise be unable to fulfill anAddSong
request because they could not find the song in their own library. If this was the case, they send aSongFile
request back to the peer which sent theAddSong
.
- Response messages are sent by peers in direct response to a request
message, and all requests must have an according response.
Response::Playlist(PlaylistMetadata)
is sent in direct response toRequest::Playlist
containing aPlaylistMetadta
json
object containing the name and description of the playlist, and playlist state consisting of the number of songs andSongMetadata
of the currently playing and next-up songs if they exist.Response::NoPlaylist
is sent in response toRequest::Playlist
if the peer which received the request was not hosting a playlist when the request was recieved.Response::SongRejected
is sent in response to aRequest::AddSong
message if the receiver either a) was not able to find the song in their own library or b) did not receive the song after sending aRequest::SongFile
message to the originating peer.Response::SongAccepted
is sent in response toRequest::AddSong
if the song was added to the playlist successfully.Response::SongFile(song)
is sent in response toRequest::SongFile
containing the file requested.Response::UnableToProvide
is sent in response toRequest::SongFile
if the receiver cannot provide the song that was requested.
- Broadcast messages are sent to all or some peers to inform them of
relevant information.
HostingPlaylist(PlaylistMetadata)
announces that the sender is now hosing a playlist described byPlaylistMetadata
and can accept songs.StoppedHosting
announces that the sender has stopped hosting their playlist.PlaylistUpdate(PlaylistMetadata)
announces that the state of the sender’s playlist has updated to the new state described in the includedPlaylistMetadta
object.
This revised and trimmed down protocol specification is much more in-line with the initial project proposal and significantly more feasible to implement.
Implementation Progress and Changes #
After running into limitations with the basic request-response system provided
with libp2p
and following some additional research, it was decided that
Broadcast messages in the reference library will be built on top of the
Gossipsub implementation provided by libp2p
. Primarily a protocol for p2p
chat applications, Gossipsub allows peers to subscribe to and post messages on
topics. Messages posted on a topic are then broadcast to all subscribed peers.
This greatly simplified the reference implementation, as now a playlist
topic,
subscribed to by all peers, can be used to announce when peers are
hosting/stopping hosting their playlists, and peers now create Gossipsub topics
for their playlists which they use to broadcast state changes on. The only
limitation of this approach is that it greatly hinders the flexibility of the
reference library, as it would not be able to inter-operate with other
implementations that do not use Gossipsub for message broadcasting. The actual
protocol however, does not require or specify a specific method of sending
broadcast messages, and the reference library would be updated and revised with
a more universal broadcasting capability for real-world use if this project were
to continue beyond the scope of this course project, but Gossipsub works well
for demonstration purposes and simplifies development.
Otherwise, the Server
described in the first update has been almost entirely
complete, with the most difficult part—the network event loop and message
handling—fully functional. This means that the work going forward will be
easier and more straightforward as it just involves adding additional match
cases for the remaining message types.
The Client
implementation has also been revised alongside the protocol changes
to reduce the complexity that would be required when implementing a Client
.
The current iteration of the Client
trait is as follows:
use crate::types::*;
pub trait Client {
// Methods for the Server to get information about the Client.
fn hosted_playlist(&self) -> Option<PlaylistMetadata>;
// Methods for the Server to notify the Client of changes to playlists.
/// A new playlist has been discovered in the network.
fn playlist_discovered(&self, playlist: PlaylistId);
/// A playlist has been removed from the network.
fn playlist_removed(&self, playlist_id: PlaylistId);
/// The subscribed playlist's state has changed.
fn subscribed_playlist_update(&self, playlist_data: PlaylistMetadata);
/// A peer has requested to add a song to the playlist
fn add_song_request(&self, song: SongMetadata) -> Result<(), Error>;
}
The only additional function which will need to be added is
song_request(&self, song: SongMetadata) -> Option<SongData>
to fulfill
Request::SongFile
, and then the only thing that would be required to implement
in order to use this library is an implementation of Client
according to this
specification, which would be provided to an instance of Server
.
Next Steps #
The next stage of the project will consist of finalizing the reference
implementation and implementing the remaining functionality of the protocol, as
handling Request::SongFile
and related messages is not yet fully completed due
to the time spent on revising the protocol.
The last major deliverable to work on is to expand the current SampleClient
to
include a small library of songs that it can use and actual playlists beyond the
hard-coded test systems. Essentially this will be implementing an extremely
basic interface to demonstrate practical use of the protocol beyond the
hard-coded test interactions that have been implemented.
Work on a test suite was also started, which will be expanded upon as well.