Вторая статья Романа Нгуена про True NFT. В этот раз Роман рассказывает, как современные блокчейны подходят к решению вопроса о хранении данных в децентрализованных системах и какие преимущества имеет столь сложная, относительно существующих, модель передачи токенов.
Что такое распределенный реестр?
Если говорить про системы распределенного реестра как таковые, то их реализаций масса — от торрентов и IPFS до блокчейнов. Подход не новый, и, в последнее время, он все больше развивается именно в сторону блокчейнов. Блокчейн, как технология, решает целый ряд разношерстных задач — от обеспечения достоверности информации до ее хранения. Как правило это обычные пары “ключ-значение”, когда некий идентификатор соответствует какому-либо значению. Например “номер-баланс”, “название-статус” или “доменное имя-адрес”. В любом блокчейне со смарт-контрактами мы так или иначе будем иметь подобные структуры.
В чем проблема с обычными реестрами?
В целом ни в чем. Сама структура данных существует с незапамятных времен и успешно применяется как в централизованных системах, так и в децентрализованных, в том числе и в блокчейнах. Однако в блокчейне любые действия (кроме чтения) чего-то стоят, а в некоторых блокчейнах, например во FreeTON, небесплатно и хранение данных.
С точки зрения хранения данных как таковых ключевая проблема всегда в ограниченности носителя — условно-бесконечный реестр требует условно-бесконечного объема хранилища. В реальной жизни мы можем представить большой датацентр как некий единый носитель, который при необходимости можно расширять. В децентрализованых системах (читать как блокчейны со смарт-контрактами) такой подход невозможен. Хранить бесконечный объем данных в одном контракте попросту не получится: мы неизбежно упремся, например, в размер блока или поймаем коллизию, что приведет к непредсказуемым последствиям. Это то же самое, что в реальной жизни не добавлять новые носители в датацентр, а строить еще один рядом. Очевидно, что сравнение весьма абстрактно, однако иллюстрирует ситуацию.
Дополнительно существующие реестры в разрезе блокчейнов, как правило, представляют собой большой смарт-контракт, который поддерживает полный список записей или древовидное решение, которое разбивает список на части на основе некоторых параметров. Подобные решения отпадают из-за недостаточной масштабируемости, высоких затрат на обслуживание, длительного времени поиска при увеличении объема данных, единой точки отказа и так далее.

И что же делать?
Перечислим некоторый набор критериев, которым решение должно соответствовать:
- должно уметь хранить данные в формате ключ — значение;
- должно быть условно бесконечно масштабируемым;
- должно выполнять crud (create read update delete) операции;
- пользовательский опыт взаимодействия с решением не должен отличаться от взаимодействия с обычными реестрами.
Первое, что приходит в голову, — записи реестра нужно разделить на некоторые части, которые точно не будут превышать определенный максимально возможный объем. Но создание массы контрактов с частичками реестра бессмысленно, их нужно как-то связать между собой.
Можно сделать блокчейн в блокчейне и попытаться связать записи друг с другом — так каждая запись будет иметь одну или две ссылки на подобные записи и будет представлять нечто вроде строки таблицы. Но в этом случае придется решать вопрос удаления и создания записей, который повлечет за собой изменение не только записи, над которой выполняется операция, но и других.
Создать центральный контракт и запоминать ссылки на записи тоже не подходит — даже массив ссылок на строки может быть условно бесконечным в рамках одного контракта.
Остается одно — нужно как-то связать записи, при этом не запоминая ссылки на них. Хорошо бы придумать путь, который позволит искать и проверять записи, а также их создавать и обеспечивать незаменимость, при этом не запоминая размещенные части.
Звучит как фантазия, но Free TON предлагает элегантное решение этой проблемы.

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

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

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

Данный подход возможен не только при изменении изначальных данных, сам код тоже можно изменять. В TIP-31 добавили “соль” кода, что позволяет, не меняя функциональность контракта, изменять его код, добавляя некоторую структуру данных прямо в него. Это полезно, если нужно разделить строки реестра в двух разных реестрах и при поиске контракта по хэшу кода.
Проблематика и безопасность
Но как решить проблему “фальшивых” контрактов, которые можно развернуть с такими же параметрами, не ссылаясь на родительский контракт?

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

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

________________________
Описанный подход позволяет посмотреть на блокчейн с непривычной стороны — как на базу данных с хранением формата [код контракта, исходные данные] -> адрес контракта. Это приоткрывает двери в мир распределенных реестров неограниченной масштабируемости, что, безусловно, приведет к созданию систем распределенного реестра нового поколения, чем и является True NFT.
Читайте “Настоящее NFT. Решение от Surf и RSquad”