Децентрализация / социальные сети

Создание приложения для дебатов: часть 1

Проект одноранговой базы данных

В прошлый раз мы разработали схему того, как приложение для онлайн-дебатов могло бы работать с точки зрения пользователя. На этот раз мы копнем немного глубже и подумаем, как реализовать коллекции, содержащие необходимые данные, организованные для эффективного выполнения запросов.

Мы будем использовать одноранговую базу данных Bonono для реализации наших коллекций. Bonono предоставляет индексированные коллекции с четырьмя режимами контроля доступа:

  • Личное (только владелец может читать/писать)
  • Общедоступно для чтения (только владелец может писать)
  • Общедоступно (любой может читать и писать)
  • Написать собственный (каждый может прочитать, любой может написать свой собственный пользовательский ключ)

Права доступа распространяются на всю коллекцию. Это означает, что данные, которые не могут быть обновлены всеми, должны быть разделены по владельцам на отдельные коллекции.

Единственным исключением является режим доступа «запись собственного», в котором доступ зависит от ключа: любой пользователь может читать и записывать данные под своим собственным ключом (равным его криптографическому открытому ключу), но он может читать данные только под другими ключами.

Bonono поддерживает две схемы разрешения конфликтов (как обрабатываются одновременные записи на один и тот же ключ):

  • Выигрывает последняя запись (выберите самую последнюю запись по значению часов для каждого ключа)
  • Выигрывает первая запись (выберите самую старую запись по значению часов для каждого ключа)

Выигрыш последней записи полезен для коллекции, которая может быть обновлена ​​одним пользователем, где значения должны быть изменяемыми. Выигрыш первой записи полезен для коллекции, которая может быть обновлена ​​несколькими пользователями без совместной работы, где значения должны быть неизменяемыми (например, реестр).

Профиль пользователя

В Bonono личность пользователя — это его криптографический открытый ключ. Он создается автоматически при первом открытии базы данных и гарантированно будет уникальным для этого пользователя. Кроме того, Bonono гарантирует, что каждая объединенная запись проверяется на наличие действительного открытого ключа и подписи, поэтому мы уверены, что она была добавлена ​​пользователем с этим открытым ключом.

Однако открытые ключи не подходят для идентифицируемых имен пользователей! Например, если кто-то отправляет сообщение, мы хотим показать его имя пользователя, а не длинную строку шестнадцатеричных цифр. Нам нужен способ сопоставления их открытого ключа с их именем пользователя.

Для этого мы можем создать коллекцию имен пользователей с открытым ключом и воспользоваться режимом доступа «запись собственного», чтобы пользователи могли обновлять только свое собственное имя пользователя.

Однако проблема все еще существует — ничто не мешает двум разным пользователям выбрать одно и то же имя пользователя. Чтобы избежать олицетворения, мы хотели бы убедиться, что каждому пользователю назначено уникальное имя пользователя.

Мы можем сделать это, используя первую коллекцию выигрышей записи с ключом по имени пользователя. Первый пользователь, написавший ключ с определенным именем пользователя, становится владельцем этого имени пользователя. Значение — это просто их открытый ключ. Теперь, когда мы ищем пользователя по его открытому ключу и получаем его имя пользователя, мы также выполняем обратный поиск от имени пользователя к открытому ключу, чтобы убедиться, что он им владеет.

Однако для пользователя есть нечто большее, чем просто его криптографическая идентификация и имя пользователя. Большинство приложений для социальных сетей также позволяют своим пользователям предоставлять как минимум:

  • Изображение профиля
  • Краткое описание себя (био)

Здесь у нас есть несколько вариантов:

  1. Мы могли бы изменить нашу коллекцию открытых ключей -> имен пользователей, чтобы хранить дополнительные данные профиля, а также имена пользователей.
  2. В качестве альтернативы мы могли бы создать новую коллекцию для каждого открытого ключа, содержащую дополнительные данные профиля.

