owned this note
owned this note
Published
Linked with GitHub
---
title: t.me/lasagnahowto Введение в разработку децентрализованных приложений на Ethereum
---
# Что такое Dapp? я не знаю что это, но права на статью у вас настроены неверно. t.me/lasagnahowto
**Dapp** (Decentralized application) - приложения, логика которых построена не на обычных централизованых серверах, а работающие на децентрализованых peer-to-peer сетях.
![](https://vanbex.com/sites/default/files/inline-images/Artboard-9--1-.png)
Примеры Dapp:
- [CryptoKitties](https://www.cryptokitties.co/) (игра с котиками)
- [Ethlance](https://ethlance.com/) (биржа фрилансеров)
- [Aragon](https://aragon.org/) (управление компанией)
- [uPort](https://www.uport.me/) (управление персональными данными)
- и еще более двух тысяч (можно посмотреть [здесь](https://www.stateofthedapps.com/))
## Dapp vs App
При традиционном подходе к разработке, приложение взаимодействует с сервером, который обрабатывает запросы и формирует логику общения с пользователем. В Dapp же всё немного иначе - в роли сервера выступает блокчейн сеть, имеющая функцию смарт-контрактов или похожего функционала.
Как правило, взаимодействие с обычными веб-приложениями строится следующим образом:
1) Сервер отдаёт статичные файлы (html, css, js, ...)
2) При определенном действии пользователя, браузер пользователя отправляет запрос к серверу
3) Сервер обрабатывает запрос и отдаёт ответ
4) Браузер пользователя реагирует на ответ и изменяет страницу
А Dapp как правило работают следующим образом:
1) Сервер отдаёт статичные файлы (html, css, js, ...)
2) При определенном действии пользователя, браузер пользователя отправляет запрос к эндпоинту, подключенному к блокчейну Ethereum
3) Смарт контракт запускает нужный сценарий
4) Браузер пользователя реагирует на изменения в блокчейне и изменяет страницу
# Ethereum
Мы сфокусируемся на разработке децентрализованных приложений с помощью блокчейна Ethereum и смарт контрактов. Это наиболее популярный и стабильный выбор для реализации Dapp на сегодняшний день.
![](https://miro.medium.com/max/1400/1*xIzdc_FVszgB3cCaqMd5ZQ.png)
Ethereum имеет поддержку смарт контрактов - именно их мы будем использовать для реализации логики наших приложений.
## Тестовые сети
Для тестирования наших приложений мы будем пользоваться публичными тестовыми сетями Ethereum. Это позволит нам создавать транзации и ничего не платить за газ. При этом они поддерживают весь функционал mainnet сети.
Тестовые сети:
- Ropsten (POW)
- Kovan (POA)
- Rinkeby (POA)
- Görli (POA)
:::success
Все транзакции, проведенные в тестовой сети, бесплатны и никак не влияют на mainnet сеть.
:::
## JSON RPC
[JSON RPC](https://www.jsonrpc.org/specification) - простой и легковесный протокол удалённого вызова процедур, использующий JSON для кодирования сообщений.
JSON RPC понадобится нам для взаимодействия с узлом Ethereum - большинство ПО для нод (Geth, Parity, PyEthereum, Aleth) поддерживает JSON RPC API. С помощью JSON RPC API мы сможем создавать транзакции и взаимодействовать со смарт контрактами.
Пример запроса к ноде, чтобы узнать цену газа:
```json
{
"jsonrpc": "2.0",
"method": "eth_gasPrice",
"params": [],
"id":73
}
```
Ответ:
```json
{
"jsonrpc": "2.0",
"id":73,
"result": "0x09184e72a000"
}
```
# Web3
Как пользователю проводить операции в блокчейне Ethereum через браузер?
Для этого есть [web3.js](https://github.com/ethereum/web3.js/) - коллекция библиотек, позволяющая взаимодействовать с локальными и удаленными узлами Ethereum (использует JSON RPC). С помощью web3.js браузер может создавать транзакции, и следить за состоянием блокчейн сети.
## Metamask
Metamask - это расширение для браузера, встраивающее web3 в браузер, на котором оно установленно. Таким образом, любой пользователь может взаимодействать с блокчейном Ethereum на вашем сайте.
![](https://www.ledger.com/wp-content/uploads/2019/06/assets_logo_metamask.jpg)
## Задание №1: Установка Metamask и создание аккаунта
1) Установите расширение Metamask для вашего браузера
2) Создайте новый кошелек
3) Выберите `Ropsten test network` в качестве провайдера
4) Пополните тестовый аккаунт здесь: https://faucet.metamask.io/
# Простое веб-приложение с Metamask
Рассмотрим простое веб-приложение, показывающее номер последнего блока Ethereum.
`index.html:`
```htmlmixed=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Текущий блок</title>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<p>Текущий блок: <span id='block' style="font-size: 120%">неизвестно</span></p>
<button id='update-button'>Обновить</button>
</body>
</html>
```
`script.js:`
```javascript=
window.addEventListener('load', () => {
window.web3 = new Web3(window.ethereum);
if (window.ethereum) {
try {
await ethereum.enable()
} catch (error) {
alert('MetaMask access is not granted!')
}
button = document.getElementById('update-button')
blockSpan = document.getElementById('block');
button.addEventListener('click', () => {
web3.eth.getBlockNumber(function(err, blockNumber) {
blockSpan.textContent = blockNumber;
});
});
} else {
alert('MetaMask needed!')
}
});
```
Файлы `index.html` и `script.js` находятся в директории с названием `content`.
Как сервер будем использовать nginx в docker контейнере:
Dockerfile:
```dockerfile=
FROM nginx
COPY content /var/www/html
COPY default.conf /etc/nginx/conf.d/
```
Docker build:
```bash
docker build -t block-viewer .
```
Для запуска контейнера (предварительно заменив `/path/to/content` на путь до директории `content`):
```bash
docker run -it -p 8080:80 -v /path/to/content:/var/www/html block-viewer
```
## Задание №2: Получение баланса на веб-странице
Разработайте веб-приложение, позволяющее показать текущий баланс пользователя.
1) Используйте `nginx` + `docker`
2) Баланс должен обновляться по нажатию на кнопку
3) Получение баланса: https://web3js.readthedocs.io/en/v1.2.1/web3-eth.html#getbalance
## Отправка транзакции
```javascript=
web3.eth.sendTransaction({
'to': '0x..', // адрес получателя
'value': 2077 // сумма в сатоши
})
```
Подробнее с методом `sendTransaction` можно познакомиться здесь: https://web3js.readthedocs.io/en/v1.2.1/web3-eth.html#sendtransaction
## Задание №3: Простые транзакции
Модифицируйте ваше веб-приложение из задания №2 таким образом, чтобы пользователь мог:
1) Указать адрес для перевода
2) Указать сумму перевода (в сатоши)
3) По нажатию кнопки отправить транзакцию
4) Баланс должен обновляться автоматически
Обменяйтесь адресами и попробуйте отправить транзакции друг другу.
# Solidity
Solidity - язык программирования, позволяющий разрабатывать смарт-контракты для Ethereum. Компилируется в байткод EVM (Ethereum Virtual Machine).
Документация: https://solidity.readthedocs.io
Пример кода на Solidity:
```javascript=
pragma solidity^0.5.1;
contract Storage {
int public value ;
address public owner;
constructor(int _value) public {
owner = msg.sender;
value = _value;
}
function setValue(int _value) public {
require(msg.sender == owner);
value = _value;
}
}
```
Данный контракт имеет функционал простого хранилища единственной числовой переменной `value`. При этом любой участник блокчейн сети может увидеть это значение, а менять его может только создатель контракта `owner`. Создателем считается адрес, создавший транзакцию на создание контракта в блокчейне.
Разберем код по частям:
```javascript=
pragma solidity^0.5.1;
```
Указание версии компилятора, в данном случае контракт скомпилируется любой версией solc от `0.5.1` до `6.0`.
```javascript=3
contract Storage {
int public value ;
address public owner;
```
Объявляем переменные состояния: целочисленное значение `value` и адрес `owner`.
Указываем их область видимости, в данном случае `public`, при этом геттеры будут сгенерированы автоматически.
```javascript=7
constructor(int _value) public {
owner = msg.sender;
value = _value;
}
```
`constructor` - это специальный метод, вызывающийся ровно один раз - при создании контракта. В нашем конструкторе будут инициализироваться значения `owner` и `value`.
Переменная `owner` примет значение адреса отправителя транзакции - `msg.sender` (т.е. адреса, который отправил транзакцию на создание контракта).
Переменная `value` будет иметь значение, которое создатель отправит в виде аргумента `_value` при создании контракта.
```javascript=12
function setValue(int _value) public {
```
Объявляем метод `setValue`, принимающий единственный аргумент `_value` - новое значение переменной `value`. Указываем область видимости `public`, чтобы метод можно было вызвать извне.
Прочитать больше про области видимости можно здесь: https://solidity.readthedocs.io/en/v0.5.3/contracts.html#visibility-and-getters
```javascript=13
require(msg.sender == owner);
value = _value;
```
Так как право на изменение переменной имеет только владелец (создатель) контракта, нужно проверить адрес отправителя транзакции. Для этого можно использовать функцию `require`, аргументом которой является булевое значение (`true` или `false`). Если `require` вызвать с аргументом `false`, транзакция не будет выполнена (будет отвергнута).
## Remix
[Remix](https://remix.ethereum.org) - это онлайн IDE, в которой можно разрабатывать, деплоить и тестировать смарт-контракты.
Выберите окружение `Solidity`. После этого, в окне компилятора можно отметить поле `Auto compile` для удобства.
Чтобы создать новый контракт, нажмите на `New File`:
![](https://i.imgur.com/ApXqi9I.png)
Далее введите название контракта с расширением `.sol`. Откроется окно редактора, где можно писать код на Solidity, а также окно компилятора.
Для деплоя (развертывания) контракта в блокчейне и его тестирования, в Remix есть вкладка `Deploy & run transactions`:
![](https://i.imgur.com/xFOJyEs.png)
## Задание №4: Деплой контракта через Remix и Metamask
1) Скопируйте и скомпилируйте контракт `Storage`, который мы разбирали ранее
2) С помощью Remix задеплойте контракт в тестовой сети Ropsten (выбрать провайдер `Injected Web3` можно в поле `Environment`)
3) Посмотрите на значение `owner` и `value`, попробуйте изменить значение `value`
## ABI и BIN контракта
**ABI** (Application Binary Interface) - это описание методов контракта в json формате. Это описание позволяет внешним программам (например: веб-сайту, использующем web3) правильно взаимодействовать со смарт-контрактом.
Пример ABI:
```json=
[
{
"constant": false,
"inputs": [
{
"internalType": "int256",
"name": "_value",
"type": "int256"
}
],
"name": "setValue",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int256",
"name": "_value",
"type": "int256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "value",
"outputs": [
{
"internalType": "int256",
"name": "",
"type": "int256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
```
Как мы видим, ABI показывает:
1. Типы и названия аргументов функции
2. Тип возвращаемых значений
3. Область видимости функции
4. Способность функции изменять состояние контракта
5. Способность функции принимать эфир
**BIN** - это байткод (bytecode), в котором закодированы инструкции для EVM.
Байткод контракта `Storage.sol`, который мы рассматривали ранее:
```
608060405234801561001057600080fd5b5060405161023d38038061023d8339818101604052602081101561003357600080fd5b810190808051906020019092919050505033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600081905550506101a18061009c6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80633fa4f245146100465780635093dc7d146100645780638da5cb5b14610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e2565b005b61009a610146565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60005481565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461013c57600080fd5b8060008190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea265627a7a723158207c33070c35f57ab226edc1a386a531f4cd873b74455a8e1809de33b5fd19392964736f6c634300050b0032
```
## Пример работы с контрактом через браузер
Рассмотрим пример работы с контрактом `Storage.sol` с помощью `web3`, встроенного в браузер пользователя:
```javascript=
abi = "ABI контракта";
address = "адрес контракта";
function callbackSet(err, txHash) {
if (err) {
console.error("Ошибка при отправке транзакции");
} else {
console.log("Хэш транзакции: " + txHash);
}
}
function callbackGet(err, value) {
if (err) {
console.error("Ошибка при получении значения");
} else {
alert("Значение: " + value);
}
}
// Инициализация контракта на определенном адресе
contract = web3.eth.contract(abi).at(address);
// Вызов функции, изменяющей состояние контракта
// в формате contract.methodName.sendTransaction(...args, callback)
contract.setValue.sendTransaction(10, callbackSet);
// Вызов функции, не изменяющей состояние контракта
// в формате contract.methodName.call(...args, callback)
contract.value.call(callbackGet);
```
## Задание №5: Простой контракт и веб-приложение
Разработайте смарт-контракт, позволяющий хранить эфир на его счету, и при определенной сумме на балансе контракта, позволяющий выводить их на адрес владельца контракта. Разработайте веб-приложение, позволяющее отправить на смарт-контракт средства, а также вывести их.
_Подсказка_: для отправки транзакции к контракту, нужно правильно указать количество газа, т.к. стандартное количество газа для обычной транзакции - 21000, а вызов метода контракта потребляет больше.
# Инструменты для разработки Dapp
- [Truffle](https://github.com/trufflesuite/truffle)
Фреймворк для создания и тестирования смарт-контрактов
- [Ganache](https://github.com/trufflesuite/ganache)
Позволяет развертывать локальный тестовый Ethereum блокчейн
- Infura.io
Иногда, при создании и тестировании децентрализованных приложений, удобно пользоваться сторонними API, предоставляющими Ethereum узлы, с которыми можно взаимодействовать с помощью JSON RPC. Это позволяет разработчикам не запускать и синхронизировать Etherem узел на своем компьютере.