Inteligentne kontrakty w blockchain

7 lat temu
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](/blog/blockchain-czyli-sposob-na-zabezpieczenie-danych/).

## 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. w tej chwili najpopularniejszym jest [Solidity](https://solidity.readthedocs.io/en/develop/).

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.

```solidity
/* identyfikator używanej wersji języka */
pragma solidity ^0.4.8;

contract greeter {
/* zmienna przechowująca wartość typu string */
string greeting;

/* konstruktor kontraktu */
function greeter(string _greeting) public {
/* przypisanie wartości pola kontraktu */
greeting = _greeting;
}

/* funkcja kontraktu, zwraca zapisaną wartość */
function greet() constant returns (string) {
return greeting;
}
}
```

Kontrakt utworzymy dzięki klienta
[Mist](https://github.com/ethereum/mist/releases). 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](/uploads/mist_deploy_new_contract_928337c76a.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](/uploads/mist_create_contract_fe4a82d635.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żeli nie potrzebuje ona żadnych parametrów.

![mist-greeter.webp](/uploads/mist_greeter_40183d093e.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.

```solidity
/* funkcja ustawiająca nowe pozdrowienia */
function setGreeting(string _newgreeting) {
greeting = _newgreeting;
}
```

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](/uploads/mist_greeter_set_f4217776ae.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, iż 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.

```solidity
pragma solidity ^0.4.8;

contract greeter {
string greeting;

/* zmienna przechowująca adres tworzącego kontrakt */
address owner;

function greeter(string _greeting) public {
owner = msg.sender;
greeting = _greeting;
}

function greet() constant returns (string) {
return greeting;
}

/* funkcja ustawiająca nowe pozdrowienie */
function setGreeting(string _newgreeting) {
if (msg.sender == owner)
greeting = _newgreeting;
}
}
```

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, iż 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 dzięki 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.

```solidity
pragma solidity ^0.4.8;

contract Token {
/* mapa adresów w blockchain i przyporządkowanym im sald */
mapping (address => uint256) public balanceOf;

/* zdarzenie w logu blockchain, poinformuje klientów o transakcji */
event Transfer(address indexed from, address indexed to, uint256 value);

function Token(uint256 initialSupply) {
balanceOf[msg.sender] = initialSupply;
}

/* wykonanie transferu środków */
function transfer(address _to, uint256 _value) {
/* sprawdź środki na koncie */
if (balanceOf[msg.sender]
Idź do oryginalnego materiału