Solidity. Смарт контракты и аудит
2.62K subscribers
246 photos
7 videos
18 files
550 links
Обучение Solidity. Уроки, аудит, разбор кода и популярных сервисов
Download Telegram
Урок 15. Работа с памятью в Solidity

Еще один сложный для понимания урок, который я пересматривал несколько раз.

Для меня лично, проблемы возникли не только с пониманием организации памяти, но и с добавление языка Yul, который я раньше не встречал в своей практике.

Тем не менее, он оказался не таким уж и навороченным, если посидеть с ним и разобраться, что к чему.

Видео урок - работа с памятью

К данному уроку можно вернуться в любой момент, так как он просто рассказывает про внутреннюю работу языка Solidity.

И, на данный момент, я встречал в различных видео всего раза два, когда требовалось использовать assembly и вытаскивать из памяти селектор или другие значение. Уверен, это уже знания продвинутого разработчика Solidity.

Я в течение дня все также буду выкладывать скрины и пояснения кода и работы памяти.

#урок #memory #calldata
🔥1
Solidity резервирует 4 слота памяти, которые хранят в себе до 32 байтов, где первые два слота или 64 байта - это некое служебное пространство, оно уже использкется по умолчанию, и мы не будем обращаться к нему напрямую. Следующие 32 байта хранят в себе указатель на свободное место в памяти, который как бы говорит, что с этого момента ты можешь что-то записать в память. Потом идет пустой слот, после которого мы можем записывать свои данные.

Для того, чтобы обращаться к слотам памяти используют Yul (код с assemly в видео).

Чтобы найти значение строки, которое возвращается в bytes32, нужно найти указатель в памяти, а затем вычесть из него предполагаемый размер данных в байтах. Например, строка "test" точно поместится в 32 байта, поэтому код будет выглядеть как на скрине.

#accembly #memory #calldata #yul
🔥1
Массивы с фиксированной длиной хранятся немного иначе, чем строки. Перед указателем будет размещаться последнее значение массива. А чтобы найти другие значение необходимо вычитать по 32 байта. 

#accembly #memory #calldata #yul
🔥1
В случае с вызовом данных нашей функции, в первых 4 байтах находится селектор самой функции.

#accembly #memory #calldata #yul
🔥1
Основное различие calldata от memory заключается в том, что при использовании memory тратится больше газа, так как его можно перезаписывать.

Calldata же является неизменным значением. Другими словами, мы можем использовать calldata в своих функциях, но изменить его значение у нас не получится.

#accembly #memory #calldata #yul
🔥1
Чтобы получить селектор функции, можно использовать следующую функцию. Более того, данный селектор функции можно найти в artifacts/myContract.json в блоке methodIdentifiers.

#memory #calldata #yul
🔥1
Чтобы найти первый элемент массива с фиксированной длинной в calldata, нам необходимо пропустить первые 4 байта, которые содержат селектор функции.

#accembly #memory #calldata #yul
🔥1
Найти строку в calldata немного сложнее. Так как после селектора есть еще 32 байта, после которых начинается информация о строке. Потом еще слот с байтовой длинной самой строки, и только после этого значение строки.

#accembly #memory #calldata #yul
🔥1
Найти строку в calldata немного сложнее. Так как после селектора есть еще 32 байта, после которых начинается информация о строке. Потом еще слот с байтовой длинной самой строки, и только после этого значение строки.

#accembly #memory #calldata #yul
🔥1
Найти массив с динамической длинной в calldata можно так. Сначала в слотах хранится селектор, потом количество элементов в массиве или его длина, затем идет первый элемент нашего массива.

#accembly #memory #calldata #yul
🔥1
Сдвиги / смещение

Прикольную штуку сейчас в чате по Solidity встретил.

Задача

В общем, есть 4 переменных uint64 и нужно передать в функцию как одну uint256, а потом сдвигами их доставать.

Решение

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

И вот, как эту задачу можно решить:

Сначала объединяете все переменные в одну uint256, а потом достаете значение так:

uint64 a1 = uint64(a);
uint64 a2 = uint64(a >> 64);
uint64 a3 = uint64(a >> 128);
uint64 a4 = uint64(a >> 192);

Круто, да?

#uint #memory #сдвиг #смещение
👍1
Структура/хранение данных

Очередной прекрасный видео урок от Ильи на его Youtube канале.

Это видео подойдет скорее для продвинутых учеников Solidity, так как без практики будет немного сложно соотнести знания с теорией.

