The Ethernaut #1 – nie popełniaj tych samych błędów

Przemek/ 23 stycznia, 2023

Błędy w smart kontraktach doprowadziły niejednokrotnie do utraty setek milionów dolarów. Chociaż wiele z nich nie zostało jeszcze wykrytych, dobrze poznać chociaż te najbardziej popularne. Dlatego tym wpisem rozpoczynam serię dotyczącą platformy Ethernaut. Nie jest to jedyna rzecz, która pojawiać się będzie na blogu, dlatego od teraz posty różnego typu, będą przeplatane wpisami, w których przejdziemy razem przez wszystkie zadania. Jeżeli masz już wystarczającą wiedzę, to polecam spróbować je rozwiązać samemu. Wszystkie wyzwania znajdziesz pod tym adresem. Ale jeśli utkniesz na którymś zadaniu, to na blogu będziesz mógł dowiedzieć się co i jak. Nie przedłużając, bierzemy się do roboty!

Czy przypadkiem czegoś nie pominąłeś?

Wchodząc na stronę zauważysz, że pierwsze wyzwanie ma numerek 0 (Hello Ethernaut). Zadanie to ma na celu skonfigurowanie niezbędnych narzędzi i zapoznanie się z platformą. Ponieważ wszystko co niezbędne opisane tam jest w 9 szczegółowych krokach, to nie ma sensu, abym po kolei przepisywał dokładnie instrukcję na bloga:). Co prawda wszystko to, co tam się znajduje jest w języku angielskim i jeśli sprawia Ci on problemy, to nie musisz się martwić i możesz skorzystać np. z Google Translate, który będzie wystarczający do tego typu zadania.

Do ukończenia wszystkich wyzwań skorzystamy z sieci Goe, dlatego jeżeli jeszcze nie posiadasz żadnego testowego etheru, to możesz go dostać tutaj.

Możesz również od razu przejść do zadania #1, ale jeżeli lubisz mieć ukończone wszystko w 100%, a nie chce Ci się przeklikać wszystkiego, to poniżej podaję rozwiązanie. Nie patrz tam, jeżeli chcesz to zrobić samemu. Pamiętaj jedynie, żeby posiadać zainstalowanego Metamaska.

Wciśnij przycisk “Get Instance” i zatwierdź operację w Metamasku. Następnie otwórz konsolę deweloperską i wpisz:
await contract.authenticate(“ethernaut0”)

Po zatwierdzeniu operacji i wykonaniu się transakcji wciśnij “Submit Instance” i tym sposobem ukończysz wyzwanie nr 0. Teraz możemy już przejść do czegoś ciekawszego.

Zadanie #1 – fallback

Rysunek 2. Drugi mem
(źródło: https://memy.pl/mem_623022_do_roboty)

Celem pierwszego wyzwania jest przejęcie kontroli nad kontraktem i wyciągnięcie z niego całego etheru. Na początek sprawdźmy, co się w nim dzieje.

pragma solidity ^0.8.0;

contract Fallback {

 mapping(address => uint) public contributions;
 address public owner;

 constructor() {
   owner = msg.sender;
   contributions[msg.sender] = 1000 * (1 ether);
 }

 modifier onlyOwner {
       require(
           msg.sender == owner,
           "caller is not the owner"
       );
       _;
   }

 function contribute() public payable {
   require(msg.value < 0.001 ether);
   contributions[msg.sender] += msg.value;
   if(contributions[msg.sender] > contributions[owner]) {
     owner = msg.sender;
   }
 }

 function getContribution() public view returns (uint) {
   return contributions[msg.sender];
 }

 function withdraw() public onlyOwner {
   payable(owner).transfer(address(this).balance);
 }

 receive() external payable {
   require(msg.value > 0 && contributions[msg.sender] > 0);
   owner = msg.sender;
 }
}

Kontrakt zawiera dwa pola. Pierwsze z nich przechowuje mapowanie, mówiące o tym, ile dana osoba wniosła środków do kontraktu, drugie posiada adres obecnego właściciela kontraktu (jest to twórca kontraktu). Jak możesz zauważyć, wkład twórcy wynosi 1000 Etherów, a przynajmniej taka dana zapisywana jest w mapowaniu, ponieważ nic nie zostało wpłacone podczas tworzenia kontraktu.

Za konstruktorem znajduje się modyfikator, który pozwala wywołać funkcję jedynie właścicielowi.

Pierwsza z funkcji to “contribute”. Rozpoczyna się ona od sprawdzenia, czy ilość etheru wysłanego podczas jej wywołania jest mniejsza niż 0.001 jednostki. Jeżeli tak, to do mapowania przypisywana jest wartość i jeżeli jest ona większa niż wkład obecnego właściciela, to jest on podmieniany na nowego. Łącząc początkowy warunek z faktem, że właściciel ma przypisane 1000 jednostek etheru, nie będziemy w stanie wykorzystać tej funkcji do przejęcia kontroli. Jednak przyda ona nam się w całym procesie, także idźmy dalej.

Następnie napotykamy na metodę zwracająca dane o wkładzie poszczególnych kont. Nic interesującego. Za nią znajdziemy funkcję służącą do wypłaty środków znajdujących się na koncie. Jak możesz zauważyć, po jej wywołaniu cały wkład zostanie przetransferowany na konto aktualnego właściciela. Za ten przyda ona nam się później, jak już przejmiemy kontrolę nad kontraktem.

Ostatnią funkcją, jaka znajduje się w kontrakcie, jest ta o nazwie “receive”. Jest to jednak tzw. “fallback function”, a implementuje się ją w celu umożliwienia wysyłania etherów na kontrakt. Aby funkcja “przesłała”, wartość wysłanych środków musi być większa niż 0 oraz wysyłający musi już mieć jakiś wkład. Jeśli taka sytuacja będzie miała miejsca, to zostaje przypisany nowy właściciel kontraktu.

Kod omówiony, pora zabrać się do działania. Jedyne miejsce, gdzie możemy realnie przejąć kontrolę nad kontraktem, to funkcja “receive”. Jednak żeby przejść jej wymagania, musimy najpierw dokonać jakiekolwiek wpłaty, przy pomocy metody “contribute”. Przy jej wywołaniu możemy wybrać dowolnie małą ilość etheru do wpłaty np. 1 WEI (0,000000000000000001 ETH). Wszystkie operacje możesz wykonać zarówno z poziomu konsoli deweloperskiej na stronie wyzwania lub np. przy pomocy środowiska Remix. Wybór należy do Ciebie:).

Kolejnym krokiem będzie wysłanie ponownie 1 WEI bezpośrednio na adres kontraktu. Po zakończeniu transakcji, zostaniemy aktualnym właścicielem kontraktu. W tym momencie możemy wywołać funkcję “withdraw”, która opróżni kontrakt z etheru. Teraz wyzwanie jest gotowe do zatwierdzenia, dlatego na stronie zadania klikamy przycisk “Submit Instance” i po chwili możemy cieszyć się wygraną.

To by było na tyle w tym wyzwaniu. Mam nadzieję, że wszystko jest w tej chwili dla Ciebie zrozumiałe. Jeśli coś jest niejasne, to nie wahaj się zostawić komentarza z pytaniem. Tymczasem to wszystko na dziś i do zobaczenia za niedługo:)

Share this Post
Subscribe
Powiadom o
guest
0 komentarzy
Inline Feedbacks
View all comments