- PVSM.RU - https://www.pvsm.ru -

Универсальный cмарт-контракт мультиподписи в Ethereum

Несколько дней назад мы в компании BitClave [1] прочли о недавнем инциденте с мультиподписными кошельками [2] компании Parity Technologies, решили пригляделся к коду их смарт-контракта. Свежий пост [3] в блоге компании Zeppelin Solutions детально описывает произошедший инцидент с технической стороны, поэтому мы хотели бы в нашей статье больше сфокусироваться на принципах проектирования смарт-контрактов.

Ethereum wallet

Есть несколько широко известных принципов ООП, входящих в аббревиатуру SOLID [4]:

Есть множество приверженцев и противников использования этих принципов, но мы просмотрели множество библиотек на языке Solidity и пришли к выводу, что подход Zeppelin Solutions является наиболее удобным и безопасным. Их библиотека OpenZeppelin.org [15] предоставляет множество небольших смарт-контрактов, которые могут быть скомпилированы для достижения более сложного поведения по паттерну примесей [16] (англ. mixin [17]) через использование множественного наследования в языке Solidity. Вам необходимо произвести декомпозицию обязанностей вашего финального смарт-контракта на множество смарт-контрактов с единственными ответственностями – каждый смарт-контракт должен служить единственной цели. Причем часть необходимых вам контрактов вы скорее всего обнаружите в библиотеках вроде той, что предлагает компания Zeppelin Solutions. Помимо всего прочего вы к тому же сможете протестировать каждый из контрактов по отдельности.

Руководствуясь этими принципами мы разработали смарт-контракты для проведения распродажи токенов: github.com/bitclave/crowdsale [18]. Вы можете заметить в репозитории смарт-контракты BonusCrowdsale и TokensCappedCrowdsale, которые были разработаны таким образом чтобы обрабатывать такие аспекты нашей распродажи, как обработку бонусов участников в зависимости от времени и суммы инвестиций, а также контролировать суммарное число продаваемых токенов. На наш код мы получили довольно хвалебный отзыв аудитора безопасности смарт-контрактов:

«Великолепная работа по повторному использованию существующих контрактов библиотек OpenZeppelin! Дополнительные контракты выглядят очень продуманно спроектированными и выглядят хорошим расширением этого фреймвока» («Great work reusing the existing OpenZeppelin libraries! The additional contracts are very thoughtfully designed, and are a good extension of the framework») — Zeppelin Solutions. С полным заключением можно ознакомиться по ссылке [19].

Для того чтобы успешно применять эти принципы на практике необходимо четко понимать как именно работает множественное наследование. На деле компилятор Solidity преобразует множественное наследование в одиночное наследование. Таки образом после компиляции у каждого смарт-контракта будет всего 1 родитель, обращаться к которому можно через ключевое слово super. Возможно следующий пример дополнительно поможет понять как именно работает линеаризация множественного наследования C3 [20]:

contract A { }
contract B { }
contract C is A, B { } // C(A,B) = ABC
contract D is C, A { } // D(C(A,B),A) = D(ABC,A) = ABCAD !!! Error !!!
contract E is A, C { } // E(A,C(A,B)) = E(A,ABC) = ABCE

Проблема в том, что смарт-контракт A не может переопределять C, потому что C переопределяет B, который в свою очередь переопределяет A:

TypeError: Linearization of inheritance graph impossible
contract D is C, A { }
^ — — — — — — — — — — ^
Compiliation failed. See above.

Так же необходимо учитывать, что любое непосредственное наследование контрактов может быть превано в процессе наследования дочерних классов. Обратите внимание, как в следующем примере компилируется контракт W, и его родительский контракт Z становится наследником контракта Y(который является наследником X), вместо непосредственного наследования от X:

contract X {}
contract Y is X {}    // Y(X) = XY
contract Z is X {}    // Z(X) = XZ
contract W is Y, Z {} // W(Y(X),Z(X)) = W(XY, XZ) = XYZW

Возвращаясь к смарт-контракту мультиподписного кошелька Parity Technologies мы ообратили внимание, что он совершенно не декомпозирован. Единственное архитектурное решение замеченное нами: вынос общего кода всех кошельков в единую библиотеку с целью уменьшить стоимость загрузки контракта. Кстати, именно эта особенность и позволила случайному разработчику нарушить работу всех кошельков, сломав единственную библиотеку с общим кодом. Мы поразмышляли на тему множественного владения ресурсом и подготовили своё решение этой задачи в манере библиотек OpenZeppelin. Мы рады представить вам
контракт Multiownable.sol [21], который позволит вам с легкостью добавить функциональность мультиподписи в любые ваши контракты. Его использование так же просто как и использование обычного контракта Ownable — нужно лишь отнаследоваться от него и добавить модификаторы onlyAnyOwner и onlyManyOwners к необходимым функциям:

contract SimplestMultiWallet is Multiownable {
    function transferTo(address to, uint256 amount) onlyManyOwners {
        to.transfer(amount);
    }
}

Метод transferTo смарт-контракта будет вызван только после того как все владельцы кошелька вызовут его с одинаковыми аргументами. Вы не поверите, но это полный код простейшего мультиподписного кошелька для валюты Эфир. Дополнительно можно реализовать поддержку любых токенов совместимых со спецификацией ERC20, вот такой метод:

function transferTokens(address token, address to, uint256 amount) onlyManyOwners {
    ERC20(token).transfer(to, amount);
}

Будем рабы любому фидбеку сообщества: github.com/bitclave/Multiownable [21]

Автор: Антон Буков

Источник [22]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/267981

Ссылки в тексте:

[1] BitClave: https://bitclave.com

[2] инциденте с мультиподписными кошельками: https://medium.com/@ParityTech/security-alert-1fe9d015f1ba

[3] Свежий пост: https://blog.zeppelinos.org/parity-wallet-hack-reloaded/

[4] SOLID: https://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29

[5] Single responsibility principle: https://en.wikipedia.org/wiki/Single_responsibility_principle

[6] принцип единственной ответственности: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%B9_%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8

[7] Open/closed principle: https://en.wikipedia.org/wiki/Open/closed_principle

[8] принцип открытости/закрытости: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8/%D0%B7%D0%B0%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8

[9] Liskov substitution principle: https://en.wikipedia.org/wiki/Liskov_substitution_principle

[10] принцип подстановки Барбары Лисков: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B8_%D0%91%D0%B0%D1%80%D0%B1%D0%B0%D1%80%D1%8B_%D0%9B%D0%B8%D1%81%D0%BA%D0%BE%D0%B2

[11] Interface segregation principle: https://en.wikipedia.org/wiki/Interface_segregation_principle

[12] принцип разделения интерфейса: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D0%B0

[13] Dependency inversion principle: https://en.wikipedia.org/wiki/Dependency_inversion_principle

[14] принцип инверсии зависимостей: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%B8%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8_%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B5%D0%B9

[15] OpenZeppelin.org: https://openzeppelin.org

[16] паттерну примесей: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%81%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)

[17] mixin: https://en.wikipedia.org/wiki/Mixin

[18] github.com/bitclave/crowdsale: http://github.com/bitclave/crowdsale

[19] ссылке: https://blog.zeppelin.solutions/bitclave-token-audit-570b0c664eb0

[20] линеаризация множественного наследования C3: http://solidity.readthedocs.io/en/develop/contracts.html#multiple-inheritance-and-linearization

[21] Multiownable.sol: https://github.com/bitclave/Multiownable

[22] Источник: https://habrahabr.ru/post/342100/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best