Невзаимозаменяемые токены (NFT) стали популярным использованием технологии блокчейна, представляя уникальные цифровые активы. В основе реализации NFT лежит задача эффективного хранения и извлечения данных NFT в рамках ограничений модели хранения Ethereum. В этой статье рассмотрим технические тонкости хранения метаданных NFT (в частности, tokenURI и tokenId) в смарт-контрактах Ethereum с акцентом на хранилище смарт-контракта и важную роль хэш-функции keccak256.

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

Отличие хранения NFT от обычных токенов

Основное различие в хранении между NFT и взаимозаменяемыми токенами вытекает из их фундаментальной природы и способа отслеживания права собственности.

Для взаимозаменяемых токенов, таких как те, которые соответствуют стандарту ERC-20, модель хранения относительно проста. Первичная структура данных обычно представляет собой mapping, который связывает адреса с балансами. Это выглядит примерно так:

mapping(address => uint256) private _balances;

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

Напротив, хранение NFT, как видно из таких стандартов, как ERC-721, является более сложным из-за уникальной природы каждого токена. Типичный контракт NFT может включать несколько mapping:

mapping(uint256 => address) private _owners;

mapping(address => uint256) private _balances;

mapping(uint256 => string) private _tokenURIs;

Здесь каждый токен имеет уникальный идентификатор tokenId, и контракт отслеживает владельца каждого конкретного токена. Отображение _balances ведет подсчет того, сколько токенов принадлежит каждому адресу, аналогично взаимозаменяемым токенам, но это дополнительная информация, а не основная запись о владельце. Помимо mapping, который по адресу вычисляет баланс токенов, для NFT есть еще mapping, который по номеру токена вычисляет адрес владельца. Этот mapping не хранят идентификаторы токенов напрямую, а используют их в качестве ключей для доступа к адресам владельцев.

Mapping _tokenURIs является еще одним ключевым отличием. Он связывает каждый токен с метаданными, обычно с URI, указывающим на данные вне блокчейна, описывающие характеристики токена (металанные). tokenURI обычно хранятся в виде строки (string). Этот mapping подчеркивает индивидуальность каждого NFT. 

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

Масштабируемость — еще одна точка расхождения. Контракты взаимозаменяемых токенов обычно хорошо масштабируются с большим количеством токенов и пользователей, поскольку они хранят только ненулевые балансы. Однако контракты NFT масштабируются линейно с количеством токенов, поскольку для каждого токена требуются собственные слоты в хранилище для записи права собственности и метаданных.

Модель хранения Ethereum

Смарт-контракты Ethereum имеют доступ к нескольким типам хранения данных:

  • Хранилище (Storage): хранилище относится к долгосрочной записи данных в рамках контракта и хранится в блокчейне. Эти данные могут быть доступны и изменены функциями контракта. Каждый смарт-контракт, развернутый в Ethereum, имеет свое собственное хранилище, и изменение его является достаточно дорогостоящим.

  • Память (Memory): разработчики используют память для переменных и параметров, которые используются внутри функции. Эти типы переменных существуют только в течение жизненного цикла выполняемой функции. Когда функция завершает выполнение, переменные и параметры, хранящиеся в области памяти, стираются.

  • Стек (Stack): это временное хранилище, используемое виртуальной машиной Ethereum (EVM) для хранения данных во время выполнения функций контракта. Стек используется для хранения значений и промежуточных результатов во время вычислений.

Подробно о хранилище

Каждый смарт-контракт получает свою собственную область хранения, которая является постоянной областью памяти для чтения и записи. Контракты могут читать и писать только из своего собственного хранилища. Хранилище контракта разделено на 2²⁵⁶ слотов по 32 байта каждый. Слоты являются смежными и индексируются, начиная с 0 и заканчивая 2²⁵⁶. Все слоты инициализируются значением 0.

Память хранилища EVM доступна только через эти 32-байтовые слоты.

2²⁵⁶ слотов!

Из-за огромного размера хранилища контракта его можно считать виртуальным. Это означает, что если вы прочитаете случайный слот, он, скорее всего, будет пустым/неинициализированным. Чтение такого слота вернет значение 0. EVM на самом деле не хранит все эти нули, но отслеживает, какие слоты используются, а какие нет. Когда вы обращаетесь к неиспользуемому слоту, EVM знает об этом и вернет 0.

