Building a Debate App: Part 1

By akohad Apr26,2023

[ad_1]

Decentralization / Social Media

Peer to peer database design

Last time we developed an outline of how an online debate app could work from a user’s point of view. This time we’ll delve a bit deeper and think about how to implement collections containing the necessary data, organised for efficient querying.

We’ll be using peer-to-peer database Bonono to implement our collections. Bonono provides indexed collections with four modes of access control:

  • Private (only the owner can read/write)
  • Public read (only the owner can write)
  • Public (anyone can read/write)
  • Write own (anyone can read, anyone can write their own user key)

Access rights apply to the whole collection. This means that data that isn’t updatable by all has to be partitioned – by owner – into separate collections.

The one exception is the ‘write own’ access mode where access varies by key: any user can read and write the data under their own key (equal to their cryptographic public key) but they can only read the data under other keys.

Bonono supports two conflict resolution schemes (how concurrent writes to the same key are handled):

  • Last write wins (select the most recent write by clock value for each key)
  • First write wins (select the oldest write with by clock value for each key)

Last write wins is useful for a collection that is updateable by a single user where the values need to be mutable. First write wins is useful for a collection that is updatable by multiple users, non-cooperatively, where the values need to be immutable (e.g., a registry).

User profile

In Bonono, a user’s identity is their cryptographic public key. This is automatically generated when the database is opened for the first time and is guaranteed to be unique to that user. Furthermore, Bonono ensures that every merged write is verified to have a valid public key and signature so we’re certain it was added by the user with that public key.

Public keys don’t make for identifiable usernames though! For example, if somebody sends a message, we want to show their username — not a long string of hex digits. We need a way to map from their public key to their username.

To achieve this, we can create a username collection keyed by public key and take advantage of the ‘write-own’ access mode to ensure that users can only update their own username.

There’s still a problem though — there’s nothing to stop two different users from picking the same username. To avoid impersonation, we’d like to make sure a unique username is allocated to each user.

We can do this using a first write wins collection keyed by username. The first user to write a key with a particular username takes ownership of that username. The value is simply their public key. Now, when we look up a user from their public key and get their username, we also do a reverse lookup from username to public key to make sure they own it.

There is more to a user than just their cryptographic identity and username though. Most social media apps also allow their users to provide at least:

  • A profile picture
  • A short description of themselves (bio)

We have a couple of options here:

  1. We could change our public key -> username collection to store the additional profile data as well as usernames.
  2. Alternatively, we could create a new collection for each public key containing the additional profile data.

Solution #1 is simpler but the downside is we have to replicate the extra profile data for every user, which might lead to slow replication.

Solution #2 involves another step to fetch the extra data but we can fetch just the profiles we need.

Debate

Next, we’ll need a collection to store debates. Anyone can propose a debate so we need to use a publicly writable collection.

Once a debate has been created it must be immutable. This is because people choose to participate in the debate based on its configuration and therefore it would be unfair to retrospectively alter it. We can ensure immutability using the first write wins conflict resolution mode.

We need to be careful again here that we don’t end up replicating a huge amount of data. We could put all the details for a single debate in its own collection with a name that includes its unique debate id then have a collection that registers all the debate ids.

Moving all the debate details to another collection minimises the amount of data to replicate but it limits our ability to search and filter. It would be useful to keep at least the following items in the main collection:

  • Title – to enable keyword searches
  • End time – to filter by active/inactive
  • Start time – to filter out debates too far in the future

Items that would go into the detail collection could be:

  • Groups specification – used for calculating the vote scaling
  • Rounds specification – number and duration of debate rounds

Debate likes

Now we can propose debates it would be nice if we could also like them so that we have a mechanism for pushing poor quality debates down the order.

To sort debates by likes we need to count up the number of likes for each debate. Therefore, it’s convenient to keep all likes together in a single collection.

The write-own access mode allows us to keep likes from all users together in a single collection while allowing each user to modify only the likes under their own key.

A user needs to be able to both like and unlike a debate, so we need to use the last write wins conflict resolution mode to allow mutability.

Debate events

A debate exists in various states between specified points in time, for example:

  • Waiting to start
  • Round 1
  • Round 2
  • Voting
  • Finished

We need to know where these state transitions occur with respect to the entries in public collections. For example, if we don’t know which votes occurred after voting finished we’re not able to exclude them and people could keep voting forever.

The problem is that there is no concept of wall time in a collection entry, only logical time. We also can’t trust peers to provide a valid timestamp as, for example, malicious actors could spoof it to sneak in a late vote.

One solution is for the debate owner to be responsible for specifying when transitions have occurred by having them record debate events at the appropriate wall time. A debate event specifies the current logical clock values of the relevant public collections along with the new state value.

One downside with this approach is that the debate owner must remain active to ensure the transitions happen at the correct times. They do have the most incentive to ensure the debate proceeds correctly though.

Message

Next, we need to be able to store messages posted to a debate chat by users. Anyone can post a message so we’ll use a public collection. The collection shall hold messages for a single chat room as we’re only interested in one room at a time.

Let’s make this collection append-only so it’s not possible for anyone to change messages once they’ve been posted. To do this we set the conflict resolution mode to first write wins and use a random key for entries.

We’ll sort messages by their logical clock value, which is automatically populated by Bonono and represents the time ordering of entries according to the network. This is safer than relying on peers to provide a correct timestamp.

Message likes

The next thing we need to store is likes for messages. Any user can like (or unlike) a message and users should only be able to change their own likes. This is very similar to the requirements we had for debate likes and we can use a similar approach.

We need a write own collection for each chat room. Each user stores their likes for the messages in that room under their own key. This means only they can edit their own likes. Like counts can be aggregated with a single pass through the collection entries.

Presentation

Any user can publish just one presentation for a debate. We can easily store this with a write-own collection for each debate. A user can store and update their presentation details under their own key.

Presentation likes

Presentation likes work similarly to message likes using a write-own collection for each debate with each users’ likes under their own key.

Votes

Any user can add a single vote for a debate. Once a vote is cast it should be unchangeable so we’ll use first write wins conflict resolution.

To ensure one vote per user we use the write-own access mode so each user can only write their own key, which will contain their single vote.

Permissions

How do we prevent somebody from creating numerous identities, allowing them to cast multiple votes? This is impossible to eliminate without access to a means of uniquely identifying an actual person.

One approach to minimise this could be to allow voting permission to be granted/rescinded. Casting a human eye over a user’s profile and activity prior to granting voting rights would likely catch a lot of fake users.

Who should be responsible for this? Initially it could be the founder(s) but this would have to be expanded for two main reasons:

  • If the number of users increased significantly, the workload would be too high for a small number of people
  • Such central control in the hands of a small number of people defeats the objective of decentralization as they become an easy target to attack.

Thus, there needs to be a way to delegate this responsibility to other users and ultimately for them to assert their independence.

This could be implemented as permission trees such that there’s a list of root users specified in the app source who can:

  • grant permission to vote
  • grant permission to grant permissions

Users below the root level can have their rights rescinded by those above them but if they demonstrate commitment over time they can be promoted to root by adding them to the hard coded list.

It would be the job of the founders to spread out the permissions fairly and transparently in the first place so that there is fair representation and therefore credibility.

A mechanism could also be added for flagging inappropriate activity to help rescind permissions from bad actors.

A permissions collection could be implemented as a last write wins, write-own collection containing the permissions granted by each user. This could be quickly processed to derive each users permissions.

Next time

In this part, we’ve considered how to represent our debate app’s data efficiently in several peer to peer collections. Next time we’ll start implementing our app.

[ad_2]

Source link

By akohad

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *