Inteligentne kontrakty w blockchain

Piotr Nazimek
Partner, Head of Training
Ikona kalendarza
7 czerwca 2017

Dotychczas w uruchomionym prywatnym łańcuchu bloków (ang. blockchain) Ethereum przechowywaliśmy rejestr transakcji oraz salda jego użytkowników. Poniżej przedstawię w jaki sposób umieścić w blockchain własną aplikację i jak z niej korzystać. Wykorzystamy do tego już utworzony łańcuch, którym zajmowaliśmy się w poprzednim wpisie .

Inteligentne kontrakty

Aplikacje umieszczane w blockchain mają najczęściej za zadanie dopilnowanie realizacji pewnych zobowiązań. Z tego powodu nazywane są kontraktami lub inteligentnymi kontraktami (ang. smart contracts). Jest kilka języków, w których można implementować kontrakty uruchamiane w Ethereum. Obecnie najpopularniejszym jest Solidity.

Nasz pierwszy kontrakt będzie prostą aplikacją przechowującą ciąg znaków, który będzie można odczytać w dowolnym momencie po jego utworzeniu. Dane przechowywane przez kontrakt mogą być ustalone wyłącznie na etapie jego uruchamiania. Nie będzie możliwości ich zmiany w ramach konkretnej instancji kontraktu.

1/* identyfikator używanej wersji języka */
2pragma solidity ^0.4.8;
3
4contract greeter {
5    /* zmienna przechowująca wartość typu string */
6    string greeting;
7
8    /* konstruktor kontraktu */
9    function greeter(string _greeting) public {
10        /* przypisanie wartości pola kontraktu */
11        greeting = _greeting;
12    }
13
14    /* funkcja kontraktu, zwraca zapisaną wartość */
15    function greet() constant returns (string) {
16        return greeting;
17    }
18}

Kontrakt utworzymy za pomocą klienta Mist. Po uruchomieniu węzła oraz procesu kopania w prywatnym blockchain możemy włączyć aplikację kliencką. Wybieramy w niej zakładkę Contracts, a następnie Deploy new Contract. Ustalamy konto, z którego będziemy tworzyć kontrakt (poniesie ono opłatę za transakcję tworzenia kontraktu), wklejamy kod źródłowy kontraktu i wybieramy kontrakt greeter do utworzenia, podając parametry jego konstruktora. Operację potwierdzamy wciskając klawisz Deploy.

mist-deploy-new-contract.webp

W oknie Create contract ustalamy maksymalną dopuszczalną opłatę za transakcję tworzenia kontraktu. Należy zwrócić szczególną uwagę na jej wartość. Nie powinna być ona niższa niż przewidywana, ponieważ wtedy nasz kontrakt się nie utworzy - nie starczy środków na jego utworzenie.

mist-create-contract.webp

Po zatwierdzeniu transakcji trzeba poczekać na wykopanie kolejnego bloku, w którym znajdzie się kontrakt. Będzie on widoczny w zakładce Contracts. Korzystanie z funkcji jest bezpłatne, ponieważ nie modyfikuje ona stanu blockchain, a jedynie odczytuje jego zawartość. Mist wykona taką funkcję automatycznie, jeśli nie potrzebuje ona żadnych parametrów.

mist-greeter.webp

Na podstawie powyższego kodu źródłowego można utworzyć wiele niezależnych instancji kontraktu. Każdy z nich będzie miał swój adres, podobnie jak konta użytkowników.

Przykładowy kontrakt już na zawsze będzie używał ustawionego w konstruktorze pozdrowienia. Dodając nową funkcję, możemy umożliwić zmianę przechowywanego ciągu znaków.

1    /* funkcja ustawiająca nowe pozdrowienia */
2    function setGreeting(string _newgreeting) {
3        greeting = _newgreeting;
4    }

Możliwość wywołania funkcji pojawi się w zakładce danego kontraktu jako pole Write contract. Możemy w nim wybrać określoną funkcję oraz ustalić jej parametry.

mist-greeter-set.webp

Operacja zmiany pozdrowienia wiąże się z opłatą i jest wykonywana jako transakcja, ponieważ zmienia stan blockchain. Powyższą funkcję może wywołać każdy użytkownik blockchain. W historii będą przechowywane poprzednio ustawione wartości. Możemy je odczytać odwołując się do konkretnego bloku. Funkcja greet kontraktu wywołana z poziomu Mist będzie zwracała wartość z ostatnio wykopanego bloku.

Przykładowy kontrakt rozszerzymy jeszcze o możliwość modyfikacji pozdrowienia pod warunkiem, że będzie to robił właściciel kontraktu, czyli konto, z którego kontrakt został utworzony. W tym celu rozszerzymy przykładowy kontrakt o przechowywanie adresu twórcy oraz o sprawdzanie czy transakcja zmiany pozdrowienia pochodzi z wcześniej ustalonego adresu.

1pragma solidity ^0.4.8;
2
3contract greeter {
4    string greeting;
5
6    /* zmienna przechowująca adres tworzącego kontrakt */
7    address owner;
8
9    function greeter(string _greeting) public {
10        owner = msg.sender;
11        greeting = _greeting;
12    }
13
14    function greet() constant returns (string) {
15        return greeting;
16    }
17
18    /* funkcja ustawiająca nowe pozdrowienie */
19    function setGreeting(string _newgreeting) {
20	if (msg.sender == owner)
21            greeting = _newgreeting;
22    }
23}