Решение №1 проще, но недостатком является то, что нам нужно реплицировать дополнительные данные профиля для каждого пользователя, что может привести к медленной репликации.

Решение № 2 включает в себя еще один шаг для получения дополнительных данных, но мы можем получить только те профили, которые нам нужны.

Дебаты

Далее нам понадобится коллекция для хранения дебатов. Любой может предложить дебаты, поэтому нам нужно использовать публично доступную для записи коллекцию.

После того, как дебаты были созданы, они должны быть неизменными. Это связано с тем, что люди выбирают участие в дебатах на основе его конфигурации, и поэтому было бы несправедливо изменять его задним числом. Мы можем обеспечить неизменность, используя режим разрешения конфликтов с первой записью.

Здесь нам снова нужно быть осторожными, чтобы в итоге мы не реплицировали огромное количество данных. Мы могли бы поместить все детали одного обсуждения в его собственную коллекцию с именем, которое включает его уникальный идентификатор обсуждения, а затем иметь коллекцию, в которой зарегистрированы все идентификаторы обсуждения.

Перемещение всех деталей дебатов в другую коллекцию сводит к минимуму объем данных для репликации, но ограничивает наши возможности поиска и фильтрации. Было бы полезно сохранить в основной коллекции хотя бы следующие элементы:

  • Заголовок — для включения поиска по ключевым словам.
  • Время окончания — для фильтрации по активным/неактивным
  • Время начала – чтобы отфильтровать дебаты, находящиеся слишком далеко в будущем.

Элементы, которые войдут в коллекцию деталей, могут быть:

  • Спецификация групп — используется для расчета масштабирования голосования.
  • Спецификация раундов – количество и продолжительность раундов дебатов.

Дебаты лайки

Теперь мы можем предлагать дебаты. Было бы неплохо, если бы они нам тоже нравились, чтобы у нас был механизм для отодвигания некачественных дебатов вниз по порядку.

Чтобы отсортировать дебаты по лайкам, нам нужно подсчитать количество лайков для каждого дебата. Поэтому удобно хранить все лайки вместе в одной коллекции.

Режим доступа на запись позволяет нам хранить лайки от всех пользователей вместе в одной коллекции, позволяя каждому пользователю изменять только лайки под своим собственным ключом.

Пользователь должен иметь возможность как ставить лайки, так и не принимать дебаты, поэтому нам нужно использовать режим разрешения конфликтов последней записи, чтобы обеспечить изменчивость.

Дебаты

Дебаты существуют в различных состояниях между указанными моментами времени, например:

  • Ожидание начала
  • Раунд 1
  • Раунд 2
  • Голосование
  • Законченный

Нам нужно знать, где происходят эти переходы состояний по отношению к записям в общедоступных коллекциях. Например, если мы не знаем, какие голоса произошли после завершения голосования, мы не можем их исключить, и люди могут продолжать голосовать вечно.

Проблема в том, что в записи коллекции нет понятия времени на стене, только логическое время. Мы также не можем доверять одноранговым узлам в предоставлении действительной метки времени, поскольку, например, злоумышленники могут подделать ее, чтобы прокрасться в позднем голосовании.

Одно из решений состоит в том, чтобы владелец дебатов отвечал за определение того, когда происходили переходы, заставляя их записывать дебаты в соответствующее время на стене. Событие обсуждения указывает текущие значения логических часов соответствующих общедоступных коллекций вместе с новым значением состояния.

Недостатком этого подхода является то, что владелец дебатов должен оставаться активным, чтобы переходы происходили в нужное время. Тем не менее, у них есть наибольший стимул для обеспечения правильного хода дебатов.

Сообщение

Далее нам нужно иметь возможность хранить сообщения, размещенные пользователями в чате дебатов. Любой может опубликовать сообщение, поэтому мы будем использовать общедоступную коллекцию. Коллекция должна содержать сообщения для одной комнаты чата, поскольку нас интересует только одна комната за раз.