Распределение хранилища

  • Переменные фиксированного размера: переменные, такие как uint256, address и другие фиксированного размера, напрямую хранятся в слотах. Каждой переменной обычно выделяется один слот, если только это не оптимизировано Solidity (например, упаковка нескольких меньших переменных в один слот).

  • Переменные динамического размера: переменные, такие как массивы и mapping, обрабатываются по-разному. Для массивов длина хранится в назначенном слоте, а элементы хранятся, начиная с хеша keccak256 номера слота, назначенного для массива. Для mapping значения хранятся в местах, определяемых хешированием ключа с номером слота.

Каким образом mapping хранятся в смарт-контрактах?

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

Вот как это работает:

1. Каждой переменной (включая mapping) в контракте назначается слот хранения на основе порядка ее объявления.

2. Для mapping, когда вы обращаетесь к _tokenURIs[tokenId], Solidity вычисляет место хранения следующим образом:

location = keccak256(abi.encode(tokenId, uint256(storageSlot)))

Где storageSlot — это номер слота, назначенный mapping _tokenURIs при объявлении.  Они конкатенируются в функции abi.encode. Использование функции abi.encode может быть полезно для предотвращения коллизий в хеш-функциях.

3. Полученный 256-битный хэш становится фактическим адресом хранения для URI этого tokenId. Хеш можно перевести в десятичный вид, который и будет номером слота.

Модель хранения в смарт-контрактах
Модель хранения в смарт-контрактах

Использование keccak256 в распределении адреса хранения достаточно разумно. Хэшируя комбинацию ключа и номера слота хранилища, Ethereum создает детерминированное, но, случайное распределение данных по доступному пространству хранилища. Такой подход предотвращает легкое предсказание мест хранения и позволяет эффективно использовать огромное адресное пространство 2²⁵⁶, доступное для хранилища каждого контракта.

В заключении

Эффективное хранение данных NFT в смарт-контрактах Ethereum в значительной степени зависит от понимания модели хранения Ethereum и разумного использования mapping и хэш-функции keccak256. Используя эти инструменты, мы можем создавать контракты NFT, которые являются как экономичными, так и масштабируемыми, способными обрабатывать большие коллекции с минимальными издержками на хранение в блокчейне.

Этот текст будет частью небольшой серии постов по мотивам моей статьи “Что такое NFT на самом деле?” и будет детальнее раскрывать основные аспекты работы с NFT.

