Репликация леджера

Примечание: это решение для репликации реестра было частично реализовано, но не завершено. Частичная реализация была удалена https://github.com/solana-labs/solana/pull/9992, чтобы предотвратить угрозу безопасности неиспользуемого кода. Первая часть этого проектного документа отражает ранее реализованные части репликации леджера. Вторая часть этого документа описывает части решения, которые так и не были реализованы.

Доказательство репликации

При полной мощности в сети со скоростью 1 Гбит/с solana будет генерировать 4 петабайта данных в год. Чтобы предотвратить централизацию сети вокруг валидаторов, которые должны хранить полный набор данных, этот протокол предлагает майнинговым узлам способ обеспечить емкость для хранения фрагментов данных.

Основная идея Proof of Replication заключается в шифровании набора данных с помощью открытого симметричного ключа с использованием шифрования CBC, а затем хешировании зашифрованного набора данных. Основная проблема с наивным подходом заключается в том, что нечестный узел хранения может передавать шифрование и удалять данные по мере их хеширования. Простое решение — периодически регенерировать хэш на основе подписанного значения PoH. Это гарантирует, что все данные присутствуют во время создания доказательства, а также требует от валидаторов наличия всех зашифрованных данных для проверки каждого доказательства каждой личности. Таким образом, пространство, необходимое для проверки, равно number_of_proofs * data_size.

Оптимизация с PoH

Наше улучшение этого подхода заключается в том, чтобы случайным образом выбирать зашифрованные сегменты быстрее, чем это требуется для шифрования, и записывать хэш этих образцов в реестр PoH. Таким образом, сегменты остаются в одном и том же порядке для каждого PoRep, а проверка может передавать данные и проверять все доказательства в одном пакете. Таким образом, мы можем одновременно проверять несколько доказательств, каждое на своем собственном ядре CUDA. Общее пространство, необходимое для проверки, составляет «1_ledger_segment + 2_cbc_blocks * number_of_identities» с количеством ядер, равным «number_of_identities». Мы используем размер блока CBC чачи 64 байта.

Сеть

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

Архиваторы являются специализированными легкими клиентами. Они загружают часть реестра (также известный как Сегмент) и сохраняют его, а также предоставляют PoReps для хранения реестра. За каждый проверенный PoRep архиваторы получают вознаграждение в виде солей из пула майнинга.

Ограничения

У нас есть следующие ограничения:

Протокол проверки и репликации

Константы

  1. SLOTS_PER_SEGMENT: количество слотов в сегменте данных реестра. Единица хранения для архиватора.

  2. NUM_KEY_ROTATION_SEGMENTS: количество сегментов, после которых архиваторы повторно генерируют свои ключи шифрования и выбирают новый набор данных для хранения.

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

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

  5. NUM_STORAGE_SAMPLES: количество образцов, необходимое для доказательства майнинга хранилища.

  6. NUM_CHACHA_ROUNDS: количество циклов шифрования, выполненных для создания зашифрованного состояния.

  7. NUM_SLOTS_PER_TURN: количество слотов, определяющих одну эпоху хранения или «ход» игры PoRep.

Поведение валидатора

  1. Валидаторы подключаются к сети и начинают искать учетные записи архиваторов на каждом

    граница эпохи/оборота хранения.

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

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

  3. Каждые NUM_SLOTS_PER_TURN слотов валидатор объявляет значение PoH. Это значение также передается архиваторам через интерфейсы RPC.

  4. Для данного хода N все проверки блокируются до хода N+3 (разрыв в 2 хода/эпоху).

    В этот момент все проверки в течение этого хода доступны для получения вознаграждения.

  5. Любые неверные проверки будут отмечены во время промежуточного хода.

Поведение архиватора

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

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

    • (a) архиватор может запросить валидатор
    • (b) архиватор может запрашивать несколько валидаторов
    • Архиватор (c) может спрашивать у других архиваторов
    • Архиватор (d) может подписаться на полный поток транзакций и сам генерировать информацию (при условии, что слот достаточно новый)
    • (e) архиватор может подписаться на сокращенный поток транзакций для генерации самой информации (при условии, что слот достаточно новый)
  2. Архиватор получает хэш PoH, соответствующий последнему ходу со своим слотом.

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

  4. Архиватор извлекает реестр, запрашивая одноранговые валидаторы и архиваторы. См. 6.5.

  5. Затем архиватор шифрует этот сегмент с помощью ключа с помощью алгоритма chacha в режиме CBC с NUM_CHACHA_ROUNDS шифрования.

  6. Архиватор инициализирует цепь передачи подписанным недавним значением PoH в качестве начального значения.

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

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

  9. В течение данного хода архиватор должен предоставить много доказательств для одного и того же сегмента, и на основании RATIO_OF_FAKE_PROOFS некоторые из этих доказательств должны быть поддельными.

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

  11. Наконец, для хода N, поскольку игра PoRep переходит на ход N + 3, доказательства архиватора для хода N будут засчитаны в счет их вознаграждения.

Игра PoRep

Игра Proof of Replication состоит из 4 основных этапов. Для каждого «хода» может выполняться несколько игр PoRep, но каждая на своем этапе.

