Валидатор оракла Отметки времени

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

Общая схема предлагаемой реализации выглядит следующим образом:

Требования:

Та же реализация может предоставить оценку временной метки для еще не укоренившегося блока. Однако, поскольку самый последний слот с временной меткой может быть еще не внедрен, эта временная метка будет нестабильной (потенциально несоответствующей требованию 1). Первоначальная реализация будет нацелена на корневые блоки, но если есть вариант использования временной метки последнего блока, в будущем будет несложно добавить API RPC.

Время записи

Через регулярные промежутки времени, когда он голосует в определенном слоте, каждый валидатор записывает наблюдаемое время, включая временную метку в свои инструкции по голосованию. Соответствующий слот для метки времени является самым новым слотом в векторе голосования (Vote::slots.iter().max()). Он подписывается парой ключей идентификации валидатора как обычное голосование. Чтобы включить эту отчетность, структуру Vote необходимо расширить, включив в нее поле временной метки timestamp: Option<UnixTimestamp>, для которого будет установлено значение None в большинстве голосов.

Начиная с https://github.com/solana-labs/solana/pull/10630, валидаторы отправляют метку времени для каждого голосования. Это позволяет реализовать службу кэширования времени блока, которая позволяет узлам вычислять предполагаемую временную метку сразу после того, как блок укоренен, и кэшировать это значение в Blockstore. Это обеспечивает постоянные данные и быстрые запросы, но при этом удовлетворяет требованию 1) выше.

Аккаунты для голосования

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

Программа голосования

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

Вычисление средневзвешенной временной метки

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

let timestamp_slot = floor(current_slot / timestamp_interval);

Затем валидатору необходимо собрать все транзакции Vote WithTimestamp из реестра, которые ссылаются на этот слот, используя Blockstore::get_slot_entries(). Поскольку для достижения этих транзакций и их обработки лидером могло потребоваться некоторое время, валидатору необходимо просканировать несколько завершенных блоков после timestamp_slot, чтобы получить разумный набор временных меток. Необходимо настроить точное количество слотов: большее количество слотов позволит увеличить участие кластера и увеличить количество точек данных с отметками времени; меньшее количество слотов ускорит фильтрацию временных меток.

Из этой коллекции транзакций валидатор вычисляет средневзвешенную временную метку, ссылаясь на доли эпохи из staking_utils::staked_nodes_at_epoch().

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

Расчет предполагаемого времени для определенного блока

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

let block_n_timestamp = mean_timestamp + (block_n_slot_offset * slot_duration);

где block_n_slot_offset — это разница между слотом блока N и timestamp_slot, а slot_duration получается из кластера slots_per_year, хранящегося в каждом банке.