February 20, 2022

Ограничение доступа (restricted access)

Друзья, привет!

Продолжим обсуждение функций (осталось совсем не много). Поговорим кратко об ограничении доступа (restricted access).

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

Для этих целей на основе паттерна Guard Check реализован механизм ограничения доступа (restricted access). То есть, после вызова функции, этот паттерн проверяет необходимые условия доступа и выбрасывает исключения (throw an exception), если они не выполняются.

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

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

Примеры модификаторов доступа:

modifier onlyBy(address _account) {
  require(msg.sender == _account);
  _;
modifier onlyAfter(uint _time) {
  require(now >= _time)
  _;
}
modifier costs(uint _amount) {
  require(msg.value >= _amount);
  _;
  if (msg.value > _amount) {
    msg.sender.transfer(msg.value - _amount);
  }
}

Пример использования:

function changeOwner(address _newOwner) public onlyBy(owner) {
  owner = _newOwner;
}
function buyContract() public payable 
                       onlyAfter(lastOwnerChange + 4 weeks) 
                       costs(1ether) {
  owner = msg.sender;
  lastOwnerChange = now;
}

После проверки условия в require, выполнение кода возвращается в исходную функцию (откуда был вызван модификатор), на это указывает символ подчеркивания (_;).

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

Выводы:

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

Ярким примером реализации модификаторов в функционирующем контракте вы можете найти в контракте DApp CryptoKitties

Источники:

  1. Один
  2. Два