Struct используется довольно часто в сложных контрактах, поэтому знать этот тип данных не только нужно, но и важно!
В struct мы можем объявлять специальные поля, как в примере на скрине. При этом struct может хранить другие сложные типы данных, типа массивов и mapping.
#struct #типыданных
В struct мы можем объявлять специальные поля, как в примере на скрине. При этом struct может хранить другие сложные типы данных, типа массивов и mapping.
#struct #типыданных
🔥1
Обратите внимание на урок с 21 минуты! Здесь рассказывают, как обращаться в функциях к mapping, который содержит struct и т.д. Сложный момент, особенно для новичков!
#struct #типыданных
#struct #типыданных
❤2🔥1
Уязвимость в struct
Не довелось мне поработать со struct достаточно хорошо, поэтому следующий баг был для меня в новинку.
Рассмотрим просто контракт:
contract NameRegistrar {
bool public unlocked = false; // registrar locked, no name updates
struct NameRecord {
bytes32 name;
address mappedAddress;
}
mapping(address => NameRecord) public registeredNameRecord;
mapping(bytes32 => address) public resolve;
function register(bytes32 _name, address _mappedAddress) public {
NameRecord newRecord;
newRecord.name = _name;
newRecord.mappedAddress = _mappedAddress;
resolve[_name] = _mappedAddress;
registeredNameRecord[msg.sender] = newRecord;
require(unlocked);
}
}
Ничего странного не замечаете? Ошибок каких? Вроде бы их нет. Но есть один недочет.
Тут в контракте newRecord не инициализируется. Поэтому, когда в последующих строках мы устанавливаем значения:
newRecord.name = _name;
newRecord.mappedAddress = _mappedAddress;
они указывают на 0 и 1 слот в памяти, что приводит к установке значений в unlocked. И если мы передадим нужное значение в _name, то можно установить его, как true.
Так можно взломать этот контракт.
Вернее было бы оформить запись в struct так:
NameRecord memory newRecord = NameRecord({name: _name, mappedAddress: _mappedAddress});
Лично я не знал, что в этом случае структура может указывать на слоты памяти переменных. Поэтому важно инициализировать правильно.
Компилятор может указать вам на этот недочет, а в некоторых случая и остановить компиляцию контракта. Будьте внимательны со struct.
#struct #security
Не довелось мне поработать со struct достаточно хорошо, поэтому следующий баг был для меня в новинку.
Рассмотрим просто контракт:
contract NameRegistrar {
bool public unlocked = false; // registrar locked, no name updates
struct NameRecord {
bytes32 name;
address mappedAddress;
}
mapping(address => NameRecord) public registeredNameRecord;
mapping(bytes32 => address) public resolve;
function register(bytes32 _name, address _mappedAddress) public {
NameRecord newRecord;
newRecord.name = _name;
newRecord.mappedAddress = _mappedAddress;
resolve[_name] = _mappedAddress;
registeredNameRecord[msg.sender] = newRecord;
require(unlocked);
}
}
Ничего странного не замечаете? Ошибок каких? Вроде бы их нет. Но есть один недочет.
Тут в контракте newRecord не инициализируется. Поэтому, когда в последующих строках мы устанавливаем значения:
newRecord.name = _name;
newRecord.mappedAddress = _mappedAddress;
они указывают на 0 и 1 слот в памяти, что приводит к установке значений в unlocked. И если мы передадим нужное значение в _name, то можно установить его, как true.
Так можно взломать этот контракт.
Вернее было бы оформить запись в struct так:
NameRecord memory newRecord = NameRecord({name: _name, mappedAddress: _mappedAddress});
Лично я не знал, что в этом случае структура может указывать на слоты памяти переменных. Поэтому важно инициализировать правильно.
Компилятор может указать вам на этот недочет, а в некоторых случая и остановить компиляцию контракта. Будьте внимательны со struct.
#struct #security
🤯2
Передача параметров в struct
На форуме увидел вопрос-ответ на тему передачи параметров для struct из одного контракта в другой. Сам еще не пробовал, но решил сохранить себе и поделиться тут.
Итак, у нас есть контракт и struct, например:
interface IExternalContract{
struct one {
address user;
unit amount;
}
function callOne(one[] calldata data) external;
}
и в него из другого контракта нужно передать информацию.
Сделать можно так:
В нужном контракте дублирует struct и передаем его аргументы с помощью abi.encodeWithSignature:
contract YourContract {
address public contractAddr;
constructor(address _contractAddr) {
contractAddr = _contractAddr;
}
function callOne(IExternalContract.one[] calldata data) public {
IExternalContract(contractAddr).callOne(data);
}
}
Кто-то уже имел дело с передачей инфы в struct? Как справлялись?
#struct
На форуме увидел вопрос-ответ на тему передачи параметров для struct из одного контракта в другой. Сам еще не пробовал, но решил сохранить себе и поделиться тут.
Итак, у нас есть контракт и struct, например:
interface IExternalContract{
struct one {
address user;
unit amount;
}
function callOne(one[] calldata data) external;
}
и в него из другого контракта нужно передать информацию.
Сделать можно так:
В нужном контракте дублирует struct и передаем его аргументы с помощью abi.encodeWithSignature:
contract YourContract {
address public contractAddr;
constructor(address _contractAddr) {
contractAddr = _contractAddr;
}
function callOne(IExternalContract.one[] calldata data) public {
IExternalContract(contractAddr).callOne(data);
}
}
Кто-то уже имел дело с передачей инфы в struct? Как справлялись?
#struct
Хэширование в EIP712
Все чаще встречаюсь с этим стандартом на просторах аудиторских отчетов, и стараюсь осмысливать его шаг за шагом.
Вообще EIP712 был создан для того, чтобы можно было подписывать сообщения, которые состоят не только из строк, но и более сложных параметров: например, struct.
И сегодня разберем, как шифруются структуры. Возьмем такой код:
struct Parent {
uint s;
Child[] children;
}
Child {
uint a;
uint b;
}
Сначала будет хешироваться каждый пункт в Child по отдельности, затем они соединяются (конкатенация) и еще раз хешируются. Из этого получается хеш структуры.
В самом конце, берется S и хеш структуры и высчитывается уже конечный хеш Parent.
#erc712 #struct
Все чаще встречаюсь с этим стандартом на просторах аудиторских отчетов, и стараюсь осмысливать его шаг за шагом.
Вообще EIP712 был создан для того, чтобы можно было подписывать сообщения, которые состоят не только из строк, но и более сложных параметров: например, struct.
И сегодня разберем, как шифруются структуры. Возьмем такой код:
struct Parent {
uint s;
Child[] children;
}
Child {
uint a;
uint b;
}
Сначала будет хешироваться каждый пункт в Child по отдельности, затем они соединяются (конкатенация) и еще раз хешируются. Из этого получается хеш структуры.
В самом конце, берется S и хеш структуры и высчитывается уже конечный хеш Parent.
#erc712 #struct
Storage Structs
Еще одна потрясающая статья от автора The File Pattern, в которой он разбирает вопрос использования структур для хранения данных при использовании прокси контрактов.
Статья мне приглянулась еще тем, что я сам участвовал в конкурсных аудитах Astaria и Drips. Тогда я хоть и понял, что данные берутся из определённого слота, но не понимал зачем это сделано.
В общем, тут рассказывается о том, что в обновляемых контрактах часто встречается проблема коллизии данных в слотах памяти, а также случайной обновление переменных.
Этот паттерн предлагает создавать структуру данных (struct) и помещать ее в слот "далеко" в памяти при помощи формулы EIP-1967, о которой писалось выше.
В статье приводятся примеры использовании в реальных контрактах, а также некоторые проблемы, которые могут возникнуть.
Прочитать статью будет полезно не только аудиторам, но и разработчикам, которые хотят повысить свои скиллы.
#storage #eip1967 #struct
Еще одна потрясающая статья от автора The File Pattern, в которой он разбирает вопрос использования структур для хранения данных при использовании прокси контрактов.
Статья мне приглянулась еще тем, что я сам участвовал в конкурсных аудитах Astaria и Drips. Тогда я хоть и понял, что данные берутся из определённого слота, но не понимал зачем это сделано.
В общем, тут рассказывается о том, что в обновляемых контрактах часто встречается проблема коллизии данных в слотах памяти, а также случайной обновление переменных.
Этот паттерн предлагает создавать структуру данных (struct) и помещать ее в слот "далеко" в памяти при помощи формулы EIP-1967, о которой писалось выше.
В статье приводятся примеры использовании в реальных контрактах, а также некоторые проблемы, которые могут возникнуть.
Прочитать статью будет полезно не только аудиторам, но и разработчикам, которые хотят повысить свои скиллы.
#storage #eip1967 #struct
❤2👍1
Пример упаковки структур в маппинге
В отчетах можно часто найти информационный пункт о том, что переменные в структурах следует правильно паковать. В этом примере автор решил пойти дальше и еще больше оптимизировать хранение структуру в контракте.
С помощью двух функций, упаковки и распаковки, и побитовых операций переменные в структуре размещаются в uint256, который в свою очередь сохраняется в маппинге с ключом адресата.
Не уверен, что с практической точки зрения это может быть часто применимо, однако вполне вероятно такой способ упаковки можно будет встретить в проектах при аудите.
#mapping #struct
В отчетах можно часто найти информационный пункт о том, что переменные в структурах следует правильно паковать. В этом примере автор решил пойти дальше и еще больше оптимизировать хранение структуру в контракте.
С помощью двух функций, упаковки и распаковки, и побитовых операций переменные в структуре размещаются в uint256, который в свою очередь сохраняется в маппинге с ключом адресата.
Не уверен, что с практической точки зрения это может быть часто применимо, однако вполне вероятно такой способ упаковки можно будет встретить в проектах при аудите.
#mapping #struct
❤2👍1