ZK-доказательства: отношение и публичные входы
Какое утверждение нужно библиотеке
Ранее мы рассматривали ZK-rollup, где доказательство подтверждает переход состояния. Теперь используем доказательства для другого действия: участник группы курса 17 хочет получить одно право на учебный материал, не раскрывая, какое именно обязательство в списке принадлежит ему. Это собственное расширение AccessClaimRegistry. В базовый CourseCatalog не добавляется скрытый механизм допуска.
Первый шаг — описать утверждение словами. Пользователь знает секрет, из которого получается зарегистрированное обязательство участника. Это обязательство входит в разрешённую группу. Доказательство связано с конкретным действием библиотеки и получателем результата. Ничего в таком утверждении пока не говорит об оплате, правильном решении задания или реальном имени человека. Эти дополнительные условия нельзя приписывать доказательству только потому, что оно криптографическое.
Схема приложения опирается на понятия членства и сообщения из Semaphore V4. Конкретную интеграцию с библиотекой ещё не реализуем. Нам нужен точный договор о входах и результатах, прежде чем выбирать пакеты, артефакты схемы и проверяющий контракт.
Отношение между публичным и скрытым
В доказательных системах полезно говорить об отношении R(x, w). x — публичное утверждение, w — свидетель, то есть данные, позволяющие подтвердить его. Проверяющий должен убедиться, что подходящий свидетель существует, не получая его содержимое.
В нашем сценарии публичная часть содержит корень группы и сообщение действия. В сообщении библиотеки кодируются версия схемы, курс 17, выпуск 2, тип права и получатель. Область однократного использования задаётся отдельным scope. Скрытая часть содержит секрет участника и путь членства. Точный состав полей готовой системы может отличаться, поэтому не объявляем этот список готовым ABI Semaphore.
R(publicStatement, witness):
identityCommitment = deriveCommitment(witness.secret)
membership(identityCommitment, witness.path, publicStatement.root)
bind(publicStatement.message, libraryAction)
deriveNullifier(witness.secret, publicStatement.scope)
Это модель требований, не исполняемая схема. deriveCommitment, membership и bind здесь обозначают отношения, а не написанные безопасные функции. В настоящей реализации используются совместимые конструкции выбранного протокола. Самовольная замена одной операции обычным хешированием строки меняет утверждение и может разрушить его свойства.
Почему публичного корня недостаточно
Пусть корень R1 действительно соответствует списку участников. Если проверяющий принимает любой переданный корень, злоумышленник создаёт свою группу с единственным участником и получает корректное доказательство. Криптография сработала: человек подтвердил членство. Ошибка приложения состоит в том, что группа не имела отношения к библиотеке.
Поэтому доверенное состояние задаёт groupId и политику допустимых корней. Администратор группы управляет включением участников. Его полномочия — отдельная часть модели: если он добавил себя тысячу раз, доказательство не обнаружит несправедливость процедуры регистрации. Корень фиксирует набор, но не обосновывает законность каждого включения.
Когда состав меняется, появляются R1 и R2. Нужно решить, принимаются ли старые корни и как долго. Для библиотеки удаление права и добавление нового участника имеют разные последствия. Окно принятия старых доказательств удобно при задержке, но означает, что удалённое обязательство может оставаться допустимым в этом окне.
Маленькая арифметическая схема
Чтобы почувствовать разницу между вычислением и ограничением, рассмотрим отдельный учебный фрагмент Circom. Он доказывает связь двух скрытых множителей с публичным произведением. Это не доказательство членства и не система доступа.
pragma circom 2.0.0;
template ProductRelation() {
signal input a;
signal input b;
signal output product;
product <== a * b;
}
component main = ProductRelation();
При a = 3 и b = 4 ожидаем product = 12. Оператор <== связывает назначение значения с ограничением. Простое <-- вычисляло бы значение для построения свидетеля без соответствующего ограничения. Документация Circom объясняет это различие. Компилятор и доказательство здесь не запускались.
Изменим свидетель: a = 2, b = 6. Публичный результат остаётся 12, и отношение по-прежнему выполнено. Следовательно, доказательство такого отношения не устанавливает единственную пару. Оно также не подтверждает, что множители являются простыми, положительными целыми в желаемом диапазоне или принадлежат конкретному человеку. Требуемые ограничения должны присутствовать в самой схеме.
Арифметика поля и прикладные числа
Сигналы схемы живут в конечном поле. Обычное ожидание о целых числах не появляется автоматически. Если приложению нужен возраст в диапазоне или сумма в сатоши с верхней границей, задаются соответствующие ограничения и представление. Нельзя перенести JavaScript-сравнение в текст урока и считать, что схема уже проверяет его.
Для нашей библиотеки это особенно заметно при кодировании courseId и edition. Два разных сообщения не должны становиться одним значением из-за неоднозначного представления, обрезки или несовместимого преобразования. Спецификация описывает порядок полей, длины, версию и перенос сообщения в допустимое представление системы.
ContentHash выпуска остаётся SHA-256 точных байтов манифеста. CID сохраняется целиком отдельно. Эти соглашения не превращают произвольные 32 байта в корректный сигнал схемы. Если нужно связывать доказательство с выпуском, проектируем явное кодирование и проверяем его совместимость, а не обрезаем значения ради удобного поля.
Предположим, преподаватель хочет разрешать доступ только после решения упражнения. Добавление текста «успешный слушатель» в описание группы этого не доказывает. Можно поручить доверенной роли включать обязательство после проверки решения; тогда схема подтверждает членство в сформированном списке, а качество проверки остаётся обязанностью этой роли. Другой вариант — доказать дополнительные ограничения решения в отдельной схеме. Эти варианты имеют разные источники доверия и стоимость. В нашей первой модели выбираем членство в управляемой группе и прямо называем администратора, чтобы читатель не приписал математической проверке работу преподавателя.
Результат главы
Теперь у нас есть проверяемый вопрос: какой скрытый факт должен существовать и какое публичное действие он разрешает. Для ожидаемых сценариев отдельно меняем корень, сообщение и свидетель. Свидетель другого участника может подтвердить то же право в группе, но сообщение другого получателя не должно разрешать исходное действие. Корректное доказательство для посторонней группы отклоняется политикой библиотеки.
Это программа будущих проверок, без фактической генерации proof. В следующем уроке разберём, где хранится свидетель, какие сведения видит браузер и почему скрытый вход схемы сам по себе не обеспечивает приватность всего пользовательского маршрута.