Давайте сделаем эту коллекцию доступной только для добавления, чтобы никто не мог изменить сообщения после их публикации. Для этого мы устанавливаем режим разрешения конфликтов, чтобы сначала записывать выигрыши и использовать случайный ключ для записей.

Мы будем сортировать сообщения по их логическому значению часов, которое автоматически заполняется Bonono и представляет временной порядок записей в соответствии с сетью. Это безопаснее, чем полагаться на одноранговые узлы для предоставления правильной временной метки.

Сообщение нравится

Следующее, что нам нужно сохранить, это лайки для сообщений. Любому пользователю может понравиться (или не понравиться) сообщение, и пользователи должны иметь возможность изменять только свои собственные предпочтения. Это очень похоже на требования, которые мы предъявляли к лайкам дебатов, и мы можем использовать аналогичный подход.

Нам нужно написать собственную коллекцию для каждого чата. Каждый пользователь хранит лайки сообщений в этой комнате под своим ключом. Это означает, что только они могут редактировать свои лайки. Подсчет лайков может быть агрегирован за один проход по записям коллекции.

Презентация

Любой пользователь может опубликовать только одну презентацию для дебатов. Мы можем легко сохранить это в собственной коллекции для каждого дебата. Пользователь может хранить и обновлять информацию о своей презентации под своим собственным ключом.

Презентация нравится

Лайки презентации работают аналогично лайкам сообщений, используя собственную коллекцию для каждой дискуссии с лайками каждого пользователя под своим собственным ключом.

Голоса

Любой пользователь может добавить один голос за дебаты. После того, как голос будет подан, он не должен быть изменен, поэтому мы будем использовать разрешение конфликтов с первой записью.

Чтобы обеспечить один голос для каждого пользователя, мы используем режим доступа для записи, поэтому каждый пользователь может написать только свой собственный ключ, который будет содержать его единственный голос.

Разрешения

Как мы можем помешать кому-то создать множество личностей, позволяющих им подавать несколько голосов? Это невозможно устранить без доступа к средствам однозначной идентификации реального человека.

Одним из подходов к минимизации этого может быть предоставление/отмена разрешения на голосование. Взгляд человека на профиль и активность пользователя перед предоставлением права голоса, скорее всего, поймает много фальшивых пользователей.

Кто должен нести за это ответственность? Первоначально это мог быть учредитель(и), но его пришлось бы расширить по двум основным причинам:

  • Если бы количество пользователей значительно увеличилось, рабочая нагрузка была бы слишком высокой для небольшого числа людей.
  • Такой централизованный контроль в руках небольшого числа людей сводит на нет цель децентрализации, поскольку они становятся легкой мишенью для атак.

Таким образом, должен быть способ делегировать эту ответственность другим пользователям и, в конечном счете, чтобы они отстаивали свою независимость.

Это можно реализовать в виде деревьев разрешений, чтобы в исходном коде приложения был указан список привилегированных пользователей, которые могут:

  • дать разрешение на голосование
  • предоставить разрешение на предоставление разрешений

Права пользователей ниже корневого уровня могут быть аннулированы вышестоящими, но если они продемонстрируют приверженность со временем, их можно будет повысить до корневого уровня, добавив их в жестко закодированный список.

Работа учредителей будет заключаться в том, чтобы распространять разрешения справедливо и прозрачно, в первую очередь, чтобы обеспечить справедливое представительство и, следовательно, доверие.

Также может быть добавлен механизм для пометки ненадлежащей активности, чтобы помочь отменить разрешения от злоумышленников.

Коллекция разрешений может быть реализована как последняя запись выигрывает, запись-собственная коллекция, содержащая разрешения, предоставленные каждым пользователем. Это может быть быстро обработано для получения разрешений каждого пользователя.

В следующий раз

В этой части мы рассмотрели, как эффективно представлять данные нашего приложения для дебатов в нескольких одноранговых коллекциях. В следующий раз мы начнем реализовывать наше приложение.