Комментарии (9)


  1. TerraV
    26.08.2024 11:47
    +1

    NFT классический пример мошенничества на хайпе. Даже покупка участка на луне более рациональна чем покупка NFT.


    1. axel2001 Автор
      26.08.2024 11:47

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


    1. palyaros02
      26.08.2024 11:47

      В чем мошенничество? Это просто технология, жёстко связывающая определенные данные, не важно, в общем-то, какие с определенным владельцем. Есть вполне успешные применения, например, крипто-dns. Вы покупаете домен, являющийся nft, один раз - и не платите регистратору ежегодно за продление, его не заблокируют, вы даже не зависите от всей мировой dns инфраструктуры. Если все классические dns-сервера мира выключатся - ваши ip продолжат резолвится, потому что это просто публичная запись в блокчейне типа "домен - ip (включая все C, AAAA и прочие записи), владелец: такой-то адрес". Жаль, что пока из браузеров из коробки без расширений такое поддерживают только Opera и Brave, но вскоре и остальные подтянутся.


      1. TerraV
        26.08.2024 11:47

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


        1. palyaros02
          26.08.2024 11:47

          Выкачать надо несколько байт. Чтобы что-то читать из блокчейна - не нужна его копия, нужен интернет и один запрос. Количество публичных нод (поставщиков) измеряется тысячами. Ситуация массового сбоя невозможна by design (разве что всепланетное отключение электричества, но тогда проблемы посерьёзнее будут). Вы полную чушь написали. Почему, согласно вашей логике, браузер Опера не весит полтора терабайта? Ему ж надо блокчейн скачать, да не один (вы утверждаете, что это должен сделать каждый клиент, аж курсивом выделили). Вашей экспертности не хватило даже чтобы понять мой комментарий, у вас хотя бы криптокошелек есть?

          Ещё раз поясню, в чем отличие от классического DNS.

          В классическом случае вы 1) ежегодно платите за домен, 2) доменом вы не владеете, а арендуете его, 3) аренду могут отозвать в любой момент по тысяче причин, 4) ваши данные раскрываются третьим сторонам без вашего ведома, 5) возможны сбои в масштабах страны или мира (недавно было в рунете), 6) изменение днс-записи занимает от пары часов до пары дней, 7) админ днс сервера (например, Ростелеком) может просто отказаться резолвить ваш запрос и выдать вам заглушку, 8) возможен днс-спуфинг

          В крипто-днс вы 1) платите один раз (причем меньше), домен можно хоть завещать, вы истинный и единственный владелец, 2) вы и только вы решаете, какие данные будете раскрывать, 3) система имеет 100% аптайм и отказоустойчивость, 4) не смотря на ваше утверждение вы НЕ ЗАВИСИТЕ от поставщика, потому что нет поставщика - есть просто запись в публичном реестре, если ближайшая нода к клиенту отключится - он просто обратится к другой. Сами ноды не могут не выдать данные или выдать не те, 5) обновление данных - время одной транзакции, в эфире пара минут, в солане пара секунд, 6) днс-спуфинг, цензура и т.п. невозможны, так как нет никакого третьего звена, резолвинг ip происходит не на сервере у провайдера, а у клиента в браузере.

          Единственный минус - за обновление данных придется заплатить несколько центов.

          Реально ничем не лучше?


          1. TerraV
            26.08.2024 11:47

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


            1. palyaros02
              26.08.2024 11:47

              Вы ведёте неконструктивный диалог и занимаетесь демагогией.

              Сначала вы назвали технологию мошенничеством. Я отметил, что это некорректно и привел пример применения этой технологии, который точно никак не назвать мошенническим.

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

              Я вас поправил и по пунктам написал, чем крипто-днс лучше. Вы полностью это проигнорировпли, и теперь пишете, что нодам нельзя доверять (упомянув торговлю страхом в предыдущем своем сообщении, что показательно). То есть доверять куче промежуточных звеньев, которые могут/ДОЛЖНЫ вас не обслужить - это ок, а доверять нодам блокчейна (автономно синхронизирующимся и штрафуемым за ложь) - не ок. И снова допустили ошибку/манипуляцию что нельзя проверить достоверность данных в блокчейне, не скачав его, зачем-то упомянув неизменяемость. Вы снова не правы, погуглите, что такое дерево Меркла. Да и просто послать запрос на 5/10/100 нод как будто решает проблему, не находите?

              Итого, вы не ответили, почему nft - мошенничество, проигнорировали кучу текста и дважды сменили тему, допустив противоречие в своих суждениях (про доверие), и добавив, что я в танке. И минус мне влепили в карму как вишенку на торт.

              Самое смешное, что я согласен с вашим изначальным тезисом - что покупка nft бессмысленна, и согласен с тем, что блокчейн лучше скачивать. Только как это все относится к вашему первому предложению?


              1. axel2001 Автор
                26.08.2024 11:47

                Не очень понимаю, когда говорят "покупка nft бесмысленна". Какого именно nft?

                Это тоже самое сказать, что покупка "токена" бесмысленна, без уточнения какого именно.


                1. palyaros02
                  26.08.2024 11:47
                  +1

                  Полностью согласен, выше привел пример nft, которые как раз есть смысл покупать ввиду очевидной утилитарной ценности. Забыл добавить, что поддержка во всех браузерах даже не то что бы и нужна, есть домены-обертки, открывающиеся где угодно, например .sol-domain.org для браузеров, не понимающих .sol.

                  Но, к сожалению, без уточнения контекста под nft чаще всего имеют ввиду именно токенизированную ссылку на картинку, а в покупке видят только инвестиционную ценность. Тут TerraV прав, смысла покупать такие nft практически нет, подавляющее большинство сминченных токенов не подорожали ни на йоту за время своего существования, многие сначала взлетели, потом упали, только считанные проценты принесли что-то своим владельцам. Ну и такие токены ничего кроме идеи в себе не несут, это первый шаг, proof-of-concept, придуманный за два дня на хакатоне.

                  У nft может быть куча применений, можно токенизировать (и уже есть подобные эксперименты) дипломы, сертификаты и прочие подтверждающие что-либо документы (nft без права повторной передачи), которые не нужно будет переводить, апостилировать, их нельзя будет подделать и проверка подлинности будет сильно упрощена; можно токенизировать права владения чем-либо (документы на квартиру, участок, машину и прочее), паспорта, электронные подписи и много чего еще, что будет дешевле и надежнее физических вариантов.

                  В игровой сфере тоже можно много куда применить nft, но геймерам не заходит ввиду массово развившейся криптофобии, и игровые компании часто даже не могут начать такие эксперименты - как только становится известно, что где-то будут nft-предметы, игру сразу "отменяют" толпой. Торговая площадка Steam, где берется огромная комиссия, есть запрет на торговлю предметами вовне, и продавцы предметов ими не владеют, несмотря на все эти недостатки, пользуется огромной популярностью. Ее можно легко перевести в вид NFT DEX, от чего вообще все будут только в выигрыше, включая владельца торговой площадки, но такое вряд ли случится до массового принятия концепции nft и криптовалют в целом.