Четыре этапа игры PoRep состоят в следующем:

  1. Этап подачи доказательств
    • Архиваторы: отправьте как можно больше доказательств на этом этапе.
    • Валидаторы: нет операций
  2. Этап проверки доказательств
    • Архиваторы: нет операций
    • Валидаторы: выберите архиваторов и проверьте их доказательства с предыдущего хода
  3. Этап проверки доказательств
    • Архиваторы: отправьте маску доказательства с обоснованиями (для поддельных доказательств, отправленных 2 хода назад)
    • Валидаторы: нет операций
  4. Этап сбора наград
    • Архиваторы: собирайте награды за 3 хода назад
    • Валидаторы: собирайте награды за 3 хода назад

Для каждого хода игры PoRep и Валидаторы, и Архиваторы оценивают каждый этап. Этапы выполняются как отдельные транзакции в программе хранения.

Поиск того, у кого есть данный блок бухгалтерской книги

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

  2. Валидаторы поддерживают карту сегментов реестра и соответствующих открытых ключей архиватора.

    Карта обновляется, когда Валидатор обрабатывает доказательства архиватора для сегмента.

    Валидатор предоставляет интерфейс RPC для доступа к этой карте. Используя этот API, клиенты могут сопоставить сегмент с сетевым адресом архиватора (сопоставив его через таблицу cluster_info).

    Затем клиенты могут отправлять запросы на восстановление архиватору для извлечения сегментов.

  3. Валидаторы должны будут аннулировать этот список каждые N ходов.

Атаки Сивиллы

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

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

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

Атаки с валидатором

Поощрения

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

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

Примечания

Репликация реестра не реализована

Поведение репликации еще не реализовано.

Эпоха хранения

Эпохой хранения должно быть количество слотов, в результате чего для архиваторов будет создано около 100–1 ТБ реестра. Архиваторы начнут хранить реестр, когда данный форк с высокой вероятностью не будет откатан.

Поведение валидатора

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

    • Младшие 5 бит первого байта подписи создают индекс для другого начального байта подписи.

    • Затем валидатор просматривает набор доказательств хранения, где байт вектора состояния sha доказательства, начиная с младшего байта, точно совпадает с выбранным байтом(s) подписи.

    • Если набор доказательств больше, чем может обработать валидатор, то он увеличивается до соответствия 2 байтам в подписи.

    • Валидатор продолжает увеличивать количество совпадающих байтов, пока не будет найден рабочий набор.

    • Затем он создает маску действительных доказательств и поддельных доказательств и отправляет ее лидеру. Это транзакция подтверждения хранения.

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

Поведение архиватора

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

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

    Архиватор вознаграждается, а баланс стейкинга валидатора сокращается или замораживается.

Логика контракта с защитой от хранения

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

SubmitMiningProof

SubmitMiningProof {
    slot: u64,
    sha_state: Hash,
    signature: Signature,
};
keys = [archiver_keypair]

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

В программе должен быть список слотов, которые являются действительными слотами майнинга хранилища. Этот список следует поддерживать, отслеживая слоты, которые являются укоренившимися слотами, за которые проголосовала значительная часть сети с высоким значением блокировки, возможно, 32-голоса. В этот набор будет добавлено каждое количество слотов SLOTS_PER_SEGMENT. Программа должна проверить, что слот находится в этом наборе. Набор можно поддерживать, получая AdvertiseStorageRecentBlockHash и проверяя его состояние банка/Башни BFT.

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

ProofValidation

ProofValidation {
   proof_mask: Vec<ProofStatus>,
}
keys = [validator_keypair, archiver_keypair(s) (unsigned)]

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

Включенные ключи архиватора будут указывать образцы хранилища, на которые ссылаются; длина proof_mask должна сверяться с набором доказательств хранения в указанной учетной записи архиватора(s) и должна совпадать с количеством доказательств, отправленных в предыдущую эпоху хранения в состоянии указанной учетной записи архиватора.

ClaimStorageReward

ClaimStorageReward {
}
keys = [validator_keypair or archiver_keypair, validator/archiver_keypairs (unsigned)]

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

ChallengeProofValidation

ChallengeProofValidation {
    proof_index: u64,
    hash_seed_value: Vec<u8>,
}
keys = [archiver_keypair, validator_keypair]

Эта транзакция предназначена для отлова ленивых валидаторов, которые не выполняют работу по проверке доказательств. Архиватор отправит эту транзакцию, когда увидит, что валидатор одобрил поддельную транзакцию SubmitMiningProof. Поскольку архиватор является легким клиентом, не просматривающим всю цепочку, ему придется запрашивать эту информацию у валидатора или некоторого набора валидаторов, возможно, через вызов RPC, чтобы получить все ProofValidations для определенного сегмента в предыдущей эпохе хранения. Программа просмотрит состояние учетной записи валидатора, увидит, что ProofValidation отправлена в предыдущую эпоху хранения, и хеширует hash_seed_value, и увидит, что хэш соответствует транзакции SubmitMiningProof и что валидатор пометил ее как действительную. Если это так, то он сохранит вызов в списке вызовов, которые он имеет в своем состоянии.

AdvertiseStorageRecentBlockhash

AdvertiseStorageRecentBlockhash {
    hash: Hash,
    slot: u64,
}

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