Proszę spróbować zmienić wartość pozdrowienia w powyższym kontrakcie używając różnych kont.

Nowa kryptowaluta

Za pomocą kontraktów można również utworzyć nową kryptowalutę w ramach blockchain Ethereum. Poniższy kontrakt kojarzy saldo z adresem poprzez strukturę mapy. W konstruktorze ustalamy początkową liczbę tokenów i przypisujemy ją do konta twórcy. Zauważmy, że nie ma on możliwości późniejszego tworzenia nowych tokenów. Będzie ich w obrocie tyle, ile utworzono ich w konstruktorze.

Dodatkowo poniższy kontrakt zapisuje każdą transakcję w logu Ethereum, który może być obserwowany przez klientów blockchain. Nie muszą oni śledzić sald wszystkich adresów, zostaną poinformowani o wykonywanych transakcjach odczytując zawartość logu w każdym bloku. Realizowane jest to za pomocą zdarzenia (ang. event) Transfer, które przechowuje informacje o adresach zlecającego i odbiorcy oraz o kwocie transakcji. Zdarzenie zostanie zapisane wyłącznie dla zrealizowanych transakcji.

1pragma solidity ^0.4.8;
2
3contract Token {
4    /* mapa adresów w blockchain i przyporządkowanym im sald */
5    mapping (address => uint256) public balanceOf;
6
7    /* zdarzenie w logu blockchain, poinformuje klientów o transakcji */
8    event Transfer(address indexed from, address indexed to, uint256 value);
9
10    function Token(uint256 initialSupply) {
11        balanceOf[msg.sender] = initialSupply;
12    }
13
14    /* wykonanie transferu środków */
15    function transfer(address _to, uint256 _value) {
16	/* sprawdź środki na koncie */
17        if (balanceOf[msg.sender] < _value) throw;
18	/* sprawdź czy nie dojdzie do przepełnienia na koncie odbiorcy */
19        if (balanceOf[_to] + _value < balanceOf[_to]) throw;
20	/* przekaż środki */
21        balanceOf[msg.sender] -= _value;
22        balanceOf[_to] += _value;
23	/* poinformuj o transakcji */
24        Transfer(msg.sender, _to, _value);
25    }
26}

W kontrakcie nie ma zaimplementowanej funkcji, która zwraca saldo dla danego adresu, ale operacja ta jest możliwa. Funkcja ta tworzy się automatycznie jeśli ustawimy daną zmienną jako public. Odczyt salda danego konta jest bezpłatny.

mist-token.webp

Zlecając transakcję transferujemy tokeny pomiędzy kontami, ale samą opłatę za realizację tej operacji ponosimy w paliwie Ethereum, czyli w etherach. Po realizacji transakcji i włączeniu opcji obserwacji zdarzeń w polu Latest events w zakładce danego kontraktu będzie pokazywał się ich wykaz.

mist-events.webp

Zachęcam do rozbudowania powyższego kontraktu o możliwość kreacji dodatkowych tokenów przez właściciela kontraktu. Z poziomu klienta Mist możemy również utworzyć kontrakt Wallet, który będzie portfelem umożliwiającym przechowywanie etherów rozbudowanym o dodatkowe funkcje takie jak potwierdzanie transakcji przez kilku właścicieli portfela oraz maksymalną wartość jednorazowej transakcji.

Podsumowanie

Inteligentne kontrakty umożliwiają realizację własnych pomysłów wykorzystujących jedną instancję blockchain Ethereum. W praktyce pozwala to na uruchomienie nowej kryptowaluty, funduszu inwestycyjnego lub innej aplikacji z zaimplementowanymi zasadami. Za jej utrzymanie zapłacimy etherami, które są paliwem dla samego Ethereum. Tym sposobem ułatwione jest uruchamianie rozproszonych aplikacji, które powinny mieć wysoki poziom zaufania w niezaufanej sieci. Kody źródłowe wielu klasycznych kontraktów można odnaleźć na stronach Ethereum.

Przeczytaj także

Ikona kalendarza

29 marzec

To be or not to be a multi cloud company? Przewodnik dla kadry kierowniczej, doradców ds. chmury i architektów IT. (część 2)

Po przeczytaniu pierwszej części poradnika zauważymy, że strategie organizacji są różne. Część firm oparło swój biznes wyłącznie na j...

Ikona kalendarza

27 wrzesień

Sages wdraża system Omega-PSIR oraz System Oceny Pracowniczej w SGH

Wdrożenie Omega-PSIR i Systemu Oceny Pracowniczej w SGH. Sprawdź, jak nasze rozwiązania wspierają zarządzanie uczelnią i potencjałem ...

Ikona kalendarza

12 wrzesień

Playwright vs Cypress vs Selenium - czy warto postawić na nowe?

Playwright, Selenium czy Cypress? Odkryj kluczowe różnice i zalety każdego z tych narzędzi do automatyzacji testów aplikacji internet...