Привет всем из мира авось-программирования Solidity.
Как я уже писал раньше, при неограниченном использовании маппингов и динамических массивов в Solidity, есть весьма малая, но ненулевая вероятность того, что адреса данных, сохраненных в массивах и маппингах, пересекутся. При этом очевидно, логика контракта будет непредсказуемо нарушена.
Я задался целью по возможности, исследовать, какие сценарии использования маппингов и динамических массивов будут в некотором смысле, наиболее безопасны, то есть гарантировать отсутствие таких коллизий даже теоретически.
Динамические массивы
Вы можете перебрать все возможные сгенерированные адреса динамических массивов для первых нескольких слотов статических (то есть заданных в переменных контракта) данных. Это сделать очень просто. Каждый динамический массив (то есть область его данных) располагается по адресу, вычисленному какот адреса слота, где он располагается. То есть динамический массив, объявленный первой переменной, расположится по адресу
, второй переменной - по адресу
и так далее. Самая маленькая разница между такими адресами, вычисленными для первых
слотов, будет около
, (учитывая также разницу между нулевым адресом и самым маленьким адресом массива, и самым большим адресом массива и самым большим возможным адресом). Таким образом, поскольку элементы массивов располагаются по адресам, непосредственно следующим за вычисленным начальным адресом массива, вы можете совершенно безопасно располагать не менее
эдементов данных размером
слот в каждом таком массиве, плюс примерно столько же - в статической (начальной) области данных контракта, при условии, что больше никаких динамических данных в вашем контракте нет.
Маппинги
В отличие от динамических массивов, адрес каждого значения маппинга вычисляется отдельно, какгде
это слот маппинга,
это ключ маппинга (для простых типов), а
соединяет побайтное представление своих параметров.
Перебрать и сравнить все возможные сгенерированные адреса на достаточно большом количестве ключей маппингов затруднительно. Поэтому я решил симитировать работу динамических массивов и маппингов на сокращенном адресном пространстве.
Для этого нужно было имитировать сокращенную функциюи вычисление адресов значений массивов и значений маппингов, подобно тому, как это происходит в "настоящем" Solidity. Детали реализации исследования и проведенных измерений можно посмотреть в открытом доступе.
В исследовании, изучается полный набор адресов, сгенерированный из ключей заданной битовой ширины, в пространстве адресов сокращенной битовой ширины
где
.
Основные выводы
Все рассмотренные в исследовании случаи позволяют сделать следующий вывод: если вы используете ключ маппинга шириной в половину или больше ширины адресного пространства (при ширине адресного пространства "настоящей" EVM составляющейбит, это будет
бит), то перебирая все ключи, вы совершенно точно наткнетесь на коллизию. То есть использование например, адреса счета или контракта шириной
бит в качестве ключа маппинга, в этом смысле, безопасным точно не является.