
This article is a small practical guide that will help you quickly set up a smart contract development environment in the Solidity language for the Ethereum blockchain. You will publish the first contract, save it in the Rinkeby test blockchain, and learn how to call the contract methods. This will be your first step towards creating a decentralized DApp (DApp) application.
Despite the abundance of books, articles and manuals on the topic of this article, it is quite difficult for a beginner to start publishing contracts and working with them. When trying to do something through books, manuals and articles, it often happens that the examples do not work, and the teams return incomprehensible errors. I will try to simplify to some extent the first stage of development, reflecting in this article my experience of studying Ethereum.
')
When diving into this topic, I used the Mist application (browser) in the Microsoft Windows environment, as well as the Geth command line interface of Ethereum in Ubuntu. In this article we will talk about working with Geth, and also a little about how to call contract methods from Node.js.
With gratitude, I will accept comments and suggestions on further articles about the Ethereum blockchain, the development of contracts in the Solidity language and DApp applications.
Immersion in the subject
Before starting the creation of smart contracts, I recommend learning Blockchain technology (Blockchain). For example, for starters, you can read the article
Explanation of the blockchain for web developers , published on Habré.
Ethereum is a decentralized platform for developing smart contracts and creating so-called decentralized DApp applications. The Ethereum website is located at
https://www.ethereum.org/ . Here you will find links to key resources that will be useful to you.
For example, at
http://www.ethdocs.org/en/latest/, the platform documentation is published.
The Solidity Language Guide is
here . There is a
translation into Russian , which, however, may lag behind the original documentation.
In the course of the presentation of the material, we will make links to those or other resources.
Installing the Ethereum node in a Ubuntu virtual machine
For publishing contracts and working with Ethereum, ordinary computer users will find it easier to use the Mist browser with a visual interface in the Microsoft Windows environment. However, software developers (software) to understand what is happening will be more useful to deal with the command line.
We will install Go Ethereum - one of the three original implementations of the Ethereum protocol, written in the Go language (there are also C ++ and Python implementations).
Go Ethereum can be installed on Ubuntu, Arch Linux and FreeBSD server platforms. I chose Ubuntu because it is similar to Debian, and we use Debian on our online store service. For the experiment, I installed Ubuntu 17.10 OS on a VMWare Workstation virtual machine.
Once installed, after installing Ubuntu, you need to add the ability to connect via SSH. Additionally, I installed the Vim editor:
sudo apt-get install ssh sudo apt-get install vim
Next you need to install a stable release Go Ethereum:
sudo add-apt-repository -y ppa:ethereum/ethereum sudo apt-get update sudo apt-get install ethereum
Fully this process is described
here .
Now we need to install the full node of the Rinkeby test network. Instructions for nodes of various types are located here:
https://www.rinkeby.io/#geth . We need a block of this instruction for a complete node.
First of all, copy the rinkeby.json file to your home directory from here:
https://www.rinkeby.io/rinkeby.json .
Next, open the first console window, create the rinkeby folder in the home directory and run the command:
geth --datadir=/home/frolov/rinkeby init rinkeby.json
This command is run only once during initialization. Here it is assumed that the node data will be stored in the directory / home / frolov / rinkeby, and the file rinkeby.json - in the directory / home / frolov /. Of course, you can choose a different directory location.
After the initialization is completed, run geth with the following parameters in the same window (instead of shop2you, specify some unique name in the ethstats parameter):
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
The parameters description of the geth command can be found at
https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options . You can also see help on geth parameters with the command:
geth --help
The parameter --networkid = 4 indicates that we are using the Rinkeby test network. Using the --datadir parameter, we specify the location of the directory for storing the node database and keys.
The --rpc option enables the HTTP-RPC server. We will connect to our site from the second console window through this server. Using the --rpcaddr parameter, we indicate that the server is running on all network interfaces of the host (by default, only on localhost). Using the --rpcapi parameter, we specify which APIs will be available through the HTTP-RPC interface.
The --ethstats parameter specifies the URL of the ethstats service report and is in the format nodename: secret @ host: port. Finally, the --bootnodes parameter sets the URLs for P2P download (Comma separated enode URLs for P2P discovery bootstrap).
So, we run the Go Ethereum node for the Rinkeby network in the first console window. In the
second console window, connect to this node with the following command:
geth --networkid=4 --datadir=/home/frolov/rinkeby attach ipc://home/frolov/rinkeby/geth.ipc
After connecting, you will see a command prompt from the geth utility. Enter the following command in it:
> 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) }
This command will show the version of the Web3 software interface with which we will work with contracts, the geth version, and the network number, which is 4 for Rinkeby.
Node sync
Before proceeding, you must wait for the node to synchronize. Run the eth.syncing command in the geth prompt in the second window:
> eth.syncing { currentBlock: 404158, highestBlock: 1402511, knownStates: 1974866, pulledStates: 1962021, startingBlock: 0 }
So you do not start the synchronization process (it should start automatically), but by repeating the launch of the command from time to time, you will be able to track the progress of the process. When synchronization is complete, the command returns false:
> eth.syncing false
The synchronization process can be observed in the first console window in which we launched the Go Ethereum node:
... 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 ...
Note that the synchronization does not start immediately after the start of the geth node, and it can take quite a long time, on the order of several tens of minutes, to complete it on the Rinkeby network.
Add accounts
Immediately after installing a new node, you need to add accounts to it (if you already have accounts on other nodes, you can transfer them to a new node).
To add an account, enter the command personal.newAccount () at the geth invitation:
> personal.newAccount() Passphrase: Repeat passphrase: "0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3"
Here you will need to enter a password. When working with a real, non-test network Ethereum, you need to especially take care of saving the password. It is best not to write the password to a computer, even to an encrypted disk. The fact is that viruses or Trojans can steal a password from an encrypted disk, because when you work with a disk, its contents become available. There are also key loggers that can intercept the password when it is entered from the keyboard.
Password protects your private key. If an attacker can steal a password and private key from a computer, he will take possession of all the means that this account has. By the way, there is no way to recover a password, so you cannot lose it.
After entering the password, the account address will appear on the console, in our case it is “0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3” (you will have another address). Note that the addresses of the created accounts do not have to be recorded, because they can be viewed using the eth.accounts command:
> eth.accounts ["0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3"]
You can add multiple accounts in this way. In this case, the team will output an array of identifiers for all accounts:
> eth.accounts ["0xa15862b34abfc4b423fe52f153c95d83f606cc97", "0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3", "0x957f5272eadf4e284aade9b2cb2a41f475936d07"]
For each account, a private key is created. In our case, all the keys are in the directory / home / frolov / rinkeby / keystore. I recommend keeping these keys in a safe place, especially if they are related to the accounts of the main network, not the test network.
Top up account air
In order to be able to publish contracts and call their methods, it is necessary to replenish the wallet with ether (Ether). The current balance can be checked with the following command:
> web3.fromWei( eth.getBalance(eth.coinbase) ) 0
You can also give this account ID to this command:
> web3.fromWei( eth.getBalance("0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3") ) 10.428648202959703389
To replenish the wallet in the Rinkeby test network, you do not need real paper money or cryptocurrency. Use the site
https://faucet.rinkeby.io/ . You will also need a Google+, Facebook or Twitter account.
Post to one of the listed social networks containing the address of your account, such as 0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3 (you will have a different address).
Next, copy the publication address in the
Social network URL containing your Ethereum address field and select one of the values ​​from the
Give me Ether list.
Here you can receive 3 Ethers every 8 hours, 7.5 Ethers every day or 18.75 Ethers every three days. 3 Ethers is enough for you to get started, so you can choose the first option.
If you did everything right, after a while the “broadcasts” will go to your account. Of course, you can spend these funds only on the Rinkeby test network.
Contract for publication
Now that you have installed and checked everything, replenished your account, you can proceed to the publication of the contract. We will work with a very simple HelloSol contract. Here is its source code in the Solidity language:
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; } }
This contract allows you to store a text string and a numeric value. Using the setString and getString methods, you can respectively write and read strings. Similar methods setValue and getValue are provided for numeric values.
I created three test accounts:
> eth.accounts ["0xa15862b34abfc4b423fe52f153c95d83f606cc97", "0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3", "0x957f5272eadf4e284aade9b2cb2a41f475936d07"]
For the publication of the contract, I will use the last one with the address 0x957f5272eadf4e284aade9b2cb2a41f475936d07. This address can be referenced as follows:
> web3.eth.accounts[2] "0x957f5272eadf4e284aade9b2cb2a41f475936d07"
Make sure that there are funds in the accounts [2] account:
> web3.fromWei( eth.getBalance(web3.eth.accounts[2])); 1.49157544347657552
Before publishing the contract (and also before calling the contract methods that modify the data), you must unlock the account. This can be done using the personal.unlockAccount method:
> personal.unlockAccount(eth.accounts[2]) Unlock account 0x957f5272eadf4e284aade9b2cb2a41f475936d07 Passphrase: true
You will be asked for the password that you set when creating your account.
Contract compilation
Next, we need to create for the contract the so-called Application Binary Interface (ABI), as well as compile the source text of the contract written in the Solidity language.
The ABI interface is a specification of the interaction of the contract with the Ethereum system, or, more simply, the specification of the parameters and return values ​​of the contract methods. Read more about it
here .
As a result of the compilation of the source text of the contract written in Solidity, the binary code of the program should be obtained, which will run the virtual machines on all nodes of the Ethereum network. In our case, this will only be the Rinkeby test network.
The ABI interface and the binary code of the program are most easily obtained using the IDE Remix browser tool, available at
https://remix.ethereum.org . Remix manual can be found
here .
Open IDE Remix in your browser, and copy the code of our HelloSol contract into the source text window, as shown in the figure below:

By default, the code will be compiled immediately, since
Auto compile is checked. To get the ABI and binary code, click the
Details button. Find the
web3Deploy block in the
popup window:

Copy the code block from here using the
Copy value to clipboard button and paste this text into some text editor, for example, into the Vim editor.
Publication of the contract
In order to publish a contract, simply paste from the clipboard a block of code copied from Remix and press the Enter key.
Before doing this, I edited the string from: web3.eth.accounts [0], replacing it with from: web3.eth.accounts [2]. This was done to publish from the web3.eth.accounts account [2], where funds are available. If you have created only one account and replenished it with ether, then you do not need to edit anything, just paste the text copied from Remix into the console window at the geth program:
> 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: '', ...... gas: '4700000' ...... }, function (e, contract){ ...... console.log(e, contract); ...... if (typeof contract.address !== 'undefined') { ......... console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); ......... } ...... })
Once you copy the text, you will see the following in the console:
null [object Object] undefined > null [object Object]
This is normal. Now you need to wait for the contract to be published. If everything is done correctly, in a few minutes you will see a message in the console:
Contract mined! address: 0x11c63c5ebc2c6851111d881cb58c213c609c92d4 transactionHash: 0xe79342277d7e95cedf0409e0887c2cddb3ebc5f0d952b9f7c1c1c5cef845cb97
Your contract has now been published on the Rinkeby network, and is assigned the address 0x11c63c5ebc2c6851111d881cb58c213c609c92d4. Using this address, any user of the Rinkeby network can access the contract by calling its methods.
Calling contract methods
Now let's do the most interesting - the interaction with the contract. We will call its methods from the geth console using the Web3 JavaScript API described
here . Note that this is documentation for a stable version of Web3 0.2xx. There is still unrealized version 1.0, described at
http://web3js.readthedocs.io/en/1.0/index.html .
First of all, at the Geth console prompt, type the following line:
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"}]);
Here, on the basis of ABI, an object of our contract will be created. Let us call the at method for it, passing the contract address 0x11c63c5ebc2c6851111d881cb58c213c609c92d4 to this method, which we obtained at the publication step of the contract:
var HelloSol = HelloSolContract.at("0x11c63c5ebc2c6851111d881cb58c213c609c92d4");
Now you can call the methods of the contract. As expected, the getValue method returns a null value, since we have not yet stored any values ​​in the contract base:
> HelloSol.getValue() 0
Before calling the setValue method, we need to unlock the account using the unlockAccount method:
> personal.unlockAccount(eth.accounts[2]) Unlock account 0x957f5272eadf4e284aade9b2cb2a41f475936d07 Passphrase: true
If you remember, we previously unlocked an account before publishing a contract.
After the account is unlocked, we call the setValue method:
> HelloSol.setValue(777, {from: "0x957f5272eadf4e284aade9b2cb2a41f475936d07"}) "0x019131872a3a834a1aaad7a810977eaaa1f9131344b9fb96e57d21a3f08c1363"
Please note that as the first parameter we pass to the method the value that should be saved, and as the second - the address of the account on whose behalf this method will be called.
Now we will try to get the value by the getValue method:
> HelloSol.getValue() 0 > HelloSol.getValue() 777
Initially, this method will return a zero value, and only after some time will we get our new value.
We can conduct a similar experiment with the getString and setString methods, which retrieve a text string from the contract base and change the text string in the database, respectively:
> HelloSol.getString() "" > personal.unlockAccount(eth.accounts[2]) Unlock account 0x957f5272eadf4e284aade9b2cb2a41f475936d07 Passphrase: true > HelloSol.setString(" ", {from: "0x957f5272eadf4e284aade9b2cb2a41f475936d07"}) "0x6a50d48a39d7c8e4d49fcb821a16305059d3064ff25d757bf2877d4aa0a29f31" > HelloSol.getString() "" > HelloSol.getString() "" > HelloSol.getString() " "
Installing the sol package compiler
Small contracts are very convenient to debug using IDE Remix. However, there is an alternative - the solc batch compiler, which can be run on the command line. Documentation for this compiler is
here .
I used the following command to install solc:
sudo snap install solc
After installation, you can find out the version of the compiler:
solc --version solc, the solidity compiler commandline interface Version: 0.4.18+commit.9cf6e910.Linux.g++
Let's compile our HelloSol contract by getting files for it containing ABI and binary code. Write the contract source to the HelloSol.sol file, and then run the following command:
solc --bin --abi HelloSol.sol -o build --overwrite
After its execution, two files will be created in the build subdirectory of the current directory - HelloSol.abi and HelloSol.bin. The first one contains ABI, and the second one contains the binary code of the compiled contract.
To deploy a contract, you can use the above code from Remix, substituting in it the result of the compilation - the contents of the HelloSol.abi and HelloSol.bin files. At the same time, it is necessary to ensure that there are no line breaks within the lines of the contents of these files.
Calling contract methods from Node.js
For decentralized DApp applications, you need to create a user interface. You can associate a regular Web application with a contract system, for example, using a Node.js-based service. Let's try to access the methods of our contract through a JavaScript script running on a server running Node.js.
First of all, install 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
We also need npm:
sudo apt install npm npm -v 3.5.2
In the next step, we will need to install git, as well as the Web3 API, with a stable version. The unstable version 1.x is installed by default, so let's do it like this:
sudo apt-get install git npm uninstall web3 npm install web3@0.20.1 --save
To check that everything worked out for us, run Node.js in the console using the node command. From the console node prompt, enter the following commands:
> 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' ]
Here we connected to the node node and looked at the list of accounts created on this node.
You can also try to unlock your account as follows:
web3.personal.unlockAccount("0x957f5272eadf4e284aade9b2cb2a41f475936d07", "*********", 1000); true
Instead of asterisks, enter your account password.
Then you can try in the Node.js invitation to connect to the previously published contract and call its methods:
> 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() ' '
We have prepared the nodejs_web3_test.js script, which can be run using the “node nodejs_web3_test.js” command from the Ubuntu console:
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"});
This script connects to the site, then displays a list of accounts. Next, he connects to contract 0x11c63c5ebc2c6851111d881cb58c213c609c92d4 and calls his methods.
This is what you will see on the console:
node nodejs_web3_test.js [ '0xa15862b34abfc4b423fe52f153c95d83f606cc97', '0x7d9006e7f24bd6d90dd8cc63764ab0b92b77d9b3', '0x957f5272eadf4e284aade9b2cb2a41f475936d07' ] 777 123
Conclusion
The topic of smart contracts I have touched upon is too large and it is impossible to cover it in one article. It will require a very thick book! I didn’t cover some very important topics, such as the Ethereum economy, Gas units of work, filters, and more. But I have plans to create new articles, and possibly books. I would like to know what needs to be told first of all, so I’m really looking forward to your feedback!
Read also my articles: