ОРИГИНАЛЬНАЯ СТАТЬЯ ЧАСТЬ 2 ЧАСТЬ 3
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
Шекелей пачку Jolah Molivski
Поздравляем! Вы только что развернули свой первый смарт-контракт в основной сети, и запуск вашей платформы неизбежен. Когда вы расшнуровываете свои кроссовки Air Jordan после выпускной вечеринки, в глубине вашего сознания крутится непрекращающаяся мысль.
Работая в этой сфере, я заметил проблему со многими проектами в том, как они управляют правами собственности и транзакциями своих контрактов. Это первая часть серии статей о защите развертываний смарт-контрактов с использованием лучших отраслевых практик. В этой статье основное внимание будет уделено использованию мультиподписей и тому, почему они важны для безопасности и надежности проекта.
Как хороший пресвитерианин, я представлю 3 ключевых риска, которые помогает решить мультиподпись.
А multisig— это кошелек смарт-контрактов, развернутый в блокчейне, который может владеть другими контрактами и выполнять функции с повышенным доступом в качестве их владельца. Как следует из названия, это требует координации других учетных записей, дающих зеленый свет, или signing прежде чем он выполнит данную транзакцию. Мультиподписи использовались уже некоторое время и сами были объектом атак, особенно в Parity , которая была взломана несколько раз на миллионы. Сегодня на рынке есть несколько поставщиков мультиподписей, но выдающимся является Gnosis Safe.
Допустим, вы разрабатываете проект NFT, написали все свои тесты и чувствуете, что код безопасен и готов к отправке. Вы развертываете код с помощью ваших любимых средств (hardhat, трюфеля и т. д.), и теперь вы готовы.
Лучшие кодеры в мире ошибаются. Вдобавок ко всему, все контракты являются общедоступными, поэтому многие смотрят на их код. Некоторые зрители любопытны, хотят инвестировать или злонамеренны. В случае злонамеренного просмотра они будут пытаться использовать ваш контракт, чтобы извлечь выгоду или просто сломать ваш проект. Если ваш контракт получает средства, как в случае с большинством проектов NFT или defi, вы представляете потенциальному злоумышленнику приманку. Будь то организованная атака на оракула и флеш-кредит или простой взлом с повторным входом, нет смысла оставлять большую сумму средств на контракте с большой площадью поверхности для атаки.
Multisig может обеспечить безопасное убежище для ваших средств в сети, поскольку он построен с учетом этого. Многие пользовательские смарт-контракты, которые мы пишем, созданы для поддержки приложения или функции, а не для обеспечения безопасности. В этом нет ничего плохого, но чем сложнее смарт-контракт, тем больше увеличивается поверхность его атаки. Как владелец приложения или контракта NFT, мультиподпись может снимать лишние средства и хранить их в безопасности до тех пор, пока они не потребуются для развертывания.
Не все смотрят на базовый код контракта, есть довольно много псевдобезопасных лиц, которые могут подорвать ваш проект еще до того, как он будет запущен. Если один из этих "экспертов" смотрит на ваш контракт и замечает, что он принадлежит не мультиподписи, а адресу, который имеет историю покупок Shiba и случайных транзакций, они вас обманут. Это не личное, это то, как они получают помолвку. И, честно говоря, вам лучше знать.
Вот еще несколько советов, которые помогут справиться с репутационным риском при заключении публичного контракта.
Gnosis Safe , пожалуй, самый популярный и безопасный мультиподпись в кошельке. Насколько мне известно, он не был взломан, и это то, что я использую. Перейдите на https://gnosis-safe.io/app/welcome , чтобы начать. Затем подключите свой кошелек MetaMask к Rinkebyи подключите адрес, который вы хотите использовать. Обратите внимание, что вам понадобится тестовый eth Rinkeby для развертывания сейфа в цепочке — в конце концов, это смарт-контракт.
Если вам нужно немного Rinkeby eth, это лучшее место, которое я нашел за последнее время — https://rinkebyfaucet.com/
После того, как вы подключили eth и метамаску, убедитесь, что ваш кошелек подключен И вы выбрали правильную сеть в списке сетей в правом верхнем углу.
Теперь, когда Gnosis знает, в какой сети развернуть сейф и кто собирается его развернуть, выберите Create new -> Safe на странице приветствия.
Вы должны иметь возможность заглянуть в левую панель навигации и увидеть, что ваш развернутый gnosis в безопасности, но иногда кэширование или что-то еще может быть проблемой с gnosis. ЭТА статья должна помочь вам, если вы находитесь в этой лодке.
Итак, у вас есть мультиподпись, что теперь? Что ты можешь сделать с этим? Ну, одна вещь, которую вы можете сделать с ним из коробки, — это хранить в нем свои активы — так же, как и с любым другим активом. Единственное, что теперь у вас есть возможность, чтобы несколько человек высказались о том, является ли перевод ИЗ кошелька действительным, и вы не рискуете, что один скомпрометированный аккаунт истощит ваши личные или корпоративные средства. Однако для того, чтобы это работало, нам нужно добавить еще одну подписывающую сторону в мультиподпись, потому что мы добавили только одну подписывающую сторону при ее настройке.
Прежде чем мы перейдем к добавлению еще одной учетной записи для подписи, я хотел бы сделать резервную копию и порекомендовать вместо простой подписи учетной записи Metamask интегрировать использование аппаратного ХОЛОДНОГО!!!! кошеля.
Я хочу, чтобы вы поняли, насколько важна роль подписанта в этой мультиподписи. Мало того, что каждый подписывающий на вашем мультиподписном устройстве должен иметь аппаратный кошелек, они также должны соблюдать гигиену безопасности, которая гарантирует, что их исходная фраза, поддерживающая устройство, не будет потеряна или украдена. Эта seed-фраза гораздо важнее самого устройства, так как позволяет восстановить любой аппаратный или программный кошелек и подписать транзакции. Вот несколько хороших советов, которые, если их использовать в правильной комбинации, помогут вам хорошо спать по ночам. Убедитесь, что каждый член команды придерживается этого стандарта.
Как только вы убедитесь, что ваш со-подписавший защитил свой адрес для подписи, вы можете добавить его в мультиподпись. Для этого выберите только что созданную мультиподпись, прокрутите вниз и выберите Settings, прокрутите вниз до Owners и выберите его. Вы должны увидеть что-то вроде этого.
Выбирать, Add new owner:
Обратите внимание, как я добавил mark.cipherz.eth? Это пример поддомена ENS (служба имен Ethereum), и, вероятно, было бы неплохо назначить поддомены подписавшим, если они являются частью вашей организации. Мы рассмотрим ENS и поддомены чуть ниже. На данный момент не беспокойтесь об этом и называйте своего второго подписанта как хотите.
Нажмите Submit и подпишите последующую транзакцию Metamask. Обратите внимание, что вы должны быть подключены к той же учетной записи, с которой вы развернули мультиподпись, поскольку вы изменяете контракт в сети, чтобы включить подписывающую сторону и изменить требуемый минимум подписей. Когда это будет сделано, вы должны увидеть что-то вроде этого.
Если вы зайдете в Owners снова вы увидите добавленную новую подписывающую сторону.
Итак, теперь у нас есть функциональная мультиподпись на Ринкеби с несколькими подписчиками. Теперь мы проведем простой тест, используя его для безопасного хранения некоторого количества Rinkeby eth и передачи его на адрес по нашему выбору. Это касается нашего Asset Riskо которых мы говорили выше. Для начала давайте переведем немного Rinkeby eth на мультиподписной адрес и убедимся, что он отображается на нашем балансе. Чтобы получить развернутый адрес, нажмите кнопку copy, значок рядом с контрактом на левой панели.
Теперь, когда у вас есть мультиподписной адрес, переведите на него немного Rinkeby eth через финансируемый аккаунт. т.е. исходную учетную запись развертывания, если хотите.
Обратите внимание, что скопированный адрес будет иметь rink: префикс к нему — это хорошая мера безопасности, заставляющая вас перепроверить адрес.
Теперь вы должны увидеть баланс в мультиподписи. В этом случае я перевел 0,5 eth, как показано ниже.
Теперь момент, которого вы так долго ждали — выполнение транзакции, требующей нескольких подписантов…
Перейдите к Assets -> Coins и нажмите на Send, которая появляется при наведении курсора на баланс eth
Заполните информацию о переводе — вы можете отправить ее кому угодно или чему угодно, но я отправляю ее обратно себе, как показано ниже.
Нажмите «Обзор» и просмотрите сводку транзакций — если все в порядке, нажмите Submitи подпишите транзакцию метамаской. Не забудьте также подтвердить свой аппаратный кошелек. Вы должны увидеть экран, подобный приведенному ниже, который указывает, что 1 из требуемых 2 подписал транзакцию.
Затем вы можете скопировать текущий URL-адрес в браузере и отправить его человеку, который должен его подписать. Он увидит аналогичную страницу и сможет подтвердить транзакцию (обратите внимание, что вы уже подписали и эта опция сейчас неактивна)
Последний подписывающий будет иметь возможность выполнить транзакцию, но если он скряга, он может отложить выполнение другого подписывающего лица.
После того, как они отправили заявку и одна подписывающая сторона выполнила (оплатила) транзакцию — все готово. Посмотрите сводку выполненных транзакций ниже и проверьте свой баланс, чтобы увидеть, что eth был отправлен.
Ну и для чего всё это нужно?
Цель этой статьи — показать вам как разработчику, как создать реальный смарт-контракт, который имеет надлежащие меры безопасности и принадлежит контракту с несколькими подписями. Мы создадим смарт-контракт, обладающий следующими свойствами:
Как только у вас есть node:
Теперь, когда у вас настроен начальный проект, давайте запустим несколько команд и убедимся, что все работает.
Вы можете задаться вопросом, на какой цепочке блоков это работает — в конце концов, мы не указали никаких целевых цепочек блоков. Hardhat запускает автономный временный узел для выполнения этих команд за кулисами. Пока нам этого будет достаточно, но вы можете отправить его в определенную сеть через параметр --network, определенной в hardhat.config.ts файле. Это выходит за рамки данной статьи, но хорошо документировано.
Контракт представляет собой простую блокировку токена, которую владелец контракта может отправить eth и разблокировать его через указанное время.
Если мы посмотрим на test/Test.tsмы должны увидеть правильное его использование.
Это хорошее начало, но мы хотим, чтобы владелец Lock.sol отправил контракт на мультиподпись. Если вы заметили, deployer контракта устанавливается в качестве owner и нет возможности его изменить. Мы немного изменим этот контракт, чтобы дать нам больше гибкости в настройке access control к определенным функциям и дают нам возможность сменить владельца с deployer к multisig.
OpenZeppelin начинался как библиотека с открытым исходным кодом общих шаблонов и контрактов solidity и является бесценным инструментом. Мы собираемся использовать их контракт AccessControl, чтобы расширить наш и придать ему функциональность, которая нам нужна.
Теперь мы можем добавить AccessControl Lock.sol в виде зависимости следующим образом:
Вы заметите несколько добавлений/удалений в этом контракте, так что давайте рассмотрим их.
Если вы очистите и снова запустите тест, вы увидите ошибку, связанную с возвращенным сообщением об ошибке.
Замените неудачный тест на:
Вам также придется заменить другой тест, так как переменная owner больше не существует. Заменить Should set correct ownerтест, чтобы быть:
it.only("Should set the right owner", async function () {
const { lock, owner } = await loadFixture(deployOneYearLockFixture);
expect(await lock.hasRole(await lock.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.true;
});
Теперь ваши тесты должны пройти
Теперь мы готовы создать скрипт для смены владельца нашего контракта с deployerк multisigдоговор. Пристегнитесь…
До сих пор мы использовали локальный узел hardhat для запуска наших развертываний и тестов, просто чтобы убедиться, что все работает согласованно.
Теперь мы воспользуемся тестовой сетью для развертывания нашего контракта и создадим собственный сценарий жесткого диска, чтобы назначить право собственности на контракт нашей мультиподписи, которую мы создали в первой части .
Вам нужно будет создать конечную точку rpc, которую ваше приложение будет использовать для взаимодействия с тестовой сетью. Я бы предложил использовать инфуру или алхимию для создания узла rpc, указывающего на ethereum сеть и rinkeby тестовая сеть. Вы можете использовать ЭТОТ сборщик, чтобы получить тестовый эфир для своих транзакций.
Ниже представлен обновленный hardhat.config.ts которые вы должны использовать для своего проекта. Мы добавили несколько вещей
Поскольку это статья о безопасности, вероятно, лучше показать вам, как хранить секреты вне вашего кода, а не помещать их непосредственно в конфигурацию. Вы будете удивлены, узнав, сколько разработчиков невольно передают свои секреты в общедоступный репозиторий…
Создать .env файл в корне проекта со следующими значениями
Обратите внимание, что в нашем .gitignore файле мы исключаем все файлы с именем .env так что это никогда не будет зафиксировано в нашем репо.
Теперь, когда у нас все настроено, давайте продолжим и развернем наш контракт на rinkeby. На самом деле мы собираемся изменить наш deploy.tsscript немного и уменьшите заблокированное количество эфира с 1 до 0,01. Трудно найти тестовую сеть eth, поэтому давайте не будем блокировать целую сеть eth. Кроме того, мы собираемся сократить время блокировки до 5 минут, чтобы мы могли увидеть фактическое успешное withdrawal вместо того, чтобы ждать год.
Изменить строку 5 в scripts/deploy.ts:
Изменить строку 8 в scripts/deploy.ts:
Давайте продолжим и развернем контракт, используя команду ниже
Сценарий должен вернуться с развернутым адресом контракта, как показано ниже.
Lock with .01 ETH deployed to: 0x73f701a4a56AA14513a2A108624b4D00f90D27b2
Теперь давайте возьмем мультиподписной контракт, который мы развернули с помощью Gnosis для Rinkeby выше. Мы будем использовать этот адрес и адрес развернутого контракта для следующего шага.
Передача права собственности на контракт Multisig
Далее мы запустим пользовательскую задачу, которую мы создали, чтобы передать право собственности на контракт с учетной записи развертывателя на мультиподпись на rinkeby.
Вы должны увидеть вывод, похожий на следующий
Теперь, когда наш контракт развернут и право собственности передано нашей мультиподписи, давайте продолжим и настроим наше OpenZeppelin Defender , чтобы упростить подписи для нашей Gnosis .
Давайте создадим учетную запись на https://defender.openzeppelin.com/ , чтобы использовать ее для управления действиями по нашему развернутому контракту. Защитник — это полезный инструмент для согласования предложений и подписей с нашими развернутыми контрактами — он может многое сделать, но в основном мы собираемся использовать его для управления нашими действиями с несколькими подписями.
Добавить мультиподпись Gnosis
Создав учетную запись OpenZeppelin, нажмите кнопку Add Contract кнопку в правом верхнем углу, чтобы добавить наш мультиподписной контракт.
Введите имя, сеть и адрес контракта, которые вы можете получить в приложении Gnosis. Это позволит импортировать контракт в OpenZeppelin Defender, чтобы мы могли использовать его для подписи.
Прокрутите вниз и нажмите Addи у нас все хорошо. Теперь вы должны увидеть, что Defender распознал наш контракт как мультиподписной и отображает его, как показано ниже.
Нажмите Add Contractеще раз и добавить Lock контракт, который мы развернули в Ринкеби. Повторение - мать учения — просто дайте ему имя, сеть и адрес. Однако, поскольку этот контракт не проверен на Ринкеби, нам нужно вставить abi для добавления контракта. Вы можете найти abi в файле artifacts/contracts/Lock.sol/Lock.json. Найди abi атрибут и скопируйте/вставьте все значение в раздел Defender abi. Вы должны быть в состоянии щелкнуть Add чтобы завершить добавление вашего контракта. Теперь вы должны увидеть оба контракта на панели инструментов, как показано ниже.
Сейчас все настроено, и теперь мы можем создать предложение по нашему контракту и выполнить его через несколько подписантов через мультиподпись.
В нашем последнем рывке мы будем делать следующее
Перейдите к Dashboard => Contracts => Lock => New Proposal => Admin Action в Defendor'е. Выберите withdraw и Multisigс, которую вы импортировали. Введите аннотацию для заголовка и описания и нажмите Create Admin Action. Обратите внимание, что любой может создать предложение, но авторизовать предложение могут только действительные подписывающие лица. После заполнения форма должна выглядеть примерно так, как показано ниже.
Теперь, когда наше предложение создано, мы можем подписать его с помощью учетных записей владельцев, которые мы назначили нашей мультиподписи Gnosis. Перейдите к мультиподписи Gnosis и проверьте, какие подписанты авторизованы. Это должно выглядеть примерно так.
Войдите в одну из перечисленных здесь учетных записей с помощью Metamask и снова перейдите на страницу предложения в Defendor'е. Теперь вы должны увидеть Approval. Нажмите эту кнопку, чтобы одобрить предложение, как показано ниже, и подпишите свой метамаск.
Затем Defendor сообщит вам, что у вас есть одно одобрение и сколько осталось, как показано ниже. В этом случае у нас есть еще одно одобрение.
На этом этапе вы можете подключить другую учетную запись Metamask и либо Approve and Executeили просто Approve. Если вы просто одобрите предложение, один из подписантов все равно должен будет его выполнить. Иногда, если это сделка с большим объемом газа, ее должен выполнить один из более богатых владельцев. Как только все утверждающие лица подпишут транзакцию и она будет выполнена, транзакция будет отправлена в блокчейн для проверки.
Теперь, когда наша мультиподписная транзакция обработана, давайте проверим, что все работает как положено. Мы должны увидеть депозит в размере 0,01 ETH от Lock контракта с нашей мультиподписью, поэтому давайте проверим это, взглянув на нашу мультиподпись Gnosis. Как вы можете видеть ниже, withdraw функция была выполнена, и если мы посмотрим на транзакцию на etherscan, мы увидим перевод 0,01 ETH.
Успех! Поздравляем с выполнением вашей первой транзакции по контракту с мультиподписью.
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
Шекелей пачку Jolah Molivski
Поздравляем! Вы только что развернули свой первый смарт-контракт в основной сети, и запуск вашей платформы неизбежен. Когда вы расшнуровываете свои кроссовки Air Jordan после выпускной вечеринки, в глубине вашего сознания крутится непрекращающаяся мысль.
Проблемка
Есть два основных аспекта безопасности смарт-контрактов. Первый и, вероятно, самый важный — это код и его соответствие стандартам безопасного кодирования, которые были мучительно изучены в результате многочисленных эксплойтов за эти годы. Другим ключевым аспектом является владение , которому будет посвящена эта статья.Работая в этой сфере, я заметил проблему со многими проектами в том, как они управляют правами собственности и транзакциями своих контрактов. Это первая часть серии статей о защите развертываний смарт-контрактов с использованием лучших отраслевых практик. В этой статье основное внимание будет уделено использованию мультиподписей и тому, почему они важны для безопасности и надежности проекта.
Как хороший пресвитерианин, я представлю 3 ключевых риска, которые помогает решить мультиподпись.
- Риск владения — риск того, что ваша организация потеряет доступ к контракту.
- Риск активов — риск того, что ваш контракт ненадежен, и активы, принадлежащие контракту, могут быть взяты.
- Репутационный риск — риск того, что ваш проект будет рассматриваться в негативном свете.
Что такое мультиподпись
А multisig— это кошелек смарт-контрактов, развернутый в блокчейне, который может владеть другими контрактами и выполнять функции с повышенным доступом в качестве их владельца. Как следует из названия, это требует координации других учетных записей, дающих зеленый свет, или signing прежде чем он выполнит данную транзакцию. Мультиподписи использовались уже некоторое время и сами были объектом атак, особенно в Parity , которая была взломана несколько раз на миллионы. Сегодня на рынке есть несколько поставщиков мультиподписей, но выдающимся является Gnosis Safe.Риск собственности
Допустим, вы разрабатываете проект NFT, написали все свои тесты и чувствуете, что код безопасен и готов к отправке. Вы развертываете код с помощью ваших любимых средств (hardhat, трюфеля и т. д.), и теперь вы готовы.- Что произойдет, если вы или кто-то другой случайно зафиксирует/отправит закрытый ключ развертывания в ваш репозиторий? Это произошло много раз — больше, чем вы думаете.
- Вы развертываете контракт с учетной записью развертывания и передаете право собственности на бухгалтерскую книгу, принадлежащую компании. Что, если этот человек уйдет, потеряет свою бухгалтерскую книгу или, что еще хуже, seed - фразу?
Денежный риск
Лучшие кодеры в мире ошибаются. Вдобавок ко всему, все контракты являются общедоступными, поэтому многие смотрят на их код. Некоторые зрители любопытны, хотят инвестировать или злонамеренны. В случае злонамеренного просмотра они будут пытаться использовать ваш контракт, чтобы извлечь выгоду или просто сломать ваш проект. Если ваш контракт получает средства, как в случае с большинством проектов NFT или defi, вы представляете потенциальному злоумышленнику приманку. Будь то организованная атака на оракула и флеш-кредит или простой взлом с повторным входом, нет смысла оставлять большую сумму средств на контракте с большой площадью поверхности для атаки.Multisig может обеспечить безопасное убежище для ваших средств в сети, поскольку он построен с учетом этого. Многие пользовательские смарт-контракты, которые мы пишем, созданы для поддержки приложения или функции, а не для обеспечения безопасности. В этом нет ничего плохого, но чем сложнее смарт-контракт, тем больше увеличивается поверхность его атаки. Как владелец приложения или контракта NFT, мультиподпись может снимать лишние средства и хранить их в безопасности до тех пор, пока они не потребуются для развертывания.
Репутационный риск
Не все смотрят на базовый код контракта, есть довольно много псевдобезопасных лиц, которые могут подорвать ваш проект еще до того, как он будет запущен. Если один из этих "экспертов" смотрит на ваш контракт и замечает, что он принадлежит не мультиподписи, а адресу, который имеет историю покупок Shiba и случайных транзакций, они вас обманут. Это не личное, это то, как они получают помолвку. И, честно говоря, вам лучше знать.Вот еще несколько советов, которые помогут справиться с репутационным риском при заключении публичного контракта.
- Используйте мультиподпись
- Используйте ENS для сопоставления мультиподписи с вашей организацией, т.е. Coolnfts.eth
- Добавьте по крайней мере 3 подписавших в свою мультиподпись, при этом для принятия предложения требуется большинство голосов.
- Используйте поддомены ENS для подписантов вашей организации, т.е. Bill.coolnfts.eth
- Привлекайте доверенных сторонних подписывающих сторон — некоторые люди справедливо не доверяют мультиподписи только внутренним подписавшим сторонам компании.
Gnosis Safe , пожалуй, самый популярный и безопасный мультиподпись в кошельке. Насколько мне известно, он не был взломан, и это то, что я использую. Перейдите на https://gnosis-safe.io/app/welcome , чтобы начать. Затем подключите свой кошелек MetaMask к Rinkebyи подключите адрес, который вы хотите использовать. Обратите внимание, что вам понадобится тестовый eth Rinkeby для развертывания сейфа в цепочке — в конце концов, это смарт-контракт.
Если вам нужно немного Rinkeby eth, это лучшее место, которое я нашел за последнее время — https://rinkebyfaucet.com/
После того, как вы подключили eth и метамаску, убедитесь, что ваш кошелек подключен И вы выбрали правильную сеть в списке сетей в правом верхнем углу.
Теперь, когда Gnosis знает, в какой сети развернуть сейф и кто собирается его развернуть, выберите Create new -> Safe на странице приветствия.
- Подтвердите сеть как Rinkeby
- Назовите свой сейф — это не входит в блокчейн. Это просто удобство для вас
- Владельцы и подтверждения — Здесь вы можете добавить владельцев вашего сейфа и необходимое соотношение подписей для выполнения транзакции. Мы просто будем использовать текущую подключенную учетную запись web3 в качестве владельца и установим необходимые подтверждения равными 1, поскольку на данный момент мы являемся единственным владельцем. Вы также можете добавить более удобную ассоциацию имени для своей учетной записи владельца, если это поможет вам.
- После того, как вы сделаете все вышеперечисленное, вам будет представлена сводка и возможность развернуть сейф в блокчейне. Это должно выглядеть примерно так.
- Нажмите Create и он должен открыть Metamask для подписи и оплаты развертывания. Как только вы подтвердите транзакцию и она будет добыта, у вас должен появиться совершенно новый Gnosis Safe. После завершения развертывания убедитесь, что вы нажали Get Started чтобы загрузить сейф в пользовательский интерфейс. Затраты довольно минимальны, и я почти уверен, что Gnosis использует Minimal Proxy дизайн — у меня есть статья об этом ЗДЕСЬ , если вам интересно.
Вы должны иметь возможность заглянуть в левую панель навигации и увидеть, что ваш развернутый gnosis в безопасности, но иногда кэширование или что-то еще может быть проблемой с gnosis. ЭТА статья должна помочь вам, если вы находитесь в этой лодке.
Круто… Что теперь?
Итак, у вас есть мультиподпись, что теперь? Что ты можешь сделать с этим? Ну, одна вещь, которую вы можете сделать с ним из коробки, — это хранить в нем свои активы — так же, как и с любым другим активом. Единственное, что теперь у вас есть возможность, чтобы несколько человек высказались о том, является ли перевод ИЗ кошелька действительным, и вы не рискуете, что один скомпрометированный аккаунт истощит ваши личные или корпоративные средства. Однако для того, чтобы это работало, нам нужно добавить еще одну подписывающую сторону в мультиподпись, потому что мы добавили только одну подписывающую сторону при ее настройке.Добавление еще одного подписывающего лица
Аппаратный кошелек
Прежде чем мы перейдем к добавлению еще одной учетной записи для подписи, я хотел бы сделать резервную копию и порекомендовать вместо простой подписи учетной записи Metamask интегрировать использование аппаратного ХОЛОДНОГО!!!! кошеля.Хранение seed - фазы
Я хочу, чтобы вы поняли, насколько важна роль подписанта в этой мультиподписи. Мало того, что каждый подписывающий на вашем мультиподписном устройстве должен иметь аппаратный кошелек, они также должны соблюдать гигиену безопасности, которая гарантирует, что их исходная фраза, поддерживающая устройство, не будет потеряна или украдена. Эта seed-фраза гораздо важнее самого устройства, так как позволяет восстановить любой аппаратный или программный кошелек и подписать транзакции. Вот несколько хороших советов, которые, если их использовать в правильной комбинации, помогут вам хорошо спать по ночам. Убедитесь, что каждый член команды придерживается этого стандарта.- Поместите сид-фразу в сейф в банке.
- Поместите свою сид-фразу на USB-накопитель с защитой pw в своем банке.
- Разделите свой ключ и отдайте его части людям, которым вы доверяете, чтобы они позаботились о нем
- Будьте изобретательны, но никогда не храните их в Интернете или в небезопасном физическом месте.
Добавление еще одного подписывающего
Как только вы убедитесь, что ваш со-подписавший защитил свой адрес для подписи, вы можете добавить его в мультиподпись. Для этого выберите только что созданную мультиподпись, прокрутите вниз и выберите Settings, прокрутите вниз до Owners и выберите его. Вы должны увидеть что-то вроде этого.
Выбирать, Add new owner:
- Добавить удобочитаемое имя и адрес
- Установите необходимые подписи — давайте установим подтверждения на 2/2, чтобы все подписавшие должны были подписать для наших целей тестирования.
Обратите внимание, как я добавил mark.cipherz.eth? Это пример поддомена ENS (служба имен Ethereum), и, вероятно, было бы неплохо назначить поддомены подписавшим, если они являются частью вашей организации. Мы рассмотрим ENS и поддомены чуть ниже. На данный момент не беспокойтесь об этом и называйте своего второго подписанта как хотите.
Нажмите Submit и подпишите последующую транзакцию Metamask. Обратите внимание, что вы должны быть подключены к той же учетной записи, с которой вы развернули мультиподпись, поскольку вы изменяете контракт в сети, чтобы включить подписывающую сторону и изменить требуемый минимум подписей. Когда это будет сделано, вы должны увидеть что-то вроде этого.
Если вы зайдете в Owners снова вы увидите добавленную новую подписывающую сторону.
Возьмите его на тест-драйв
Итак, теперь у нас есть функциональная мультиподпись на Ринкеби с несколькими подписчиками. Теперь мы проведем простой тест, используя его для безопасного хранения некоторого количества Rinkeby eth и передачи его на адрес по нашему выбору. Это касается нашего Asset Riskо которых мы говорили выше. Для начала давайте переведем немного Rinkeby eth на мультиподписной адрес и убедимся, что он отображается на нашем балансе. Чтобы получить развернутый адрес, нажмите кнопку copy, значок рядом с контрактом на левой панели.
Теперь, когда у вас есть мультиподписной адрес, переведите на него немного Rinkeby eth через финансируемый аккаунт. т.е. исходную учетную запись развертывания, если хотите.
Обратите внимание, что скопированный адрес будет иметь rink: префикс к нему — это хорошая мера безопасности, заставляющая вас перепроверить адрес.
Теперь вы должны увидеть баланс в мультиподписи. В этом случае я перевел 0,5 eth, как показано ниже.
Теперь момент, которого вы так долго ждали — выполнение транзакции, требующей нескольких подписантов…
Перейдите к Assets -> Coins и нажмите на Send, которая появляется при наведении курсора на баланс eth
Заполните информацию о переводе — вы можете отправить ее кому угодно или чему угодно, но я отправляю ее обратно себе, как показано ниже.
Нажмите «Обзор» и просмотрите сводку транзакций — если все в порядке, нажмите Submitи подпишите транзакцию метамаской. Не забудьте также подтвердить свой аппаратный кошелек. Вы должны увидеть экран, подобный приведенному ниже, который указывает, что 1 из требуемых 2 подписал транзакцию.
Затем вы можете скопировать текущий URL-адрес в браузере и отправить его человеку, который должен его подписать. Он увидит аналогичную страницу и сможет подтвердить транзакцию (обратите внимание, что вы уже подписали и эта опция сейчас неактивна)
Последний подписывающий будет иметь возможность выполнить транзакцию, но если он скряга, он может отложить выполнение другого подписывающего лица.
После того, как они отправили заявку и одна подписывающая сторона выполнила (оплатила) транзакцию — все готово. Посмотрите сводку выполненных транзакций ниже и проверьте свой баланс, чтобы увидеть, что eth был отправлен.
Ну и для чего всё это нужно?
- Контроль доступа
- Принадлежит мультиподписи
Мы будем использовать hardhat, который требует node. Вы можете установить его ЗДЕСЬ , если у вас его еще нет.
Как только у вас есть node:
Код:
# Create a project directory
mkdir upgrademe && cd upgrademe# Init the project as a node project
npm init# Install hardhat local to the project
npm install --save-dev hardhat# Begin new project wizard
npx hardhat# Example Output
✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: · /mnt/c/Users/markm/projects/datchat/upgrademe
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y
Теперь, когда у вас настроен начальный проект, давайте запустим несколько команд и убедимся, что все работает.
Код:
# Compile the smart contract
npx hardhat compile# Run the unit tests
npx hardhat test# Run the standalone deploy script
npx hardhat run scripts/deploy.ts
Вы можете задаться вопросом, на какой цепочке блоков это работает — в конце концов, мы не указали никаких целевых цепочек блоков. Hardhat запускает автономный временный узел для выполнения этих команд за кулисами. Пока нам этого будет достаточно, но вы можете отправить его в определенную сеть через параметр --network, определенной в hardhat.config.ts файле. Это выходит за рамки данной статьи, но хорошо документировано.
Контракт
Контракт представляет собой простую блокировку токена, которую владелец контракта может отправить eth и разблокировать его через указанное время.
Код:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
// Import this file to use console.log
import "hardhat/console.sol";
contract Lock {
uint public unlockTime;
address payable public owner;
event Withdrawal(uint amount, uint when);
constructor(uint _unlockTime) payable {
require(
block.timestamp < _unlockTime,
"Unlock time should be in the future"
);
unlockTime = _unlockTime;
owner = payable(msg.sender);
}
function withdraw() public {
// Uncomment this line to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
require(block.timestamp >= unlockTime, "You can't withdraw yet");
require(msg.sender == owner, "You aren't the owner");
emit Withdrawal(address(this).balance, block.timestamp);
owner.transfer(address(this).balance);
}
}
Если мы посмотрим на test/Test.tsмы должны увидеть правильное его использование.
Код:
import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Lock", function () {
// We define a fixture to reuse the same setup in every test.
// We use loadFixture to run this setup once, snapshot that state,
// and reset Hardhat Network to that snapshopt in every test.
async function deployOneYearLockFixture() {
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
const ONE_GWEI = 1_000_000_000;
const lockedAmount = ONE_GWEI;
const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;
// Contracts are deployed using the first signer/account by default
const [owner, otherAccount] = await ethers.getSigners();
const Lock = await ethers.getContractFactory("Lock");
const lock = await Lock.deploy(unlockTime, { value: lockedAmount });
return { lock, unlockTime, lockedAmount, owner, otherAccount };
}
...
Edited for brevity
...
describe("Transfers", function () {
it("Should transfer the funds to the owner", async function () {
const { lock, unlockTime, lockedAmount, owner } = await loadFixture(
deployOneYearLockFixture
);
await time.increaseTo(unlockTime);
await expect(lock.withdraw()).to.changeEtherBalances(
[owner, lock],
[lockedAmount, -lockedAmount]
);
});
});
});
});
Это хорошее начало, но мы хотим, чтобы владелец Lock.sol отправил контракт на мультиподпись. Если вы заметили, deployer контракта устанавливается в качестве owner и нет возможности его изменить. Мы немного изменим этот контракт, чтобы дать нам больше гибкости в настройке access control к определенным функциям и дают нам возможность сменить владельца с deployer к multisig.
Контроль доступа
OpenZeppelin начинался как библиотека с открытым исходным кодом общих шаблонов и контрактов solidity и является бесценным инструментом. Мы собираемся использовать их контракт AccessControl, чтобы расширить наш и придать ему функциональность, которая нам нужна.
Код:
# Install the openzeppelin contracts library
npm i @openzeppelin/contracts
Теперь мы можем добавить AccessControl Lock.sol в виде зависимости следующим образом:
Код:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/access/AccessControl.sol";
// Import this file to use console.log
import "hardhat/console.sol";
contract Lock is AccessControl {
uint public unlockTime;
event Withdrawal(uint amount, uint when);
constructor(uint _unlockTime) payable {
require(
block.timestamp < _unlockTime,
"Unlock time should be in the future"
);
unlockTime = _unlockTime;
// Grant the contract deployer the default admin role: it will be able
// to grant and revoke any roles
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function withdraw() public onlyRole(DEFAULT_ADMIN_ROLE){
// Uncomment this line to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
require(block.timestamp >= unlockTime, "You can't withdraw yet");
emit Withdrawal(address(this).balance, block.timestamp);
address payable to = payable(msg.sender);
to.transfer(address(this).balance);
}
}
Вы заметите несколько добавлений/удалений в этом контракте, так что давайте рассмотрим их.
Код:
# Imported the contract
import "@openzeppelin/contracts/access/AccessControl.sol";# removed the `owner` variable as we don't need that anymore# set the owner via a helper method of AccessControl - note that DEFAULT_ADMIN_ROLE comes by default, but you can define custom roles as well.
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);# Decorated withdraw() method with onlyRole(DEFAULT_ADMIN_ROLE) to set the security access
Если вы очистите и снова запустите тест, вы увидите ошибку, связанную с возвращенным сообщением об ошибке.
Код:
# Clean
npx hardhat clean# Test
npx hardhat test2) Lock
Withdrawals
Validations
Should revert with the right error if called from another account:
AssertionError: Expected transaction to be reverted with reason 'You aren't the owner', but it reverted with reason 'AccessControl: account 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000'
Замените неудачный тест на:
Код:
it("Should revert with the right error if called from another account", async function () {
const { lock, unlockTime, otherAccount } = await loadFixture(
deployOneYearLockFixture
);// We can increase the time in Hardhat Network
await time.increaseTo(unlockTime);// We use lock.connect() to send a transaction from another account
await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith(
"AccessControl: account 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000"
);
});
Вам также придется заменить другой тест, так как переменная owner больше не существует. Заменить Should set correct ownerтест, чтобы быть:
it.only("Should set the right owner", async function () {
const { lock, owner } = await loadFixture(deployOneYearLockFixture);
expect(await lock.hasRole(await lock.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.true;
});
Теперь ваши тесты должны пройти
Переназначить право собственности
До сих пор мы использовали локальный узел hardhat для запуска наших развертываний и тестов, просто чтобы убедиться, что все работает согласованно.Теперь мы воспользуемся тестовой сетью для развертывания нашего контракта и создадим собственный сценарий жесткого диска, чтобы назначить право собственности на контракт нашей мультиподписи, которую мы создали в первой части .
RPC
Вам нужно будет создать конечную точку rpc, которую ваше приложение будет использовать для взаимодействия с тестовой сетью. Я бы предложил использовать инфуру или алхимию для создания узла rpc, указывающего на ethereum сеть и rinkeby тестовая сеть. Вы можете использовать ЭТОТ сборщик, чтобы получить тестовый эфир для своих транзакций.
Hardhat.config.ts
Ниже представлен обновленный hardhat.config.ts которые вы должны использовать для своего проекта. Мы добавили несколько вещей
- hardhat task передает право собственности на контракт от деплойнера к мультиподписчику
- А network для ринкеби.
- Закрытый ключ для учетной записи развертывателя — тот, который вы использовали для rinkeby
Код:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";
dotenv.config();
task(
"xfer-contract-multisig",
"Transfer ownership of contract to multisig"
).setAction(async (taskArgs, hre) => {
const Contract = await ethers.getContractFactory("Todo");
const contract = await Contract.deployed();
// grant multisig DEFAULT_ADMIN_ROLE
const grantAdminTx = await contract.grantRole(DEFAULT_ADMIN_ROLE, multisig);
await grantAdminTx.wait();
const correctMultisigRole = await contract.hasRole(
DEFAULT_ADMIN_ROLE,
multisig
);
expect(correctMultisigRole).to.be.true;
console.log(`PartnerNFT: Grant DEFAULT_ADMIN_ROLE to multisig: ${multisig}`);
// revoke deployer DEFAULT_ADMIN_ROLE
const revokeAdminTx = await contract.renounceRole(
DEFAULT_ADMIN_ROLE,
deployer
);
await revokeAdminTx.wait();
const correctDeployerRole = await contract.hasRole(
DEFAULT_ADMIN_ROLE,
deployer
);
expect(correctDeployerRole).to.be.false;
console.log( `PartnerNFT: Revoke DEFAULT_ADMIN_ROLE from deployer: ${deployer}`);
console.log("Transferred ownership to:", multisig);
});
const config: HardhatUserConfig = {
solidity: "0.8.9",
networks: {
mumbai: {
url:
process.env.RPC_URL !== undefined
? process.env.RPC_URL
: "",
accounts:
process.env.DEPLOYER_PRIVATE_KEY !== undefined
? [process.env.DEPLOYER_PRIVATE_KEY]
: [],
}
}
};
export default config;
.env
Поскольку это статья о безопасности, вероятно, лучше показать вам, как хранить секреты вне вашего кода, а не помещать их непосредственно в конфигурацию. Вы будете удивлены, узнав, сколько разработчиков невольно передают свои секреты в общедоступный репозиторий…
Код:
# Install dotenv package
npm i --save-dev dotenv# Install hardhat ethers
npm i --save-dev @nomiclabs/hardhat-ethers 'ethers@^5.0.0'# Install chai to test assertions
npm i --save-dev chai
Создать .env файл в корне проекта со следующими значениями
Код:
RPC_URL=https://https://eth-rinkeby.alchemyapi.io/v2/<ваш ключ API> DEPLOYER_PRIVATE_KEY=<ваш закрытый ключ>
Обратите внимание, что в нашем .gitignore файле мы исключаем все файлы с именем .env так что это никогда не будет зафиксировано в нашем репо.
Ринкеби
Теперь, когда у нас все настроено, давайте продолжим и развернем наш контракт на rinkeby. На самом деле мы собираемся изменить наш deploy.tsscript немного и уменьшите заблокированное количество эфира с 1 до 0,01. Трудно найти тестовую сеть eth, поэтому давайте не будем блокировать целую сеть eth. Кроме того, мы собираемся сократить время блокировки до 5 минут, чтобы мы могли увидеть фактическое успешное withdrawal вместо того, чтобы ждать год.Изменить строку 5 в scripts/deploy.ts:
Код:
const ONE_YEAR_IN_SECS = 5 * 60; // 5 minutes
Изменить строку 8 в scripts/deploy.ts:
Код:
const lockedAmount = ethers.utils.parseEther(".01");
Развертываем
Давайте продолжим и развернем контракт, используя команду ниже
Код:
# Deploy to Rinkeby
npx hardhat run scripts/deploy.ts --network rinkeby
Сценарий должен вернуться с развернутым адресом контракта, как показано ниже.
Lock with .01 ETH deployed to: 0x73f701a4a56AA14513a2A108624b4D00f90D27b2
Мультиподпись
Теперь давайте возьмем мультиподписной контракт, который мы развернули с помощью Gnosis для Rinkeby выше. Мы будем использовать этот адрес и адрес развернутого контракта для следующего шага.
Передача права собственности на контракт Multisig
Далее мы запустим пользовательскую задачу, которую мы создали, чтобы передать право собственности на контракт с учетной записи развертывателя на мультиподпись на rinkeby.
Код:
npx hardhat xfer-contract-multisig --multisig 0x8cB6021aB0eCBE65988dA55D0e55d6B4c601fA36 --contract 0x73f701a4a56AA14513a2A108624b4D00f90D27b2 --network rinkeby
Вы должны увидеть вывод, похожий на следующий
Код:
Grant DEFAULT_ADMIN_ROLE to multisig: 0x8cB6021aB0eCBE65988dA55D0e55d6B4c601fA36
Revoke DEFAULT_ADMIN_ROLE from deployer: 0xec2FFc240CbDE001643a045e2C1Ad928D9eB8aee
Transferred ownership to: 0x8cB6021aB0eCBE65988dA55D0e55d6B4c601fA36
Теперь, когда наш контракт развернут и право собственности передано нашей мультиподписи, давайте продолжим и настроим наше OpenZeppelin Defender , чтобы упростить подписи для нашей Gnosis .
OpenZeppelin Defender
Давайте создадим учетную запись на https://defender.openzeppelin.com/ , чтобы использовать ее для управления действиями по нашему развернутому контракту. Защитник — это полезный инструмент для согласования предложений и подписей с нашими развернутыми контрактами — он может многое сделать, но в основном мы собираемся использовать его для управления нашими действиями с несколькими подписями.Добавить мультиподпись Gnosis
Создав учетную запись OpenZeppelin, нажмите кнопку Add Contract кнопку в правом верхнем углу, чтобы добавить наш мультиподписной контракт.
Введите имя, сеть и адрес контракта, которые вы можете получить в приложении Gnosis. Это позволит импортировать контракт в OpenZeppelin Defender, чтобы мы могли использовать его для подписи.
Прокрутите вниз и нажмите Addи у нас все хорошо. Теперь вы должны увидеть, что Defender распознал наш контракт как мультиподписной и отображает его, как показано ниже.
Добавим контракт блокировки
Нажмите Add Contractеще раз и добавить Lock контракт, который мы развернули в Ринкеби. Повторение - мать учения — просто дайте ему имя, сеть и адрес. Однако, поскольку этот контракт не проверен на Ринкеби, нам нужно вставить abi для добавления контракта. Вы можете найти abi в файле artifacts/contracts/Lock.sol/Lock.json. Найди abi атрибут и скопируйте/вставьте все значение в раздел Defender abi. Вы должны быть в состоянии щелкнуть Add чтобы завершить добавление вашего контракта. Теперь вы должны увидеть оба контракта на панели инструментов, как показано ниже.
Сейчас все настроено, и теперь мы можем создать предложение по нашему контракту и выполнить его через несколько подписантов через мультиподпись.
Собираем все вместе
В нашем последнем рывке мы будем делать следующее- Создание предложения на нашем Lockдоговор
- Подписание предложенной транзакции с несколькими авторизованными учетными записями
- Посмотрите, как мультиподпись выполняет предложение
Создать предложение
Перейдите к Dashboard => Contracts => Lock => New Proposal => Admin Action в Defendor'е. Выберите withdraw и Multisigс, которую вы импортировали. Введите аннотацию для заголовка и описания и нажмите Create Admin Action. Обратите внимание, что любой может создать предложение, но авторизовать предложение могут только действительные подписывающие лица. После заполнения форма должна выглядеть примерно так, как показано ниже.
Подписать предложение
Теперь, когда наше предложение создано, мы можем подписать его с помощью учетных записей владельцев, которые мы назначили нашей мультиподписи Gnosis. Перейдите к мультиподписи Gnosis и проверьте, какие подписанты авторизованы. Это должно выглядеть примерно так.
Войдите в одну из перечисленных здесь учетных записей с помощью Metamask и снова перейдите на страницу предложения в Defendor'е. Теперь вы должны увидеть Approval. Нажмите эту кнопку, чтобы одобрить предложение, как показано ниже, и подпишите свой метамаск.
Затем Defendor сообщит вам, что у вас есть одно одобрение и сколько осталось, как показано ниже. В этом случае у нас есть еще одно одобрение.
На этом этапе вы можете подключить другую учетную запись Metamask и либо Approve and Executeили просто Approve. Если вы просто одобрите предложение, один из подписантов все равно должен будет его выполнить. Иногда, если это сделка с большим объемом газа, ее должен выполнить один из более богатых владельцев. Как только все утверждающие лица подпишут транзакцию и она будет выполнена, транзакция будет отправлена в блокчейн для проверки.
Проверяем
Теперь, когда наша мультиподписная транзакция обработана, давайте проверим, что все работает как положено. Мы должны увидеть депозит в размере 0,01 ETH от Lock контракта с нашей мультиподписью, поэтому давайте проверим это, взглянув на нашу мультиподпись Gnosis. Как вы можете видеть ниже, withdraw функция была выполнена, и если мы посмотрим на транзакцию на etherscan, мы увидим перевод 0,01 ETH.
Успех! Поздравляем с выполнением вашей первой транзакции по контракту с мультиподписью.