📜 ⬆️ ⬇️

Dive into Ethereum development. Part 4: warmth and debug in truffle, ganache, infura

In the last article we looked at the developed application on Ethereum. But bypassed the question of how the development process. Clearly, this is not just writing code that works right away. Most of the time will have to spend on bringing the code, which is “almost ready”, to a working state. Depla, testing, debugging - all this has been touched upon to some extent here, for example, in these quite good articles: one , two , three (the list is not complete). In this article we will give a small overview and perhaps we will repeat something, but we will try to focus on those points that seemed important or unsaid to us. Plus, lately, some things have changed, and a huge number of instructions turned out to be outdated. We will try to correct the situation a little.



Project in truffle


For testing and deploying smart contracts, we use Truffle , it hides some of the low-level work behind the abstractions, which is very convenient. The described version is 4.0.6. This is not the only framework, Embark and Dapple are still there , but we cannot say anything about them, we did not have to work. To initialize the project, you need to execute the command (it will be executed in the current folder, so first create a project folder and navigate to it):

$ truffle init 

Only the base structure is created from the contracts , migrations and tests folders. In contracts and migrations you can see the smart contract Migrations , which is responsible for the deployment logic (in terms of truffle - migration). And the logic is something like this: in the migrations folder you add scripts and name them by the pattern 1_description.js , 2_another_one.js ... n_etc.js . The most important thing in the title is the index that goes at the beginning, after which you can add any description that is needed only for readability. The index is used to perform migrations in the order of numbering. Smart contract Migrations used to save which of the migration scripts have already been executed. So, if in the process of development we add new contracts and a new deployment logic, then the previous successful progress should not be redone. Personally, we do not use it, instead we have a fixed number of migrations, edit and run them again each time with the help of truffle migration --reset .
You can manually create contract, migration, and test files, but there are special commands in the truffle:
')
 $ truffle create contract ExampleContract $ truffle create migration ExampleMigration $ truffle create test ExampleContract 

You can check that you have created the appropriate files. But the content is very basic, so while in this feature there is no particular advantage compared to manually creating the same files.

Work with the project


In earlier versions, the initialization of truffle init immediately created a small example (Metacoin and ConvertLib). In order to see this and other examples in recent versions, you can use features called Truffle Boxes. Boxes are designed to get a full-fledged example of a Truffle project and its interaction with various web-based tools, such as ReactJS. Here is a list of boxes, both official and created by the community. Create a directory for the new project and navigate to it. Then execute the command:

 $ truffle unbox metacoin 

get the project structure, which in the old versions was created by default after truffle init . This is an example with the basic token Metacoin, which can be sent from user to user and with the help of the ConvertLib library you can watch the balance in the air at a fixed exchange rate. In addition, it shows how to create and use a smart contract and library, there is also an example of tests for JavaScript and Solidity (for more information on writing tests, read here , note that there is just considered an older version of Truffle). Let's take a quick look at how to build and test this project in test mode. First, start the development console:

 $ truffle develop 

See similar text:

 Truffle Develop started at http://localhost:9545/ Accounts: (0) 0x627306090abab3a6e1400e9345bc60c78a8bef57 (1) 0xf17f52151ebef6c7334fad080c5704d77216b732 (2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef (3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544 (4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2 (5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e (6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5 (7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5 (8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc (9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat truffle(develop)> 

What does this team do? It raises the test environment and gives access to it through the console. The test environment is what you could see in older manuals called TestRPC. In fact, this is it, just the Truffle team took it under its control and renamed it to Ganache . But let's write about this further, but for now let's move on to the console. Perform a full cycle of commands for compiling, migrating, and testing:

 truffle(develop)> compile 

Result
 Compiling ./contracts/ConvertLib.sol... Compiling ./contracts/MetaCoin.sol... Compiling ./contracts/Migrations.sol... Writing artifacts to ./build/contracts 

 truffle(develop)> migrate 

Result
 Using network 'develop'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x29619f8ba9b9e001bef885c8ca2fbee45beab738adc41c7f9e2e8273fbc67e9f Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0 Saving successful migration to network... ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying ConvertLib... ... 0x02318651545ac96670af626ef7795cb928d7504afc3f856258058ce579d47fe6 ConvertLib: 0x345ca3e014aaf5dca488057592ee47305d9b3e10 Linking ConvertLib to MetaCoin Deploying MetaCoin... ... 0x486c572bbb2df30bb166f5507423d394807b5b92041860968a7d5eb162e42e48 MetaCoin: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf Saving successful migration to network... ... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db Saving artifacts... 

 truffle(develop)> test 

