- PVSM.RU - https://www.pvsm.ru -

Dive into Ethereum

Сегодня платформа Ethereum стала одним из самых узнаваемых брендов блокчейн сферы, вплотную приблизившись по популярности (и капитализации) к Bitcoin. Но из-за отсутствия "полноценного" рускоязычного гайда, отечественные разработчики все еще не очень понимают, что это за зверь и как с ним работать. Поэтому в данной статье я попытался максимально подробно охватить все аспекты разработки умных контрактов под Ethereum.

Я расскажу про инструменты разработки, сам ЯП, процесс добавления UI и еще много интересного. В конечном итоге мы получим обычный сайт-визитку, но "под капотом" он будет работать на умных контрактах Ethereum. Кого заинтересовало — прошу под кат.

preview

Содержание

Введение в Ethereum

Эта статья не расчитана на тех, кто совсем не знаком с Ethereum (или технологией блокчейн вообще), поэтому объяснений базовых вещей вроде блоков, транзакций или контрактов здесь не будет. Я подразумеваю, что вы хотя бы чуть-чуть в курсе происходящего. В противном случае полистайте статьи из списка ниже, а потом возвращайтесь :)

Больше ссылок на интересные статьи вы найдете в конце.

P.S. Я работаю под Ubuntu 16.04, так что весь процесс установки, разработки и деплоя будет описан под эту ОС. Тем не менее все используемые инструменты кроссплатформенны (скорее всего, не проверял), так что при желании можете поэкспериментировать на других ОС.

Инструменты

Geth [35]

Работа с Ethereum возможна через огромное число клиентов, часть из которых terminal-based, часть GUI и есть несколько гибридных решений. Своего рода стандартом является [Geth](), который разрабатывается командой Ethereum. Про него я уже писал в предыдущих статьях [27], но на всякий случай повторюсь.

Клиент написан на Go, устанавливается стандартным способом [36]:

sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

Сам Geth не имеет GUI, но работать с ним из терминала довольно приятно. Здесь [37] описан весь набор аргументов командной строки, я же опишу несколько самых популярных.

Вот команда, которую я чаще всего использую в работе: $ geth --dev --rpc --rpcaddr "0.0.0.0" --rpcapi "admin,debug,miner,shh,txpool,personal,eth,net,web3" console

  • --dev запускает geth в режиме приватного блокчейна, то есть не синхронизирет основную / тестовую ветку. Вместо этого вы получаете стерильную цепочку без единого блока. Это самый удобный вариант в плане разработки, так как, например, майнинг блока занимает несколько секунд и нет никакой нагрузки на сеть или диск.

  • --rpc включает RPC-HTTP сервер. По сути это API к вашей ноде — через него сторонние приложения, вроде кошельков или IDE, смогут работать с блокчейном: загружать контракты, отправлять транзакции и так далее. По дефолту запускается на localhost:8545 [38], можете изменить эти параметры с помощью --rpcaddr и --rpcport соответственно.
  • --rpcapi устанавливает что-то вроде прав доступа для приложений, подключенных к RPC серверу. Например, если вы не укажете "miner", то, подключив к ноде кошелек и запустив майнер, вы получите ошибку. В примере я указал все возможные права, подробнее можете почитать здесь [39].
  • console — как можно догадаться, эта опция запускает консоль разработчика. Она поддерживает самый обычный JS и ряд встроенных функций для работы с Ethereum, вот простой пример [27] (пункт — Поднимаем ноду).

Parity [40]

Geth довольно хорош, но в последнее время все чаще можно встретить другой клиент — Parity, написанный на Rust. Главным его отличием от Geth является встроенный web интерфейс, на мой взгляд, самый удобный среди всех ныне существующих. Установка:

