Разбор заданий с ethernaut.
Hello Ethernaut
Решение задачи банально, просто организуем работу с функциями. Вызывай каждую функцию по отдельности, после получения пароля
- В момент создания экземпляра контракта, в конструктор передается строковый аргумент
_password
, который устанавливается в переменнуюpassword
. - Функция
info()
возвращает строку "Вы найдете то, что вам нужно в info1()." - Функция
info1()
возвращает строку "Попробуйте info2(), но с параметром 'hello'." - Функция
info2()
принимает аргументparam
и проверяет, равен ли хэш переданного параметра 'hello'. Если равны, то возвращает строку "Свойство infoNum содержит номер следующего метода для вызова.", в противном случае возвращает строку "Неверный параметр." - Функция
info42()
возвращает строку "theMethodName - это имя следующего метода." - Функция
method7123949()
возвращает строку "Если вы знаете пароль, представьтесь в authenticate()." - Функция
authenticate()
принимает аргументpasskey
и проверяет, совпадает ли хэш переданного аргумента с хэшемpassword
. Если пароль верный, то переменнаяcleared
устанавливается в значениеtrue
. - Функция
getCleared()
возвращает текущее значение переменнойcleared
.
Задача Fallback.
И методы контракта, и функции web3.js внедрены в консоль.
Мы, адрес игрока, должны каким-то образом стать владельцем контракта и вывести все средства из контракта.
Ключевые моменты, на которые следует обратить внимание, - это функция contribute и функция receive fallback контракта.
Из конструктора видно, что вклад владельца составляет 1000 ETH. Одним из способов стать владельцем является отправка на функцию contribute суммы, превышающей текущий вклад владельца, чтобы стать владельцем. Давайте проверим текущий вклад владельца, используя консоль.
Однако это слишком много ETH! У нас далеко не такая сумма.
Однако обратите внимание на функцию receive fallback. В ней также содержится код для изменения владения. Согласно коду, мы можем заявить права на владение, если:
- В контракте есть ненулевой вклад от нас (то есть от игрока).
- Затем мы отправляем контракту ненулевую сумму ETH.
В данный момент адрес игрока не имеет никаких вкладов в контракт, поэтому давайте удовлетворим первое условие, отправив менее 0.001 ETH (как требуется в коде).
await contract.contribute.sendTransaction({ from: player, value: toWei('0.0009')})
await contract.getContribution().then(v => v.toString())
await sendTransaction({from: player, to: contract.address, value: toWei('0.000001')})
await contract.owner()
await contract.withdraw()
Адрес игрока в данный момент не имеет никаких вкладов в контракт, поэтому давайте удовлетворим первое условие, отправив менее 0.001 ETH (как требуется в коде).
Задания Fallout
Игрок должен заявить права на владение контрактом.
Изучив все методы, видно, что нет ни одного метода, который изменяет владение контрактом. Логика владения находится только в конструкторе. Но конструкторы вызываются только один раз при развертывании контракта!
Как? Что-то необычно с объявлением конструктора - он "кажется" определенным с тем же именем, что и контракт, то есть Fallout (на самом деле это не так). Разве мы не используем ключевое слово constructor для объявления конструкторов?
Прежде всего - намеренное объявление конструктора содержит опечатку, "Fal1out" вместо "Fallout". Поэтому он просто рассматривается как обычный метод, а не как конструктор. Поэтому мы просто вызываем его и заявляем права на владение.
Во-вторых, даже если это не была опечатка, то есть, если конструктор был объявлен как Fallout, он даже не скомпилируется! В более старых версиях языка Solidity такое объявление конструктора было возможным. Если вы просмотрите документацию, вы узнаете, что ключевое слово constructor было предпочтительным перед именем контракта в качестве конструктора. Версия 0.5.0 введет следующее требование: "Теперь конструкторы должны быть определены с использованием ключевого слова constructor". И целевой контракт использует версию 0.6.0.