Видео урок.

Особо добавить или разобрать тут нечего. Рекомендую посмотреть на досуге.

#memory
👍2
Большая заметка по работе с памятью

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


Storage

1. Размер Storage контракта 2**256-1.

2. Слоты в памяти имеют размер в 32 байта. Если переменные меньше 32 байт, то они могут быть "упакованы". Т.е. если есть 4 переменные uint8, то они все будут занимать только один слот.

3. Динамические массивы хранятся следующим образом: сначала определяется слот, в котором массив был обозначен. Там хранится его длина (кол-во элементов), а значение элемента в массиве высчитывается через keccak256(p), где "р" - номер слота инициализации массива. Таким образом, первый элемент массива будет храниться в keccak256(p), второй в keccak256(p)+1, третий в keccak256(p)+2 и т.д.

4. Маппинга хранятся немного по другому. В главном слоте, где был объявлен маппинг, хранится пустое значение (все нули). А значение маппинга находится с помощью keccak256(p CONCAT key), где key - это ключ, к которому нужно найти само значение.

5. Enum, по описанию из комментария на stackexchange: Up to 255 values seems to take up 8 bits of storage, and 256 values seems to take up 16 bits of storage.


Memory

1. Максимальный размер Memory - 2 ** 64 байта.

2. Стоимость хранения считается linearly для первых 724 байтов, и Quadratically - после.

3. Структура памяти:

0x00 - 0x3f (64 bytes): зарезервированное место для хеширования;
0x40 - 0x5f (32 bytes): указатель на свободное место (free memory pointer);
0x60 - 0x7f (32 bytes): пустой слот, который также используется, как изначальное значение для динамических массивов;

Отсчет для хранения новых значений начинается с 128 байта.

4. Слоты всегда по 32 байта.

5. Для динамических массивов в первом слоте хранится его длина, а затем идут сами элементы.

6. Если добавляете значения в Memory через assembly, то также необходимо обновлять и free memory pointer, так как в этом случае он не обновляется автоматически.

7. Все не инициализированные значение будут указывать на слот 0х60 (кроме структур), в то время как строки, байти и динамические массивы будут указывать на следующий free memory location.


Calldata

1. Неизменяемое хранилище данных.

2. Не могут храниться и передаваться маппинги.

3. В отличие от Memory размер не лимитирован.

4. Размер всего calldata можно узнать с помощью опкода CALLDATASIZE.

5. Первые 4 байта всегда занимает селектор функции, который считается через bytes4(keccak256("funcName(args)")).

6. Для динамических массивов и строк сначала идет offset (32 байта), потом длина / размер (32 байта), потом сами значения.


Stack

1. Хранение по принципу LIFO - last in, first out.

2. В стеке доступны только верхние 16 слотов.

3. Базовые опкоды: PUSH, POP, SWAP и DUP.

4. Solidity, как язык, не работает на прямую со стеком.

5. Функции помеченные, как external, занимают два слота в стеке: адрес контракта и селектор вызываемой функции. В этом случае они zero-padded слева, а не справа, как это в других случаях.

6. Для того, чтобы обойти ошибку "stack too deep" можно использовать block scopes {}.


Code

1. Константы и immutable хранятся в байткоде контракта.

2. Если константы на определены по какой-либо причине, то они на попадают в байткод.

3. Immutable нельзя определять в try/catch в версии 0.8.20.

4. Аргументы для конструктора контракта добавляются в конец байтокода самого контракта.

P.S. Можете смело корректировать или добавлять пункты в комментариях.

#storage #memory #calldata #code #stack
👍11💯32🔥2
Работа с памятью в Solidity. Большой сборник

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

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


На русском языке:

1. Большая заметка канала по работе с памятью. Тут я собрал краткие сведения по storage, memory, stack и code для удобства.

2. Структура. Хранение данных. Видео с канала Ильи

3. Memory и calldata. Видео с канала Ильи

4. Динамические массивы и мэппинги в storage. Видео с канала Ильи


На английском языке:

5. Solidity Tutorial: All About Stack

6. Understanding Ethereum Smart Contract Storage

7. Solidity Tutorial: All About Calldata

8. Solidity Tutorial: All About Code

9. Solidity Tutorial: All About Memory

10. All About Solidity Data Locations — Storage

11. Solidity Tutorial: All About Data Locations

12. Guide To Advanced Calldata


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

Приятного обучения и хорошей недели!

#storage #memory #stack #code #calldata
👍10🐳3🔥1💯1