sudo <(curl https://get.parity.io -Lk)

По окончании загрузки запустите в консоли parity и по адресу localhost:8180 [41] можете найти сам кошелек.

parity

Еще один плюс: Parity быстрее своих конкурентов. По крайней мере так утверждают авторы, но по моим ощущениям это действительно так, особенно в плане синхронизации блокчейна.

Единственный нюанс — своей консоли в parity нет. Но можно без проблем использовать для этих целей Geth:

$ parity --geth # Run parity in Geth mode
$ geth attach console # Attach Geth to the PArity node (Do it in another window)

TestRPC [42]

Этот инструмент, в отличие от предыдущих, будет полезен только разработчикам. Он позволяет одной командой testrpc поднять приватный блокчейн с включенным RPC протоколом, десятком заранее созданных аккаунтов с этерами на счету, работающим майнером и так далее. Весь список здесь [43]. По сути, testrpc — это тот же geth --dev --rpc ..., только на этот раз не надо тратить время на создание аккаунтов, включение / выключение майнера и прочие рутинные действия.

Установка — npm install -g ethereumjs-testrpc.

testrpc

Mist [44]

Самый популярный кошелек для Ethereum, хотя на самом деле он умеет намного больше. Вот отличная статья [45], где step-by-step объясняется весь процесс работы с Mist. Скачать самую свежую версию можно со страницы релизов [46]. Помимо работы с кошельком, есть возможность работы с контрактами.

mist

Remix [47]

Самая популярная IDE для разработки контрактов. Работает в браузере по адресу ethereum.github.io/browser-solidity/ [48], поддерживает огромное число функций:

  • Подключение к указанному RPC провайдеру
  • Компиляция кода в байткод / опкоды
  • Публикация в Github gist
  • Пошаговый дебагер
  • Подсчет стоимости исполнения функций в газе
  • Сохранение вашего кода в localstorage
  • И многое другое [49]

При этом нет автокомплита, что очень печально.

remix

Cosmo [50]

Еще одна IDE для разработки умных контрактов, написана на Meteor, работает из коробки. Для начала откройте новый терминал и поднимите ноду с включенным RPC интерфесом geth --rpc --rpcapi="db,eth,net,web3,personal" --rpcport "8545" --rpcaddr "127.0.0.1" --rpccorsdomain "localhost" console. После этого можете запускать саму IDE:

$ git clone http://github.com/SilentCicero/meteor-dapp-cosmo.git
$ cd meteor-dapp-cosmo/app
$ meteor

Далее открываете localhost:3000 [51] и можете начинать работать:

cosmo_screenshot

Etheratom [52]

Последний на сегодня инструмент для ускорения разработки умных контрактов. Это плагин для редактора Atom, устанавливается с помощью apm install atom-ethereum-interface. Штука удобная, сам пользуюсь. Позволяет работать c JS EVM или подключиться к ноде через RPC. Компилирует контракт на CTRL + ALT + C, деплоит в сеть на CTRL + ALT + S. Ну и предоставляет неплохой интерфейс для работы с самим контрактом.

atom_ethereum

Если вам не нужен такой навороченный функционал внутри редактора, то для Atom есть отдельный плагин с подсветкой синтаксиса Solidity — language-ethereum [53]. Последний по сути является плагином под Sublime text [54], только конвертированный для работы в Atom.

Solidity

Возможно, вы слышали про то, что можно писать контракты не только на Solidity, но и на других языках, например Serpent [55] (внешне напоминает Python). Но последний комит в develop ветке ethereum/serpent [56] был примерно полгода назад, так что, по-видимому, язык, увы, deprecated.

Поэтому писать будем только на Solidity. Пока что язык находится на относительно раннем этапе развития, так что никаких сложных конструкций или уникальных абстракций в нем нет. Поэтому отдельно рассказывать про него я не вижу смысла — любой человек с опытом в программировании сможет свободно писать на нем после 20 минут чтения документации [57]. На случай, если у вас такого опыта нет, — ниже я довольно подробно прокомментировал весь код контракта.

Для самостоятельного обучения есть несколько очень хороших примеров с максимально подробными описаниями:

Еще раз отмечу (отличную!) документацию [57] языка, местами [61] даже переведена на русский язык.

Создаем контракт-визитку

Самое время создать наш контракт. В конечном итоге это будет приложение-визитка, на которую мы поместим само "резюме":

  • Имя, почта, контакты и так далее
  • Список проектов
  • Образование: вузы, курсы и тд
  • Навыки
  • Публикации

Первый шаг

Первым делом создадим шаблон контракта и функцию-конструктор. Она должна называться также как и сам контракт и вызывается лишь однажды — при загрузке контракта в блокчейн. Мы будем использовать ее для инициализации одной единственной переменной — address owner. Как вы уже наверное догадались, в нее будет записан адрес того, кто залил контракт в сеть. А использоваться она будет для реализации функций администратора контракта, но об этом позже.

pragma solidity ^0.4.0;

contract EthereumCV is Structures {
    address owner;

    // =====================
    // ==== CONSTRUCTOR ====
    // =====================
    function EthereumCV() {
        owner = msg.sender;
    }
}

Базовая информация

Следующим шагом добавим возможность указывать базовую информацию об авторе — имя, почту, адрес и так далее. Для этого будем использовать самый обычный mapping, который нужно объявить в начало контракта:

address owner;
mapping (string => string) basic_data;

Для того, чтобы иметь возможность "получать" от контракта эти данные, создадим следующую функцию:

function getBasicData (string arg) constant returns (string) {
    return basic_data[arg];
}

Здесь все просто, стоит только отметить модификатор constant — его можно (и нужно) использовать для тех функций, которые не изменяют state приложения. Главный плюс таких функций (sic!), в том что их можно использовать как обычные функции.

Администрирование

Теперь стоит задуматься о наполнении своего резюме контентом. В самом простом случае мы могли бы обойтись функцией вроде

function setBasicData (string key, string value) {
    basic_data[key] = value;
}

Но в этом случае любой при желании смог бы изменить, например, наше имя, вызвав setBasicData("name", "New Name"). К счастью, есть способ всего в одну строку пресечь любые такие попытки:

function setBasicData (string key, string value) {
    if (msg.sender != owner) { throw; }
    basic_data[key] = value;
}

Так как нам еще не раз придется использовать подобную конструкцию (при добавлении нового проекта, например), то стоит создать специальный модификатор:

modifier onlyOwner() {
    if (msg.sender != owner) { throw; }
    _; // Will be replaced with function body
}

// Now you can use it with any function
function setBasicData (string key, string value) onlyOwner() {
    basic_data[key] = value;
}

При желании, можно использовать другие способы авторизации, например по паролю. Хэш будет храниться в контракте и сравниваться с введенным при каждом вызове функции. Но понятно, что этот способ не такой безопасный, благо радужные таблицы и атаки по словарю никто не отменял. С другой стороны, наш способ тоже не идеален, так как если вы потеряете доступ к адресу owner, то ничего редактировать вы уже не сможете.

Модульность

Следующим шагом создадим несколько структур для описания проектов, образования, навыков и публикаций. Здесь все просто, структуры описываются точно так же как в Си. Но вместо того, чтобы описывать их в текущем контракте, вынесем их в отдельную блиблиотеку (в новом файле). Тем самым мы сможем избежать огромных простыней кода и структурировать наш проект.

Для этого в той же директории создадим новый файл structures.sol и библиотеку Structures. А уже внутри нее опишем каждую из структур:

pragma solidity ^0.4.0;

library Structures {
    struct Project {
        string name;
        string link;
        string description;
    }

    struct Education {
        string name;
        string speciality;
        int32 year_start;
        int32 year_finish;
    }

    struct Publication {
        string name;
        string link;
        string language;
    }

    struct Skill {
        string name;
        int32 level;
    }
}

Теперь осталось только импортировать полученный файл

pragma solidity ^0.4.0;

import "./structures.sol";

contract EthereumCV {
    mapping (string => string) basic_data;
    address owner;

    Structures.Project[] public projects;
    Structures.Education[] public educations;
    Structures.Skill[] public skills;
    Structures.Publication[] public publications;

    // ...
}

Самые сообразительные уже догадались, что нотация Structures.Project[] projects означает создание динамического массива с элеметнами типа Project. А вот с модификатором public уже сложнее. По сути, он заменяет нам написание функции вроде get_project(int position) { return projects[position]; } — компилятор сам создаст такую функцию. Называться она будет так же как и переменная, в нашем случае — projects.

Вы можете спросить — почему мы в самом начале не написали mapping (string => string) public basic_data, а вместо этого сами создавали такую функцию? Причина банальна — public пока что не умеет работать c переменными, для которых ключом является динамический тип данных (string именно такой тип).

Unimplemented feature (/src/libsolidity/codegen/ExpressionCompiler.cpp:105): Accessors for mapping with dynamically-sized keys not yet implemented.

Для этого нужно объявлять basic_data как например mapping (bytes32 => string).

BTW На всякий случай отмечу, что кроме локального файла, Remix умеет импортировать .sol файлы по ссылке на Github и даже с помощью протокола Swarm [62] (это что-то вроде распределенного хранилища для Ethereum, подробнее здесь [63])

Загружаем и удаляем данные

Думаю многие из вас уже сами догадались, как стоит реализовать работу с новыми данными. Покажу на примере списка публикаций, в остальных случаях все аналогично:

function editPublication (bool operation, string name, string link, string language) onlyOwner() {
    if (operation) {
        publications.push(Structures.Publication(name, link, language));
    } else {
        delete publications[publications.length - 1];
    }
}

С помощью параметра operation мы избавились от написания отдельной функции для удаления последней публикации (костыльно, но мы ведь только учимся). Хотя нужно отметить, что такой способ избавления от элемента в массиве на самом деле не совсем корректный. Сам элемент конечно будет удален, но на месте индекса останется пустое место. В нашем случае это не смертельно (мы будем проверять пустоту отдельных элементов на стороне клиента), но, вообще говоря, про это не стоит забывать. Тем более что сдвинуть весь массив и уменьшить счетчик длины не так уж сложно [64].

Отдаем данные

Как я уже сказал, модификатор public в строке Project[] public projects обеспечил нас функцией которая по индексу i вернет проект projects[i]. Но мы не знаем, сколько у нас всего проектов, и здесь есть два пути. Первый — итерироваться по i до того момента, пока мы не получим ошибку о несуществующем элементе. Второй — написать отдельную функцию, которая вернет нам размер projects. Я пойду вторым путем, чуть позже скажу почему:

function getSize(string arg) constant returns (uint) {
    if (sha3(arg) == sha3("projects")) { return projects.length; }
    if (sha3(arg) == sha3("educations")) { return educations.length; }
    if (sha3(arg) == sha3("publications")) { return quotes.length; }
    if (sha3(arg) == sha3("skills")) { return skills.length; }
    throw;
}

Заметьте, что мы не можем сравнить две строки привычным способом 'aaa' == 'bbb'. Причина все та же, string — это динамический тип данных, работа с ними довольно болезненна. Так что остается либо сравнивать хэши, либо использовать функцию для посимвольного сравнения. В этом случае можете использовать популярную библиотеку stringUtils.sol [65], в ней есть такая функция.

Деплой

В разных средах разработки процесс компиляции и деплоя разумеется отличается, поэтому я ограничусь Remix, как самым популярным.

Сначала, само собой, заливаем весь код (финальную версию можете найти в репозитории проекта [66]). Далее в выпадающем списке Select execution environment выберите Javascript VM — пока что протестируем контракт на JS эмуляторе блокчейна, чуть позже научимся работать и с настоящим. Если с контрактом все в порядке, то вам будет доступна кнопка Create — нажимаем и видим:

remix_create

Теперь, когда контракт залит в блокчейн (его эмуляцию, но не суть), можем попробовать вызвать какую-нибудь функцию и посмотреть, что из этого выйдет. Например можно сохранить в контракте email — для этого найдите функцию setBasicData, заполните поле и нажмите кнопку с именем функции:

remix_set_basic_data

Функция ничего не возвращает, поэтому result: 0x. Теперь можно запросить у контракта email: ищем функцию getBasicData и пробуем:

remix_get_basic_data

С остальными функциями предлагаю вам поэксперементировать самим.

Добавляем UI

Ниже я расскажу про самый распостраненный способ добавить UI к вашему контракту. Он позволяет с помощью JS и HTML создавать интерфейсы любой сложности, достаточно иметь доступ к рабочей ноде Ethereum (или ее аналогам).

Web3.js [67]

This is the Ethereum compatible JavaScript API [68] which implements the Generic JSON RPC [69] spec. It's available on npm as a node module, for bower and component as an embeddable js and as a meteor.js package.

Это JS библиотека, позовляющая использовать API Ethereum с помощью обычного JS. По сути с ее помощью вы просто подключаетесь ноде и у вас появляется что-то вроде консоли geth в браузере. Устанавливается через npm или bower:

$ sudo npm install web3
$ bower install web3

Вот пример работы с web3 через node.js (предварительно запустите testrpcили любую другую ноду с RPC интерфейсом):

$ node
> var Web3 = require('web3');
> var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
> web3.eth.accounts
[ '0x5f7aaf2199f95e1b991cb7961c49be5df1050d86',
  '0x1c0131b72fa0f67ac9c46c5f4bd8fa483d7553c3',
  '0x10de59faaea051b7ea889011a2d8a560a75805a7',
  '0x56e71613ff0fb6a9486555325dc6bec8e6a88c78',
  '0x40155a39d232a0bdb98ee9f721340197af3170c5',
  '0x4b9f184b2527a3605ec8d62dca22edb4b240bbda',
  '0x117a6be09f6e5fbbd373f7f460c8a74a0800c92c',
  '0x111f9a2920cbf81e4236225fcbe17c8b329bacd7',
  '0x01b4bfbca90cbfad6d6d2a80ee9540645c7bd55a',
  '0x71be5d7d2a53597ef73d90fd558df23c37f3aac1' ]
>

Тоже самое, только из JS консоли браузера (не забудьте про <script src="path_to/web3.js"></script>)

browser_js_web3

То есть мы уже на этом моменте можем запустить ноду, синхронизировать ее с текущей цепочкой и останется только сверстать наше приложение. Но тут есть два тонких момента: во-первых, вам нужно синхронизировать блокчейн Ethereum, а вы этого скорее всего до сих пор не сделали.

Второй нюанс — RPC не имеет никакого встроенного механизма авторизации, поэтому любой желающий может узнать адрес вашей ноды из исходников JS и пользоваться ей в свое удовольствие. Тут конечно можно писать какую-нибудь обертку на Nginx с простейшей HTTP basic auth, но это как-нибудь в другой раз.

Metamask [70]

Поэтому сейчас мы воспользуемся плагином Metamask (увы, только для Chrome). По сути это и есть та прослойка между нодой и браузером, которая позволит вам использовать web3 в браузере, но без своей ноды. Metamask работает очень просто — в каждую страницу он встраивает web3.js, который автоматически подключается к RPC серверам Metamask. После этого вы можете использовать Ethereum на полную катушку.

После установки плагина, в левом верхнем углу выберите Testnet и получите несколько эфиров на кране Metamask [71]. На этом моменте вы должны получить что-то вроде такого (с чистой историей разумеется):

metamask_ready

Deploy with Metamask

С Metamask задеплоить контракт в сеть так же просто, как и в случаем с JS EVM. Для этого снова открываем Remix и в списке Select execution environment выбираем пункт Injected Web3 (скорее всего он выбран автоматически). После этого нажимаем Create и видим всплывающее окно:

metamask_popup

Чуть позже надпись Waiting for transaction to be mined... сменится на информацию об опубликованном контракте — это значит что он попал в блокчейн. Адрес контракта можете узнать, открыв Metamask и нажав на запись вида:

metamask_info

Однако теперь, если вы захотите, например, вызвать функцию editProject(...), то вам так же придется подтвержать транзакцию и ждать, пока она будет замайнена в блок.

Пример

Теперь дело за малым — надо научиться получать данные от контракта через Web3. Для этого, во-первых, надо научиться определять наличие web3 на странице:

window.addEventListener('load', function() {
  // Checking if Web3 has been injected by the browser (Mist/MetaMask)
  if (typeof web3 !== 'undefined') {
    // Use Mist/MetaMask's provider
    console.log("Web3 detected!");
    window.web3 = new Web3(web3.currentProvider);
    // Now you can start your app & access web3 freely:
    startApp()
  } else {
    alert('Please use Chrome, install Metamask and then try again!')
  }
})

Внутри startApp() я определелил всю логику работы с контрактом, тем самым избегая ложных срабатываний и ошибок.

function startApp() {
  var address = {
    "3" : "0xf11398265f766b8941549c865d948ae0ac734561" // Ropsten
  }

  var current_network = web3.version.network;
  // abi initialized ealier, in abi.js
  var contract = web3.eth.contract(abi).at(address[current_network]);

  console.log("Contract initialized successfully")

  contract.getBasicData("name", function(error, data) {
    console.log(data);
  });

  contract.getBasicData("email", function(error, data) {
    console.log(data);
  });

  contract.getSize("skills", function(error, data) {
    var skills_size = data["c"][0];
    for (var i = 0; i < skills_size; ++i) {
      contract.skills(i, function(error, data) {
        // Don't forget to check blank elements!
        if (data[0]) { console.log(data[0], data[1]["c"][0]); }
      })
    }
  })
}

js_logs

Итог

Теперь, когда вы со всем разобрались, можно браться за верстку и JS. Я использовал Vue.js [72] и Spectre.css [73], для визуализации навыков добавил Google Charts [74]. Результат можете увидеть на pavlovdog.github.io [75]:

cv

Вместо заключения

Только что вы увидели, как можно довольно быстро создать приложение, которое самым непосредственным образом использует технологию blockchain. Хотя в погоне за простотой (все таки это обучающая статья) я допустили некоторые упрощения, которые по-хорошему допускать нельзя.

Например, мы используем чей-то шлюз (я про Metamask), вместо того, чтобы работать со своей нодой. Это удобно, но технология блокчейн в первую очередь — децентрализация и отсутствие посредников. У нас же всего этого нет — мы доверяем парням из Metamask.

Другая, не такая критичная проблема, — мы забыли про стоимость деплоя контрактов и транзакций к ним. На практике, стоит десять раз подумать, прежде чем использовать string вместо bytes, потому как такие вещи прежде всего влияют на затраты при работе с контрактом. Опять же, в примере я использовал Testnet, так что никаких денег мы не потратили, но при работе с Main net не стоит быть такими расточительными.

В любом случае, я надеюсь что статья оказалась полезной, если есть вопросы — задавайте в комментариях или пишите мне на почту.

Ссылки

Автор: Сергей

Источник [83]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/253981

Ссылки в тексте:

[1] Введение в Ethereum: #vvedenie-v-ethereum

[2] Инструменты: #instrumenty

[3] Geth: #gethhttpsethereumgithubiogo-ethereum

[4] Parity: #parityhttpsparityio

[5] TestRPC: #testrpchttpsgithubcomethereumjstestrpc

[6] Mist: #misthttpsgithubcomethereummist

[7] Remix: #remixhttpsethereumgithubiobrowser-solidity

[8] Cosmo: #cosmohttpsgithubcomcosmo-projectmeteor-dapp-cosmo

[9] Etheratom: #etheratomhttpsgitlabcom0mkaraetheratom

[10] Solidity: #solidity

[11] Создаем контракт-визитку: #sozdaem-kontrakt-vizitku

[12] Первый шаг: #pervyy-shag

[13] Базовая информация: #bazovaya-informaciya

[14] Администрирование: #administrirovanie

[15] Модульность: #modulnost

[16] Загружаем и удаляем данные: #zagruzhaem-i-udalyaem-dannye

[17] Отдаем данные: #otdaem-dannye

[18] Деплой: #deploy

[19] Добавляем UI: #dobavlyaem-ui

[20] Web3.js: #web3jshttpsgithubcomethereumweb3js

[21] Metamask: #metamaskhttpsmetamaskio

[22] Deploy with Metamask: #deploy-with-metamask

[23] Пример: #primer

[24] Итог: #itog

[25] Вместо заключения: #vmesto-zaklyucheniya

[26] Ссылки: #ssylki

[27] RU — Пишем умный контракт на Solidity. Часть 1 — установка и «Hello world»: https://habrahabr.ru/post/312008/

[28] RU — Malware + Blockchain = ️: https://habrahabr.ru/post/313710/

[29] RU — Как мы делали первую сделку-аккредитив на блокчейн в Альфа-Банке: https://habrahabr.ru/company/alfa/blog/323070/

[30] EN — A 101 Noob Intro to Programming Smart Contracts on Ethereum: http://consensys.github.io/developers/articles/101-noob-intro/

[31] EN — Ethereum for web developers: https://medium.com/@mvmurthy/ethereum-for-web-developers-890be23d1d0c

[32] EN — Building a smart contract using the command line: https://www.ethereum.org/greeter

[33] EN — Create your own crypto-currency: https://www.ethereum.org/token

[34] EN — Dapps for Beginners: https://dappsforbeginners.wordpress.com/

[35] Geth: https://ethereum.github.io/go-ethereum/

[36] стандартным способом: https://www.ethereum.org/cli

[37] Здесь: https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options

[38] localhost:8545: http://localhost:8545

[39] здесь: https://github.com/ethereum/go-ethereum/wiki/Management-APIs

[40] Parity: https://parity.io/

[41] localhost:8180: http://localhost:8180

[42] TestRPC: https://github.com/ethereumjs/testrpc

[43] здесь: https://github.com/ethereumjs/testrpc#usage

[44] Mist: https://github.com/ethereum/mist

[45] отличная статья: https://medium.com/@attores/step-by-step-guide-getting-started-with-ethereum-mist-wallet-772a3cc99af4

[46] страницы релизов: https://github.com/ethereum/mist/releases

[47] Remix: https://ethereum.github.io/browser-solidity/

[48] ethereum.github.io/browser-solidity/: http://ethereum.github.io/browser-solidity/

[49] многое другое: https://github.com/ethereum/remix

[50] Cosmo: https://github.com/cosmo-project/meteor-dapp-cosmo

[51] localhost:3000: http://localhost:3000

[52] Etheratom: https://gitlab.com/0mkara/etheratom

[53] language-ethereum: https://atom.io/packages/language-ethereum

[54] плагином под Sublime text: https://packagecontrol.io/packages/Ethereum

[55] Serpent: https://github.com/ethereum/wiki/wiki/Serpent

[56] ethereum/serpent: https://github.com/ethereum/serpent/tree/develop

[57] документации: https://solidity.readthedocs.io/en/develop/

[58] Voting contract: http://solidity.readthedocs.io/en/develop/solidity-by-example.html#voting

[59] Blind Auction: http://solidity.readthedocs.io/en/develop/solidity-by-example.html#blind-auction

[60] Safe Remote Purchase: http://solidity.readthedocs.io/en/develop/solidity-by-example.html#safe-remote-purchase

[61] местами: https://github.com/ethereum/wiki/wiki/%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity

[62] с помощью протокола Swarm: https://www.reddit.com/r/ethereum/comments/5vpm53/remix_can_now_import_files_via_swarm/

[63] здесь: http://ethereum.stackexchange.com/questions/375/what-is-swarm-and-what-is-it-used-for

[64] не так уж сложно: http://ethereum.stackexchange.com/questions/1527/how-to-delete-an-element-at-a-certain-index-in-an-array

[65] stringUtils.sol: https://github.com/ethereum/dapp-bin/blob/master/library/stringUtils.sol

[66] репозитории проекта: https://github.com/pavlovdog/dive_into_ethereum

[67] Web3.js: https://github.com/ethereum/web3.js

[68] JavaScript API: https://github.com/ethereum/wiki/wiki/JavaScript-API

[69] Generic JSON RPC: https://github.com/ethereum/wiki/wiki/JSON-RPC

[70] Metamask: https://metamask.io/

[71] кране Metamask: https://faucet.metamask.io/

[72] Vue.js: https://vuejs.org

[73] Spectre.css: https://picturepan2.github.io/spectre/

[74] Google Charts: https://developers.google.com/chart/

[75] pavlovdog.github.io: https://pavlovdog.github.io/

[76] MetaMask: https://medium.com/metamask/metamask-ff7d3571f331

[77] Learning Solidity Part 1: Contract Dev with MetaMask: https://karl.tech/learning-solidity-part-1-deploy-a-contract/

[78] Learning Solidity Part 2: Commit-Reveal Voting: https://karl.tech/learning-solidity-part-2-voting/

[79] How to use MetaMask: https://www.cryptocompare.com/wallets/guides/how-to-use-metamask/

[80] Building an “Oracle” for an Ethereum contract: https://medium.com/@mustwin/building-an-oracle-for-an-ethereum-contract-6096d3e39551

[81] Full Stack Hello World Voting Ethereum Dapp Tutorial — Part 1: https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-1-40d2d0d807c2

[82] Full Stack Hello World Voting Ethereum Dapp Tutorial — Part 2: https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-2-30b3d335aa1f

[83] Источник: https://habrahabr.ru/post/327236/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best