Простая оплата и проверка состояния

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

Наивный подход

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

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

Легкие клиенты

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

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

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

Квитанции

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

Доказательство включения транзакции

Доказательство включения транзакции — это структура данных, которая содержит путь Меркла от транзакции через Entry-Merkle к Block-Merkle, который включается в Bank-Hash с требуемым набором голосов валидатора. Цепочка записей PoH, содержащая последующие голоса валидатора, полученные из Bank-Hash, является доказательством подтверждения.

Транзакция Меркле

Entry-Merkle — это Merkle Root, включающий все транзакции в данной записи, отсортированные по подписи. Каждая транзакция в записи уже мерклена здесь: https://github.com/solana-labs/solana/blob/b6bfed64cb159ee67bb6bdbaefc7f833bbed3563/ledger/src/entry.rs#L205. Это означает, что мы можем показать, что транзакция «T» была включена в запись «E».

Block-Merkle — это корень Merkle всех Entry-Merkles, секвенированных в блоке.

Block Merkle Diagram

Вместе два доказательства меркла показывают, что транзакция «T» была включена в блок с банковским хэшем «B».

Accounts-Hash — это хэш конкатенации хэшей состояния каждой учетной записи, измененной в течение текущего слота.

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

В настоящее время Block-Merkle не реализован, поэтому для проверки того, что «E» была записью в блоке с хэшем банка «B», нам нужно будет предоставить все хэши записей в блоке. В идеале этот Блок-Меркл должен быть реализован, так как альтернатива очень неэффективна.

Заголовки блоков

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

Более конкретно, легкий клиент должен будет отслеживать входящие заголовки блоков, чтобы, имея два хэша банка для блоков «А» и «В», он мог определить, является ли «А» предком «В» (ниже раздел «Оптимистическое подтверждение»). Доказательство объясняет, почему!). Содержимое заголовка — это поля, необходимые для вычисления хэша банка.

Bank-Hash — это хэш объединения Block-Merkle и Accounts-Hash, описанный выше в разделе «Transaction Merkle».

Bank Hash Diagram

В коде:

https://github.com/solana-labs/solana/blob/b6bfed64cb159ee67bb6bdbaefc7f833bbed3563/runtime/src/bank.rs#L3468-L3473

        let mut hash = hashv(&[
            // bank hash of the parent block
            self.parent_hash.as_ref(),
            // hash of all the modifed accounts
            accounts_delta_hash.hash.as_ref(),
            // Number of signatures processed in this block
            &signature_count_buf,
            // Last PoH hash in this block
            self.latest_blockhash().as_ref(),
        ]);

Хорошее место для реализации этой логики наряду с существующей логикой потоковой передачи в логике воспроизведения валидатора: https://github.com/solana-labs/solana/blob/b6bfed64cb159ee67bb6bdbaefc7f833bbed3563/core/src/replay_stage.rs#L1092-L1096

Оптимистическое подтверждение

В настоящее время оптимистичное подтверждение обнаруживается с помощью прослушивателя, который отслеживает сплетни и конвейер воспроизведения для голосов: https://github.com/solana-labs/solana/blob/b6bfed64cb159ee67bb6bdbaefc7f833bbed3563/core/src/cluster_info_vote_listener.rs#L604-L614.

Каждое голосование представляет собой подписанную транзакцию, которая включает в себя банковский хэш блока, за который проголосовал валидатор, то есть «B» из раздела «Транзакция Merkle» выше. Как только определенный порог T сети проголосовал за блок, блок считается оптимально подтвержденным. Голоса, сделанные этой группой валидаторов «T», необходимы, чтобы показать, что блок с хэшем банка «B» был оптимистично подтвержден.

Однако, за исключением некоторых метаданных, сами подписанные голоса в настоящее время нигде не хранятся, поэтому их нельзя получить по запросу. Эти голоса, вероятно, должны быть сохранены в базе данных Rocksdb, проиндексированы ключом (Slot, Hash, Pubkey), который представляет слот голосования, банковский хеш голосования и публичный ключ счета голосования, ответственный за голосование.

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

Важно отметить, что оптимистическое подтверждение «B» также подразумевает, что все блоки-предки «B» также будут подтверждены оптимистично, а также что не все блоки будут подтверждены оптимистично.

B -> B'

Таким образом, в приведенном выше примере, если блок «B» оптимально подтвержден, то и «B» тоже. Таким образом, если транзакция была в блоке «B», меркл транзакции в доказательстве будет для блока «B», но голоса, представленные в доказательстве, будут для блока «B». Вот почему заголовки в разделе «Заголовки блока» выше важны, клиент должен будет убедиться, что «B» действительно является предком «B».

Доказательство распределения доли

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

Один из способов приблизиться к этому может состоять в том, чтобы для каждой эпохи, когда набор ставок меняется, записывать все доли в системную учетную запись, а затем валидаторы подписывались на эту системную учетную запись. Затем полные узлы могут предоставить меркл, доказывающий, что состояние системной учетной записи было обновлено в некотором блоке «B», а затем показать, что блок «B» был оптимистично подтвержден/укоренен.

Проверка состояния аккаунта

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

Голоса валидатора

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

Цепочка записей

Квитанция имеет PoH-ссылку от корня платежа или состояния Merkle Path к списку последовательных голосов проверки.

Он содержит следующее:

И вектор записей PoH:

/// This Entry definition skips over the transactions and only contains the
/// hash of the transactions used to modify PoH.
LightEntry {
    /// The number of hashes since the previous Entry ID.
    pub num_hashes: u64,
    /// The SHA-256 hash `num_hashes` after the previous Entry ID.
    hash: Hash,
    /// The Merkle Root of the transactions encoded into the Entry.
    entry_hash: Hash,
}

Легкие записи реконструируются из записей и просто показывают запись Merkle Root, которая была смешана с хэшем PoH, вместо полного набора транзакций.

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

Проверка

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

Синтетическое состояние

Синтетическое состояние должно быть вычислено в Bank-Hash вместе с состоянием, сгенерированным банком.

Например:

Эти значения должны иметь запись в Bank-Hash. Они должны жить под известными учетными записями и, следовательно, иметь индекс в конкатенации хэшей.