Эксперименты с контрактами Solidity в тестовой сети Rinkeby блокчейна Ethereum



    Эта статья представляет собой небольшое практическое руководство, которое поможет вам быстро настроить среду разработки смарт-контрактов на языке Solidity для блокчейна Ethereum. Вы опубликуете первый контракт, сохраните его в тестовом блокчейне Rinkeby и научитесь вызывать методы контракта. Это будет ваш первый шаг на пути создания децентрализованных приложений DApp (Decentralized Application).

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

    При погружении в эту тему я использовал приложение (браузер) Mist в среде Microsoft Windows, а также интерфейс командной строки Geth узла Ethereum в среде Ubuntu. В этой статье мы расскажем о работе с Geth, а также немного о том, как вызывать методы контрактов из Node.js.

    С благодарностью приму замечания и пожелания по дальнейшим статьям про блокчейн Ethereum, разработку контрактов на языке Solidity и приложений DApp.

    Погружение в тему


    Прежде чем начинать создание смарт-контрактов, я рекомендую изучить технологию блокчейна (Blockchain). Например, для начала вы можете почитать статью Объяснение блокчейна для веб-разработчиков, опубликованную на Хабре.

    Ethereum — это децентрализованная платформа для разработки смарт-контрактов и создания так называемых децентрализованных приложений DApp. Сайт Ethereum находится по адресу https://www.ethereum.org/. Здесь вы найдете ссылки на основные ресурсы, которые будут вам полезны.

    Например, по адресу http://www.ethdocs.org/en/latest/ опубликована документация платформы.

    Руководство по языку Solidity находится здесь. Имеется перевод на русский язык, который, однако, может отставать от оригинальной документации.

    По ходу изложения материала мы будем делать ссылки на те или иные ресурсы.

    Установка узла Ethereum на виртуальной машине Ubuntu


    Для публикации контрактов и работы с Ethereum обычным пользователям компьютеров будет проще использовать браузер Mist с визуальным интерфейсом в среде Microsoft Windows. Однако разработчикам программного обеспечения (ПО) для понимания происходящего будет полезнее иметь дело с командной строкой.

    Мы установим Go Ethereum — одну из трех оригинальных реализаций протокола Ethereum, написанную на языке Go (еще есть реализации на С++ и Python).

    Go Ethereum можно установить на серверные платформы Ubuntu, Arch Linux и FreeBSD. Я выбрал Ubuntu, так как она похожа на Debian, а Debian мы применяем на нашем сервисе интернет-магазинов. Для эксперимента я установил ОС Ubuntu 17.10 на виртуальную машину под управлением VMWare Workstation.

    Срзу после установки Ubuntu нужно добавить возможность подключения через SSH. Дополнительно я установил редактор Vim:

    sudo apt-get install ssh
    sudo apt-get install vim
    

    Далее нужно установить стабильный релиз Go Ethereum:

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

    Полностью этот процесс описан здесь.

    Теперь нам нужно установить полный узел (full node) тестовой сети Rinkeby. Инструкция для узлов различного типа находится здесь: https://www.rinkeby.io/#geth. Нам нужен блок этой инструкции для полного узла.

    Прежде всего, копируем файл rinkeby.json в домашний каталог отсюда: https://www.rinkeby.io/rinkeby.json.

    Далее открываем первое консольное окно, создаем в домашнем каталоге папку rinkeby и запускаем команду:

    geth --datadir=/home/frolov/rinkeby init rinkeby.json
    

    Эта команда запускается только один раз при инициализации. Здесь предполагается, что данные узла будут храниться в каталоге /home/frolov/rinkeby, а файл rinkeby.json — в каталоге /home/frolov/. Разумеется, вы можете выбрать другое расположение каталогов.

    После завершения инициализации в этом же окне запускаем geth со следующими параметрами (вместо shop2you в параметре ethstats укажите какое-нибудь свое уникальное имя):

    geth --networkid=4 --datadir=/home/frolov/rinkeby --rpc --rpcaddr "0.0.0.0" --rpcapi "admin,debug,miner,shh,txpool,personal,eth,net,web3" --ethstats='shop2you:Respect my authoritah!@stats.rinkeby.io' --bootnodes=enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303
    

    Описание параметров команда geth вы можете найти по адресу https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options. Также можно посмотреть справку по параметрам geth с помощью команды:

    geth --help
    

    Параметр --networkid=4 указывает, что мы используем тестовую сеть Rinkeby. С помощью параметра --datadir мы задаем расположение каталога для хранения базы данных узла и ключей.

    Параметр --rpc разрешает работу сервера HTTP-RPC. Мы будем подключаться к нашему узлу из второго консольного окна через этот сервер. С помощью параметра --rpcaddr мы указываем, что сервер работает на всех сетевых интерфейсах узла (по умолчанию только на localhost). При помощи параметра --rpcapi мы указываем, какие API будут доступны через интерфейс HTTP-RPC.

    Параметр --ethstats указывает URL-адрес отчета службы ethstats и задается в формате nodename:secret@host:port. И, наконец, параметр --bootnodes задает URL-адреса для загрузки P2P (Comma separated enode URLs for P2P discovery bootstrap).

    Итак, мы запустили узел Go Ethereum для сети Rinkeby в первом консольном окне. Во втором консольном окне подключаемся к этому узлу с помощью такой команды:

    geth --networkid=4 --datadir=/home/frolov/rinkeby attach ipc://home/frolov/rinkeby/geth.ipc
    

    После подключения вы увидите командное приглашение утилиты geth. Введите в нем следующую команду:

    > web3.version
    {
      api: "0.20.1",
      ethereum: "0x3f",
      network: "4",
      node: "Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.1",
      whisper: undefined,
      getEthereum: function(callback),
      getNetwork: function(callback),
      getNode: function(callback),
      getWhisper: function(callback)
    }
    

    Эта команда покажет версию программного интерфейса Web3, с помощью которого мы будем работать с контрактами, версию geth, а также номер сети, который для Rinkeby равен 4.

    Синхронизация узла


    Прежде чем продолжить работу, необходимо дождаться окончания синхронизации узла. Запустите во втором окне в приглашении geth команду eth.syncing:

    > eth.syncing
    {
      currentBlock: 404158,
      highestBlock: 1402511,
      knownStates: 1974866,
      pulledStates: 1962021,
      startingBlock: 0
    }
    

    Так вы не запускаете процесс синхронизации (он должен запуститься автоматически), но, повторяя время от времени запуск команды, сможете отслеживать ход процесса. Когда синхронизация будет завершена, команда вернет false:

    > eth.syncing
    false
    

    Процесс синхронизации можно наблюдать в первом консольном окне, в котором мы запустили узел Go Ethereum:

    ...
    INFO [12-13|10:00:37] Imported new chain segment               blocks=1   txs=1   mgas=0.123  elapsed=21.839ms  mgasps=5.610   number=1408525 hash=bd6e8b…745ca3
    INFO [12-13|10:00:51] Imported new chain segment               blocks=1   txs=22  mgas=1.379  elapsed=188.490ms mgasps=7.317   number=1408526 hash=924b25…b292de
    INFO [12-13|10:00:51] Imported new chain segment               blocks=1   txs=22  mgas=1.379  elapsed=162.386ms mgasps=8.493   number=1408526 hash=c05478…70b24c
    INFO [12-13|10:00:51] Imported new chain segment               blocks=1   txs=22  mgas=1.379  elapsed=165.818ms mgasps=8.317   number=1408526 hash=8961b4…48d0a4
    INFO [12-13|10:01:07] Imported new chain segment               blocks=1   txs=2   mgas=0.063  elapsed=4.120ms   mgasps=15.347  number=1408527 hash=a5e658…22d300
    ...
    

    Заметим, что синхронизация начинается не мгновенно после запуска узел geth, и на ее завершение в сети Rinkeby может уйти достаточно продолжительное время, порядка нескольких десятков минут.

    Добавление аккаунтов


    Сразу после установки нового узла вам нужно добавить в него аккаунты (если у вас уже есть аккаунты на других узлах, их можно перенести на новый узел).

    Для добавления аккаунта введите в приглашении geth команду personal.newAccount():

    > personal.newAccount()
    Passphrase:
    Repeat passphrase:
    "0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3"
    

    Здесь вам нужно будет ввести пароль. При работе с настоящей, не тестовой сетью Ethereum, нужно особенно позаботиться о сохранении пароля. Лучше всего не записывать пароль на компьютер, даже на зашифрованный диск. Дело в том, что вирусы или троянские программы смогут похитить пароль с зашифрованного диска, т.к. когда вы работаете с диском, его содержимое становится доступным. Также существуют кей-логгеры, которые могут перехватить пароль при его вводе с клавиатуры.

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

    После ввода пароля на консоли появится адрес аккаунта, в нашем случае это «0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3» (у вас будет другой адрес). Заметим, что адреса созданных аккаунтов записывать не обязательно, т.к. их можно посмотреть при помощи команды eth.accounts:

    > eth.accounts
    ["0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3"]
    

    Вы можете добавить подобным образом несколько аккаунтов. В этом случае команда выведет массив идентификаторов для всех аккаунтов:

    > eth.accounts
    ["0xa15862b34abfc4b423fe52f153c95d83f606cc97", "0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3", "0x957f5272eadf4e284aade9b2cb2a41f475936d07"]
    

    Для каждого аккаунта создается приватный ключ. В нашем случае все ключи находятся в каталоге /home/frolov/rinkeby/keystore. Рекомендую сохранить эти ключи в безопасном месте, особенно если они относятся к аккаунтам основной, а не тестовой сети.

    Пополнение аккаунта эфиром


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

    > web3.fromWei( eth.getBalance(eth.coinbase) )
    0
    

    Этой команде также можно предавать идентификатор аккаунта:

    > web3.fromWei( eth.getBalance("0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3") )
    10.428648202959703389
    

    Чтобы пополнить кошелек в тестовой сети Rinkeby вам не потребуются реальные бумажные деньги или криптовалюта. Воспользуйтесь сайтом https://faucet.rinkeby.io/. Вам также потребуется аккаунт в Google+, Facebook или Twitter.

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

    Далее скопируйте адрес публикации в поле Social network URL containing your Ethereum address и выберите из списка Give me Ether одно из значений.

    Здесь вы можете получать 3 Ethers каждые 8 часов, 7.5 Ethers каждый день или 18.75 Ethers каждые три дня. Для начала работы вам вполне хватит 3 Ethers, так что можете выбирать первый вариант.

    Если вы все сделали правильно, через некоторое время «эфирчики» поступят на ваш аккаунт. Разумеется, эти средства вы сможете потратить только в тестовой сети Rinkeby.

    Контракт для публикации


    Теперь, когда вы все установили и проверили, пополнили свой аккаунт, можно приступать к публикации контракта. Мы будем работать с очень простым контрактом HelloSol. Вот его исходный текст на языке Solidity:

    pragma solidity ^0.4.10;
    
    contract HelloSol {
        string savedString;
        uint savedValue;
        function setString( string newString ) public {
            savedString = newString;
        }
        function getString() public constant returns( string ) {
            return savedString;
        }
        function setValue( uint newValue ) public {
            savedValue = newValue;
        }
        function getValue() public constant returns( uint ) {
            return savedValue;
        }
    }
    

    Этот контракт позволяет хранить текстовую строку и числовое значение. С помощью методов setString и getString можно, соответственно, записывать и читать строки. Аналогичные методы setValue и getValue предусмотрены для числовых значений.

    Я создал три тестовых аккаунта:

    > eth.accounts
    ["0xa15862b34abfc4b423fe52f153c95d83f606cc97", "0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3", "0x957f5272eadf4e284aade9b2cb2a41f475936d07"]
    

    Для публикации контракта я буду использовать последний из них с адресом 0x957f5272eadf4e284aade9b2cb2a41f475936d07. На этот адрес можно ссылаться следующим образом:

    > web3.eth.accounts[2]
    "0x957f5272eadf4e284aade9b2cb2a41f475936d07"
    

    Убедимся, что на аккаунте accounts[2] имеются средства:

    > web3.fromWei( eth.getBalance(web3.eth.accounts[2]));
    1.49157544347657552
    

    Перед публикацией контракта (а также перед вызовом методов контракта, изменяющих данные), необходимо разблокировать аккаунт. Это можно сделать при помощи метода personal.unlockAccount:

    > personal.unlockAccount(eth.accounts[2])
    Unlock account 0x957f5272eadf4e284aade9b2cb2a41f475936d07
    Passphrase:
    true
    

    У вас будет запрошен пароль, который вы задавали при создании аккаунта.

    Компиляция контракта


    Далее нам нужно создать для контракта так называемый Application Binary Interface (ABI), а также выполнить компиляцию исходного текста контракта, написанного на языке Solidity.

    Интерфейс ABI представляет собой спецификацию взаимодействия контракта с системой Ethereum, или, проще говоря, спецификацию параметров и возвращаемых значений методов контракта. Подробнее об этом можно почитать здесь.

    В результате компиляции исходного текста контракта, написанного на Solidity, должен получиться бинарный код программы, который будет выполнять виртуальные машины на всех узлах сети Ethereum. В нашем случае это будет только тестовая сеть Rinkeby.

    Интерфейс ABI и бинарный код программы проще всего получить при помощи браузерного инструмента IDE Remix, доступного по адресу https://remix.ethereum.org. Руководство по использованию Remix можно найти здесь.

    Откройте в браузере IDE Remix, и скопируйте код нашего контракта HelloSol в окно исходного текста, как это показано на рисунке ниже:



    По умолчанию код будет сразу же откомпилирован, т.к. установлен флажок Auto compile. Чтобы получить ABI и двоичный код, щелкните кнопку Details. Найдите в появившемся окне блок web3Deploy:



    Скопируйте отсюда блок кода при помощи кнопки Copy value to clipboard и вставьте этот текст в какой-нибудь текстовый редактор, например, в редактор Vim.

    Публикация контракта


    Для того чтобы опубликовать контракт, достаточно вставить из буфера обмена блок кода, скопированный из Remix, и нажать клавишу Enter.

    Перед тем как это сделать, я отредактировал строку from: web3.eth.accounts[0], заменив ее на from: web3.eth.accounts[2]. Это было сделано, чтобы выполнить публикацию от аккаунта web3.eth.accounts[2], на котором имеются средства. Если вы создали только один аккаунт и пополнили его эфиром, то ничего редактировать не нужно, просто вставьте в окно консоли в приглашении программы geth текст, скопированный из Remix:

    > var browser_ballot_sol_hellosolContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newValue","type":"uint256"}],"name":"setValue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newString","type":"string"}],"name":"setString","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getString","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]);
     undefined
    > var browser_ballot_sol_hellosol = browser_ballot_sol_hellosolContract.new(
    ...    {
    ......      from: web3.eth.accounts[2],
    ......      data: '0x6060604052341561000f57600080fd5b6103598061001e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461006757806355241077146100905780637fcaf666146100b357806389ea642f14610110575b600080fd5b341561007257600080fd5b61007a61019e565b6040518082815260200191505060405180910390f35b341561009b57600080fd5b6100b160048080359060200190919050506101a8565b005b34156100be57600080fd5b61010e600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101b2565b005b341561011b57600080fd5b6101236101cc565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610163578082015181840152602081019050610148565b50505050905090810190601f1680156101905780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000600154905090565b8060018190555050565b80600090805190602001906101c8929190610274565b5050565b6101d46102f4565b60008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561026a5780601f1061023f5761010080835404028352916020019161026a565b820191906000526020600020905b81548152906001019060200180831161024d57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102b557805160ff19168380011785556102e3565b828001600101855582156102e3579182015b828111156102e25782518255916020019190600101906102c7565b5b5090506102f09190610308565b5090565b602060405190810160405280600081525090565b61032a91905b8082111561032657600081600090555060010161030e565b5090565b905600a165627a7a7230582072dd1e3c0f509e8064320e887b19eb36fb41842f0897f41ce216a146c342d6140029',
    ......      gas: '4700000'
    ......    }, function (e, contract){
    ......     console.log(e, contract);
    ......     if (typeof contract.address !== 'undefined') {
    .........          console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
    .........     }
    ......  })
    

    Как только вы скопируете текст, то увидите в консоли следующее:

    null [object Object]
    undefined
    > null [object Object]
    

    Это нормально. Теперь нужно подождать, когда контракт опубликуется. Если все сделано правильно, через несколько минут вы увидите в консоли сообщение:

    Contract mined! address: 0x11c63c5ebc2c6851111d881cb58c213c609c92d4 transactionHash: 0xe79342277d7e95cedf0409e0887c2cddb3ebc5f0d952b9f7c1c1c5cef845cb97
    

    Теперь ваш контракт опубликован в сети Rinkeby, и ему присвоен адрес 0x11c63c5ebc2c6851111d881cb58c213c609c92d4. Пользуясь этим адресом, любой пользователь сети Rinkeby сможет обращаться к контракту, вызывая его методы.

    Вызов методов контракта


    Теперь займемся самым интересным — взаимодействием с контрактом. Мы будем вызывать его методы из консоли geth, используя API JavaScript Web3, описанный здесь. Заметим, что это документация на стабильную версию Web3 0.2x.x. Есть еще не реализованная версия 1.0, описанная по адресу http://web3js.readthedocs.io/en/1.0/index.html.

    Прежде всего, в консольном приглашении Geth введите следующую строку:

    var HelloSolContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newValue","type":"uint256"}],"name":"setValue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newString","type":"string"}],"name":"setString","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getString","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]);
    

    Здесь на основе ABI будет создан объект нашего контракта. Вызовем для него метод at, передав этому методу в качестве параметра адрес контракта 0x11c63c5ebc2c6851111d881cb58c213c609c92d4, полученный нами на шаге публикации контракта:

    var HelloSol = HelloSolContract.at("0x11c63c5ebc2c6851111d881cb58c213c609c92d4");
    

    Теперь можно вызывать методы контракта. Как и ожидается, метод getValue возвращает нулевое значение, т.к. мы еще не сохраняли в базе контракта никаких значений:

    > HelloSol.getValue()
    0
    

    Перед тем как вызывать метод setValue, нам необходимо разблокировать аккаунт при помощи метода unlockAccount:

    > personal.unlockAccount(eth.accounts[2])
    Unlock account 0x957f5272eadf4e284aade9b2cb2a41f475936d07
    Passphrase:
    true
    

    Если вы помните, ранее мы разблокировали аккаунт перед публикацией контракта.

    После того как аккаунт разблокирован, вызываем метод setValue:

    > HelloSol.setValue(777, {from: "0x957f5272eadf4e284aade9b2cb2a41f475936d07"})
    "0x019131872a3a834a1aaad7a810977eaaa1f9131344b9fb96e57d21a3f08c1363"
    

    Обратите внимание, что в качестве первого параметра мы передаем методу значение, которое должно быть сохранено, а в качестве второго — адрес аккаунта, от имени которого будет вызван этот метод.

    Теперь попытаемся получить значение методом getValue:

    > HelloSol.getValue()
    0
    > HelloSol.getValue()
    777
    

    Вначале этот метод будет возвращать нулевое значение, и только через некоторое время мы получим наше новое значение.

    Аналогичный эксперимент мы можем провести и с методами getString и setString, которые извлекают из базы контракта и изменяют в базе текстовую строку, соответственно:

    > HelloSol.getString()
    ""
    > personal.unlockAccount(eth.accounts[2])
    Unlock account 0x957f5272eadf4e284aade9b2cb2a41f475936d07
    Passphrase:
    true
    > HelloSol.setString("Моя строка", {from: "0x957f5272eadf4e284aade9b2cb2a41f475936d07"})
    "0x6a50d48a39d7c8e4d49fcb821a16305059d3064ff25d757bf2877d4aa0a29f31"
    > HelloSol.getString()
    ""
    > HelloSol.getString()
    ""
    > HelloSol.getString()
    "Моя строка"
    

    Установка пакетного компилятора sol


    Небольшие контракты очень удобно отлаживать при помощи IDE Remix. Однако есть и альтернатива — пакетный компилятор solc, который можно запускать в командной строке. Документация по этому компилятору находится здесь.

    Я использовал для установки solc следующую команду:

    sudo snap install solc
    

    После установки можно узнать версию компилятора:

    solc --version
    solc, the solidity compiler commandline interface
    Version: 0.4.18+commit.9cf6e910.Linux.g++
    

    Давайте выполним компиляцию нашего контракта HelloSol, получив для него файлы, содержащие ABI и двоичный код. Запишите исходный текст контракта в файл HelloSol.sol, а затем выполните следующую команду:

    solc --bin --abi HelloSol.sol -o build --overwrite
    

    После ее выполнения в подкаталоге build текущего каталога будут созданы два файла — HelloSol.abi и HelloSol.bin. Первый из них содержит ABI, а второй — двоичный код откомпилированного контракта.

    Для деплоя контракта можно использовать приведенный код из Remix, подставив в него результат компиляции — содержимое файлов HelloSol.abi и HelloSol.bin. При этом нужно проследить за тем, чтобы не было переводов строк внутри строк содержимого этих файлов.

    Вызов методов контракта из Node.js


    Для децентрализованных приложений DApp необходимо сделать пользовательский интерфейс. Можно связать обычное Web-приложение с системой контрактов, например, при помощи сервиса на базе Node.js. Давайте попробуем обратиться к методам нашего контракта через скрипт JavaScript, работающего на сервере под управлением Node.js.

    Прежде всего, установим Node.js:

    curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - 
    sudo apt-get install -y nodejs
    node -v
    v6.11.4
    

    Также нам потребуется npm:

    sudo apt install npm
    npm -v
    3.5.2
    

    На следующем этапе нам нужно будет установить git, а также API Web3, причем стабильной версии. По умолчанию устанавливается нестабильная версия 1.x, поэтому сделаем так:

    sudo apt-get install git
    npm uninstall web3
    npm install web3@0.20.1 --save
    

    Чтобы проверить, что у нас все получилось, запустите в консоли Node.js при помощи команды node. Из консольного приглашения node введите следующие команды:

    > var Web3 = require('web3')
    undefined
    > var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
    undefined
    > console.log(web3.eth.accounts);
    [ '0xa15862b34abfc4b423fe52f153c95d83f606cc97',
      '0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3',
      '0x957f5272eadf4e284aade9b2cb2a41f475936d07' ]
    

    Здесь мы подключились у узлу node и посмотрели список аккаунтов, созданных на этом узле.

    Вы также можете попробовать разблокировать аккаунт следующим образом:

    web3.personal.unlockAccount("0x957f5272eadf4e284aade9b2cb2a41f475936d07", "*********", 1000);
    true
    

    Вместо звездочек укажите свой пароль к аккаунту.

    Далее вы можете попробовать в приглашении Node.js подключиться к опубликованному ранее контракту и вызвать его методы:

    > var HelloSolContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newValue","type":"uint256"}],"name":"setValue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newString","type":"string"}],"name":"setString","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getString","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]);
    undefined
    > var HelloSol = HelloSolContract.at("0x11c63c5ebc2c6851111d881cb58c213c609c92d4");
    undefined
    > HelloSol.getValue()
    { [String: '777'] s: 1, e: 2, c: [ 777 ] }
    > HelloSol.getString()
    'Моя строка'
    

    Мы подготовили скрипт nodejs_web3_test.js, который можно запускать командой «node nodejs_web3_test.js» из консоли Ubuntu:

    var Web3 = require('web3')
    var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
    console.log(web3.eth.accounts);
    
    var HelloSolContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newValue","type":"uint256"}],"name":"setValue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newString","type":"string"}],"name":"setString","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getString","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]);
    
    var HelloSol = HelloSolContract.at("0x11c63c5ebc2c6851111d881cb58c213c609c92d4");
    
    console.log(HelloSol.getValue().toString(10));
    console.log(HelloSol.getString());
    
    web3.personal.unlockAccount("0xa15862b34abfc4b423fe52f153c95d83f606cc97", "*******", 1000);
    HelloSol.setString("Моя строка 999", {from: "0xa15862b34abfc4b423fe52f153c95d83f606cc97"});
    

    Этот скрипт подключается к узлу, затем выводит список аккаунтов. Далее он подключается к контракту 0x11c63c5ebc2c6851111d881cb58c213c609c92d4 и вызывает его методы.

    Вот что примерно вы увидите на консоли:

    node nodejs_web3_test.js
    [ '0xa15862b34abfc4b423fe52f153c95d83f606cc97',
      '0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3',
      '0x957f5272eadf4e284aade9b2cb2a41f475936d07' ]
    777
    Моя строка 123
    

    Заключение


    Затронутая мною тема смарт-контрактов слишком масштабна и охватить ее в одной статье невозможно. Тут потребуется очень толстая книга! Я не рассмотрел некоторые очень важные темы, например, экономику Ethereum, единицы работы Gas, фильтры и многое другое. Но у меня есть планы создания новых статей, а возможно, и книги. Мне хотелось бы знать, о чем нужно рассказать в первую очередь, так что я очень жду ваших откликов!

    Читайте также мои статьи:

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 20
    • +1
      Могу я в методах контракта использовать XmlHttp или обращения к файловой системе?
      • 0
        Контракты работают в полностью изолированной среде виртуальной машины. Они не могут обращаться к файловой системе или передавать данные с применением сетевых протоколов. Однако приложения DApp могут вызывать методы контрактов с помощью API Web3, как я это и делаю в примерах из своей статьи.
        • +2
          Можно поприставать с глупыми вопросами?

          1. Для того, чтобы поиграться с тестовой сетью, правда нужно заводить себе полную ноду? Какого-нибудь лайтового варианта нет? Или все лайтовые варианты — только про купить / продать / перевести?

          2. Насколько вообще тема смартконтрактов Эфириума реально популярна? Есть что-нибудь интересное в продуктиве помимо «Design and issue your own cryptocurrency, пообещай в вайтпейпере счастье неземное и проведи ICO»?

          3. Слышал, что за каждый чих в Эфириуме нужно платить звонкой монетой газа. Запомнил смарт-контракт байтик — выложи сто рубликов (или сколько?), помножил два числа — выложи рублик (или сколько?). Насколько вообще расценки кусачие? Если забабахать на этой штуке системку, которая будет держать базку на жалкие сто-двести мегабайт — не будет ли это удовольствие стоить как Аэробус-380?

          4. Есть где-нибудь в природе статистика по количеству народа, пользующегося Эфириумом? То, что пишут на coinmarketcap.com — это понятно, но сколько у системы юзеров? Хотя бы порядок величины кто-нибудь знает?
          • 0
            1. Меня интересовала именно полная нода и все ее возможности. Но поиграться можно с помощью браузера Mist с визуальным интерфейсом, установив его в Windows. Там можно публиковать контракты и вызывать их методы. Однако мне нужен такой вариант, который можно установить на сервере и интегрировать со своим Web сервисом.

            2. Не берусь оценивать популярность смарт-контрактов Эфириума, однако даже если просто погуглить, можно найти немало интересных проектов. Полагаю, популярность будет только расти.

            Меня интересует в первую очередь возможность использования Эфириума и смарт-контрактов в электронной коммерции, и как распределенного журнала, записи в котором невозможно подделать, и как платежного инструмента, позволяющего обойтись без традиционных посредников, таких как банки и процессинговые компании. Дело осталось только за проработкой законодательства.

            3. Экономику Эфириума я собираюсь исследовать детальнее. Но не стоит закачивать в распределенную базу сотни мегабайт или гигабайт, например, описаний и фотографий товаров, если без этого можно обойтись. Лучше всего хранить в распределенной базе только хэш функции от данных, а данные большого объема расположить где-нибудь еще, в общедоступном и надежном месте.

            4. На сайте https://etherscan.io/accounts написано: A total of more than > 1999999 accounts found (96,339,389.092 Ether). Но тут есть такой момент, что у одного пользователя может быть много кошельков (wallets).
            • +1
              1. Т.е. Mist не вкачивает на локалку всю беду целиком? Или всё же вкачивает?

              2. Кстати, да, CryptoKitties же :))

              3. Речь не о картинках и прочей ерунде. В принципе, сотни мегабайт в нормальной базе набираются и без них. Всего лишь жалкий миллион записей каждая по сто байт, и вот уже получите 100MB.

              4. Спасибо за наводку. На редкость любопытный сайт.
              То есть в эту игрушку играется примерно миллион человек. Да, проникновенье по планете пока что так себе. 0.01%. Не густо.

              Кстати, прикинул сразу среднюю TxFee по 50-ти последним транзакциям. Получилось примерно 100 рубликов. Жестковато дерут за одно обращение к инфосистеме.
              • 0
                Mist вкачивает, с ним вообще удобно работать. Но это инструмент для конечного пользователя, не компонент DApp.

                Насчет экономики Ethereum постараюсь написать отдельную статью, это важный момент. Да, затраты есть, но за счет значительного упрощения бизнес-процессов может будет и экономия. Банки и платежные системы тоже берут комиссию за перевод средств, да еще и деньги могут идти не один день…
                • 0
                  > и деньги могут идти не один день…
                  В сети эфира транзакции тоже могут идти не один день. Так ведь?
                  • 0
                    Думал, что это только для биткоинов так.
            • +1
              подпишусь под каждым пунктом
              • +1
                1. Копайте в сторону Truffle, он умеет все это«лайтово» поднимать.
                3. На каждую операцию, которая что-либо меняет (запись, изменение) нужно определенное количество газа. Но сам газ не является валютой, это коэффициент.
                • 0
                  Спасибо, Truffle попробую.
                  • 0
                    Truffle
                    Глянул. Похоже, это command line tool. Не, ребята, с такой юзабильностью сильно дальше 0.01% проникновения проползти не получится.

                    газ не является валютой
                    Тем не менее, из него высчитывается TxFee, которая уже в ETH.
                    • 0
                      «Consensys and Microsoft have announced that the Ethereum contract programming language Solidity will be available in Microsoft’s Visual Studio integrated development environment. » Это здесь.

                      А так мы конечно не избалованные, можем исходники и через Sublime поредактировать)
                      • 0
                        Можно сказать, что газ — это единица работы, и стоимость этой единицы меняется. Из контракта можно узнать, сколько газа нужно на публикацию контракта, на вызов того или иного метода, а также сколько стоит газ в данные момент времени.
                  • 0

                    консольки:
                    1) скачать geth
                    2) запустить geth
                    все.


                    Визуалки:
                    1) скачать mist/ethereum wallet
                    2) запустить ethereum wallet.
                    все.


                    Личное мнение про ethereum: ничего особенного, кроме как просто аццкой траблы с анонимностью, которой нет. Контракты круто, но потом бабки с контрактов надо перекидывать на аккаунт, адресс у которого фикс.


                    Имхо, или юзать биткоин и генерить адреса (=стабильная крипта), или юзать монеро (xmr), которая обеспечивает анонимность переводов.


                    З.Ы. я лично в крипты не верю, я верю в человеческую жадность и в то, что можно зарабатывать на тех, кто покупает/продает крипто.

                    • 0
                      На мой взгляд, тут фишка вовсе не в анонимности, а в том, что блокчейн обеспечивает способ хранения данных, при котором их невозможно подделать. А смарт-контракты позволяют исключить посредников из бизнеса, автоматизируя бизнес-процессы. На этом можно сэкономить. Так что жадность сработает)
                      • +1

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

                    • 0
                      Адрес, на который необходимо перевести деньги с контракта, можно вполне себе менять.
                      • +1

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

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.