Result
 Using network 'develop'. Compiling ./contracts/ConvertLib.sol... Compiling ./contracts/MetaCoin.sol... Compiling ./test/TestMetacoin.sol... Compiling truffle/Assert.sol... Compiling truffle/DeployedAddresses.sol... TestMetacoin âś“ testInitialBalanceUsingDeployedContract (62ms) âś“ testInitialBalanceWithNewMetaCoin (47ms) Contract: MetaCoin âś“ should put 10000 MetaCoin in the first account âś“ should call a function that depends on a linked library (54ms) âś“ should send coin correctly (117ms) 5 passing (796ms) 


It is possible to invoke methods of secured contracts manually, for example:

 truffle(develop)> var metaCoin truffle(develop)> MetaCoin.deployed().then( function(instance) { metaCoin = instance } ); truffle(develop)> metaCoin.getBalance(web3.eth.coinbase) BigNumber { s: 1, e: 4, c: [ 10000 ] } truffle(develop)> _.toNumber() 10000 truffle(develop)> metaCoin.sendCoin( web3.eth.accounts[2], 3000 ) { tx: '0x9f59085a9f22c0bd691b890370bcffd7eedce1327a3bb525a2de3edf9db0d279', receipt: { transactionHash: '0x9f59085a9f22c0bd691b890370bcffd7eedce1327a3bb525a2de3edf9db0d279', transactionIndex: 0, blockHash: '0x24e8913b6f707bb5e5acbaa054fef9dabd548a561dc988763209f0aeed9a57b5', blockNumber: 12, gasUsed: 51024, cumulativeGasUsed: 51024, contractAddress: null, logs: [ [Object] ] }, logs: [ { logIndex: 0, transactionIndex: 0, transactionHash: '0x9f59085a9f22c0bd691b890370bcffd7eedce1327a3bb525a2de3edf9db0d279', blockHash: '0x24e8913b6f707bb5e5acbaa054fef9dabd548a561dc988763209f0aeed9a57b5', blockNumber: 12, address: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf', type: 'mined', event: 'Transfer', args: [Object] } ] } truffle(develop)> metaCoin.getBalance(web3.eth.coinbase) BigNumber { s: 1, e: 3, c: [ 7000 ] } truffle(develop)> _.toNumber() 7000 

As you can see everything is done instantly and you can track the changes: you sent 3000 from the main address to another, we see that the balance has decreased. Exit the console with the command:

 truffle(develop)> .exit 

Connection to node


Truffle development is a mode with its own test node. To connect to a real network or testnet, the truffle console command is used, which is completely analogous to develop , but does not raise the test environment. To demonstrate this, it is not necessary to run geth , for example, you can use the already mentioned Ganache , which replaced TestRPC. Run the command:

 $ ganache-cli 

There is also a GUI version . There is no fundamental difference, but in this version you can immediately see all the information, events and balances. It can be very convenient in some situations. You can use any of these versions.

But if you just made a truffle unbox metacoin and haven’t changed the configuration files yet, you won’t be able to connect

 $ truffle console No network available. Use `truffle develop` or add network to truffle.js config. 

Therefore, add the following to truffle.js :

 module.exports = { networks: { development: { host: "localhost", port: 8545, network_id: "*" } } }; 

You can also see truffle-config.js . This is the same, but for Windows. Extra file for your system can be deleted.
This config will allow the truffle to connect to any network accessible on localhost: 8545. These are the default values ​​for geth and ganache-cli. If you are using the GUI version of Ganache, go to the settings and, if necessary, change the port and restart (the “save and restart” button).


If you are stuck on a window with a logo, then most likely you have something running on the same port, geth, ganache-cli / testrpc or something else

Now you can connect, run the command:

 $ truffle console 

And you can try for example to do:

 truffle(development)> migrate --reset 

In the GUI version, you can immediately see the change in the balance of the account from which it was deployed, if you go to the blocks tab, you will see smined blocks and how much gas was spent on transactions in them. Clicking on each of them can get even more information. The transactions tab in the same way will show you all past transactions.
Many screenshots







In the console version will be the same information, but in the form of a wall of logs:
Expand
 net_version eth_accounts eth_accounts net_version net_version eth_sendTransaction Transaction: 0x29619f8ba9b9e001bef885c8ca2fbee45beab738adc41c7f9e2e8273fbc67e9f Contract created: 0x922194d35a507e5905fa4f2c9e7172ee8535272a Gas usage: 269607 Block Number: 1 Block Time: Mon Feb 05 2018 10:28:17 GMT+0300 (MSK) eth_newBlockFilter eth_getFilterChanges eth_getTransactionReceipt eth_getCode eth_uninstallFilter eth_sendTransaction Transaction: 0xfafc4352cc1dde57e46954d7ebd3a59232599081a253dd8705847a380ae5b06b Gas usage: 41981 Block Number: 2 Block Time: Mon Feb 05 2018 10:28:17 GMT+0300 (MSK) eth_getTransactionReceipt eth_accounts net_version net_version eth_sendTransaction 


Use what you think is more convenient.
By the way, if you read other instructions on the same topic, you probably already know that it is not necessary to call the console to compile and deploy, you can simply call for example

 truffle migrate --reset 

And it will be a complete analogue of what we have done above. The same principle for all other teams of truffles. Full list of commands here .

Packages


In any language you will not manage only core-functionality. Inventing bicycles is unproductive and dangerous, and with smart contracts, where you risk other people's money, this is especially critical. Therefore, the question arises where to get this already proven additional functionality. Truffle has a system of packages for this, which are available in two installation options (aside from the usual copy-paste): using npm and using ethpm.

Prior to this, using the example of Metacoin, we saw the simplest token. Tokens are used quite often, even Crypto Kitties are essentially tokens, although the original standard. The main standard for tokens right now is ERC20 . To comply with the standard, a token must implement a set of functions that can provide universal and safe use in wallets, exchanges, etc. The zeppelin-solidity package from OpenZeppelin turns out to be very useful here - a set of libraries for frequently used patterns in smart contracts. In this article, for example, the use of this package has already been described. Consider not use, and methods of installation and connection. For starters, the one described in the instructions on the project github. At the root of the truffle project:

 $ npm init -y $ npm install -E zeppelin-solidity 

After that, in the node_modules folder, you will have zeppelin-solidity , from which you can connect the required files in the smart contract, for example, ownable, with the line

 import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; 

But there is another way to install packages, which is designed specifically for packages in Ethereum: EthPM. Packages are stored on IPFS. The list of packages is available here . It is integrated into the truffle and to install the same zeppelin-solidity you can run:

 $ truffle install zeppelin 

The installed_contracts folder is added, its contents are connected in the same way as from the node_modules folder:

 import 'zeppelin/contracts/ownership/Ownable.sol'; 

But if you compare the versions that are installed one way or the other, you will find that they are different. And at the very moment, the npm version is newer (at the time of writing 1.6.0 versus 1.3.0 in EthPm). So EthPM is ideologically even more interesting, but for now it's probably better to install packages using npm

Debugging


Usually when an error occurs during the execution of a smart contract, the messages are very uninformative. For example, let's make a contract with an intentional error:

 $ mkdir FaultyContract && cd FaultyContract $ truffle init $ truffle create contract FaultyContract $ truffle create migration deploy 

In the contracts / FaultyContract.sol file add the missing code:

 pragma solidity ^0.4.4; contract FaultyContract { int public result; function divideXbyY( int x, int y ) public { y -= y; result = x/y; } } 

As you see, division by 0 is inevitable.

In the migration/xxxx_deploy.js (xxxx - generated id, can be different) add the missing code for the deployment:

 var FaultyContract = artifacts.require("./FaultyContract.sol"); module.exports = function( deployer ) { deployer.deploy( FaultyContract ); } 

Open the develop console, build and contract our contract.

 $ truffle develop truffle(develop)> migrate 

Get a contract and call the problematic function:

 truffle(develop)> var faultyContract; truffle(develop)> FaultyContract.deployed().then( function(instance) { faultyContract = instance } ); truffle(develop)> faultyContract.divideXbyY(16, 4); Error: VM Exception while processing transaction: invalid opcode at Object.InvalidResponse (/usr/lib/node_modules/truffle/build/cli.bundled.js:43303:16) at /usr/lib/node_modules/truffle/build/cli.bundled.js:331156:36 at /usr/lib/node_modules/truffle/build/cli.bundled.js:314196:9 at XMLHttpRequest.request.onreadystatechange (/usr/lib/node_modules/truffle/build/cli.bundled.js:329855:7) at XMLHttpRequestEventTarget.dispatchEvent (/usr/lib/node_modules/truffle/build/cli.bundled.js:70159:18) at XMLHttpRequest._setReadyState (/usr/lib/node_modules/truffle/build/cli.bundled.js:70449:12) at XMLHttpRequest._onHttpResponseEnd (/usr/lib/node_modules/truffle/build/cli.bundled.js:70604:12) 

As you can see, it is not very informative, it is not known where the error is and what the invalid opcode means. In Truffle, the debug command is available from version 4 (beta), allowing the transaction to be re-executed line by line. But for this you need to get a transaction hash, and there is not even an error in it. To see the hash, run another instance of truffle develop with the --log flag:

 $ truffle develop --log 

This command allows you to see the logs of what is happening in the main develop-console, and you can also find the transaction hash there. Run the function again

 truffle(develop)> faultyContract.divideXbyY(16, 4); 

In the window with the log will be something like

 develop:testrpc eth_sendTransaction +0ms develop:testrpc +27ms develop:testrpc Transaction: 0x21073e12e7c8fb785347d7bd5d974d4954379dcace7b53d452c03b39ca007b9e +1ms develop:testrpc Gas usage: 6721975 +0ms develop:testrpc Block Number: 6 +0ms develop:testrpc Block Time: Tue Feb 06 2018 10:32:27 GMT+0300 (MSK) +0ms develop:testrpc Runtime Error: invalid opcode +0ms develop:testrpc +0ms 

Take the transaction hash and pass it to the debug command:

 truffle(develop)> debug 0x21073e12e7c8fb785347d7bd5d974d4954379dcace7b53d452c03b39ca007b9e 

You should get hints for further commands. To understand at least in which line of the error, you can use the command n (step next):

 debug(develop:0x21073e12...)> n FaultyContract.sol | 0x345ca3e014aaf5dca488057592ee47305d9b3e10: 8: int public result; 9: 10: function divideXbyY( int x, int y ) public { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ debug(develop:0x21073e12...)> 

Each step will display several lines from the code and underline the part that is currently being executed. It is possible to go to a very low level and execute each opcode in turn (commands from the Ethereum virtual machine) and watch the stack status. We take one more step and try to display the stack state:

 debug(develop:0x21073e12...)> FaultyContract.sol | 0x345ca3e014aaf5dca488057592ee47305d9b3e10: 9: 10: function divideXbyY( int x, int y ) public { 11: y -= y; ^ debug(develop:0x21073e12...)> p FaultyContract.sol | 0x345ca3e014aaf5dca488057592ee47305d9b3e10: (55) DUP1 00000000000000000000000000000000000000000000000000000000c6329782 000000000000000000000000000000000000000000000000000000000000009b 0000000000000000000000000000000000000000000000000000000000000010 0000000000000000000000000000000000000000000000000000000000000004 (top) 

We see that the last ones in the stack are our x and y, 16 and 4. This is certainly not a very convenient way and you need to deal with opcodes and how they are executed in the Ethereum virtual machine. If interested - you can look for example yellow paper (H.2. Instruction Set). But our task is simply to find a line with an error. Continue to perform the next step until we get something like this:

 10: function divideXbyY( int x, int y ) public { 11: y -= y; 12: result = x/y; ^^^ debug(develop:0x21073e12...)> Transaction halted with a RUNTIME ERROR. This is likely due to an intentional halting expression, like assert(), require() or revert(). It can also be due to out-of-gas exceptions. Please inspect your transaction parameters and contract code to determine the meaning of this error. truffle(develop)> 

Here at least you can see that the error occurred somewhere in the division part. And you can look at the stack and instructions, if you understand. This is unfortunately all that offers truffle on the part of debag. Well, at least something.

In this regard, for single contracts and simple connections, we can advise Remix IDE (in this article, for example, the author uses it for deployment), there is almost a full-fledged debug with the ability to see the values ​​of variables at each step. The interface is intuitive, see how the example already discussed can be debugged there:


In the console below we see an error, click debag:


And we can scroll back and forth and see the values ​​of variables in the human form.

Remote nodes Infura


In the last article, we looked at the Metamask plugin, which allows you to connect to the blockchain without using a local synchronized Ethereum client. This is possible thanks to the Infura service. You can also access the infura nodes and connect to them through a truffle. To do this, first you need to register on their website , the letter will contain links with personal tokens for access. Let's try to close the Metacoin example on Ropsten without a local node.
Create a test project as usual:

 $ mkdir metacoin && cd metacoin $ truffle unbox metacoin 

Next, we need an additional HDWalletProvider package, through which Truffle can sign transactions

 $ npm init $ npm install $ npm install truffle-hdwallet-provider 

Add the provider to the truffle settings with the following code:

 var HDWalletProvider = require("truffle-hdwallet-provider"); var mnemonic = "correct horse battery staple correct horse battery staple correct horse battery staple" module.exports = { networks: { development: { host: "localhost", port: 8545, network_id: "*" }, ropsten: { provider: function() { return new HDWalletProvider(mnemonic, "https://ropsten.infura.io/<   >") }, network_id: 3, gas: 4000000, gasPrice: 21000000000 } } }; 

We had to set gas and gasPrice, because at least our default values ​​did not fit. Do not forget to insert the token from the letter, but also invent your mnemonic from 12 different (and not like in the example) words - it is used to generate accounts and if someone takes possession of it, you can generate the same accounts and use them without your knowledge. . For example, if you try to use the mnemonic in this article, you can use the ether (0.3) that we have sent there (if someone else does not use it). In this config is also left for development , you can choose between these two networks by launching a truffle with the appropriate name after the --network flag:

  $ truffle console --network ropsten 

Before calling migrate , you need to replenish the balance of the generated account. Find out the address and balance commands:

 truffle(ropsten)> web3.eth.getAccounts( function(e,r) { console.log(r[0]); } ); undefined 0x0bb542704819b5e6a28deb2b73245be57ce0e78b truffle(ropsten)> web3.eth.getBalance('0x0bb542704819b5e6a28deb2b73245be57ce0e78b', function(e, r) { console.log( web3.fromWei( r.toNumber() ) ); }) undefined 0 

Send to your account a little Ropsten-air in order to be able to pay for the deployment. After it has arrived (you can check with the previous command), you can try migrate :

 truffle(ropsten)> migrate 

The result should be:

 Compiling ./contracts/ConvertLib.sol... Compiling ./contracts/MetaCoin.sol... Compiling ./contracts/Migrations.sol... Writing artifacts to ./build/contracts Using network 'ropsten'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x93cf7dbde8c362534dc912926fc4d7df54c9c1f5e0a7dcfd964a0177b42bc7be Migrations: 0x02519d13f61bdcad838d938611e6722c3d1f8034 Saving successful migration to network... ... 0xec501a78cc11c723ab60186167765aa7c422177153cd72a976e66441db2b5b95 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying ConvertLib... ... 0xf172d9a9ff9f1fdfdfabc816d89f5a5e710ba26e3a2ad9e1661c9dea56564f04 ConvertLib: 0xd04bffb73bf546985938a596565141d3a3bf7f0d Linking ConvertLib to MetaCoin Deploying MetaCoin... ... 0x4d9814f1d9a959e83828bf26319dd91d73be977395d88e9e8239bb4c4ed5b0eb MetaCoin: 0x20fd16643d857ce544a91ae4c80385af99dad196 Saving successful migration to network... ... 0x0dff866460d24d56d94dcf5f833aa4fa8ae289cb708ff5c9012ce21447575ce8 Saving artifacts... truffle(ropsten)> 

We see the transaction hash, you can check that they really hit Ropsten via etherscan.io (for example ropsten.etherscan.io/tx/0x93cf7dbde8c362534dc912926fc4d7df54c9c1f5e0a7dcfd964a0177b42bc7be )

What's next?


We hope you learned something new from this article, or at least refreshed knowledge.
As for the next article, as practice has shown, it is difficult to make a real useful project without communication with the outside world through Oraclize and IPFS. About this and plan to write.

Ethereum development:
Part 1: introduction
Part 2: Web3.js and gas
Part 3: user application

Source: https://habr.com/ru/post/348656/


All Articles