How we sawed IoT payments on the hackathon in Hong Kong


June 10 was the third day of our acclimatization in Hong Kong. And the last 26 hours we spent almost without sleep, developing a prototype of the project under the working title SensorPay at the first stage of the EOS Global hakaton with a total prize fund of one and a half million dollars. The project demonstration time was near to the judges.


If you can not wait to find out how this story ended, just take a look at the last part. For the time being, we will start systematically talking about EOS technologies and how we came to the idea of ​​linking payments for IoT to EOS. Immediately after this there will be a detailed description of the technical stuffing of the project


0. Background


EOS is a blockchain of a new generation, some even consider it an Ethereum killer. If suddenly you do not know what a blockchain or Ethereum is, Google will help you. And we, so it turned out, started digging up EOS about a year ago, including studying the previous products of its authors BitShares and Steem.


The advantages of EOS compared with Ethereum: transaction throughput is three orders of magnitude higher; advanced permission-system for smart contracts; the ability to recover lost access and correct errors in the blockchain; network management onchain. Disadvantages: centralization concerns, potentially more vulnerable DPoS consensus, raw code and a steeper learning curve for developers.


Since we have been interested in this technology for a long time and consider it promising, we could not disregard the hackathon series supported by the authors of EOS. We just wanted to be there, realize our ideas in this inspiring setting and share them with a wide audience. Of course, the opportunity to win good money also became an additional pleasant motivator.


So, EOS is the only known working solution for the public blockchain, where you can do a lot of transactions. And where is it required? Of course, in IoT! After all, if each toaster becomes micropayments to pay for each piece of bread to the refrigerator itself (which is cool by default, as you understand), there will be a lot of transactions. Not to mention all other uses in medicine, manufacturing and everyday life.


A few weeks before the hackathon, alternative ideas periodically surfaced, small brainstorming sessions were conducted. We compared ideas based on well-known judging criteria: the use of EOS capabilities, creativity, social impact (impact), scalability. As a result, we stopped at IoT + EOS - a solution that would remove data from the sensors and send many payment transactions to EOS.


By the way, we really wanted to tell you more about how we raised our Block Producer for EOS; how the designer of ERC721-like tokens and support for constant functions were planned to be written down for him; how they wrote down the Merkle Root ACL protocol. But all this does not fit into the article, so back to our main project.



1. Preparation


1.1. Iot


Preparation of the IoT-part of the project is the choice of a suitable iron. In the role of RFID reader was chosen RC522, working on the SPI bus: it is popular and easy to use.



When searching for the counter, we were guided by the presence of a pulse output, since it makes it very easy to read the data: one pulse is X kWh (where X depends on the model), eventually stopped at the “Mercury 201.5” counter.



The hardest thing was to determine the controller, which must collect data from the sensors, form a transaction, sign it with its private key and send it to the network. Accordingly, we needed a device with a network module that could sign a transaction using ECDSA (in this case, on the elliptic curve secp256k1, since it is precisely this that is used in EOS for the signature).


Initially, the choice fell on the ESP8266 microcontroller, it has a Wi-Fi module and all the necessary interfaces for connecting our sensors. At the same time, it is very compact. But there is no native implementation of elliptic primitives in any of the firmwares. Writing your own implementation is possible, but this is not a task for the hackathon. As a result, Raspberry Pi 3 B was chosen for the prototype, and eosjs library was used for generating and signing transactions.



1.2. Infrastructure


A couple of days before the hackathon, an EOS assembly ( source code ) was prepared locally and on the eos-hackathon.smartz.io server. The dependency installation, build, and tests went surprisingly smoothly for such a young project (using documentation ). There was not enough time for other infrastructure preparation, and we had to do this during the hackathon.


1.3. Architecture


On the eve of the hackathon, we discussed the architecture and clarified the product details. We were going to implement the following main cases: payments for electricity and payment for purchases marked with RFID tags. We also planned to make the product easily expandable and use it in other areas.



The idea of ​​architecture is that a service provider (Producer) creates one contract - the central point of interaction between the supplier and the consumer. Each consumer has its own balance, which can be replenished, funds are charged off on the basis of sensor signals. All data — users, sensors, statistics — are stored in the supplier's contract.


The user settings, or flags (for example, the preferential category of the user) - user_meta are user_meta . Several sensors can be associated with a consumer, for each of them a contract and billing settings ( billing_meta ) are indicated. So you can get a non-changeable, stateless billing contract used for a large number of consumers; the necessary data will appear during the call to the bill(payload, user_meta, billing_meta) method bill(payload, user_meta, billing_meta) . There is also the possibility of different billing logic, i.e. different contracts: for example, one counts electricity, the other - goods. Each sensor has a “pointer” to its billing contract.


It is assumed that the consumer trusts the sensor manufacturer, but does not necessarily trust the service provider. The interface with the sensor is extremely simple: it is a call to the supplier’s contract method with a numeric parameter that will be passed to billing. The service provider brings consumers, sensors, billing contracts and their interconnections into their contract using control transactions — primitive setters. When a transaction is received from the sensor, data is checked, billing data is generated, billing is called, payment is committed, and statistics are recorded.


Perhaps most of all, we discussed the following issues that are important for the applicability of a product in the real world:



2. Coding


First of all, they specified the API and framework of the supplier's contract in order to start in parallel the development of the frontend, device code, billing and main contract ( source code ).


2.1. Iot


The first implemented code for reading pulses from the counter. To work with GPIO (general purpose pins), the onoff JS library was used. Later, for clarity, two LEDs were added to the circuit: the first blinked when receiving a signal from the counter, and the second when the response from the EOS node received a response about a successful transaction. Similarly, we developed a scheme and code for reading RFID tags, with the only difference: the reading took place on the SPI bus using the MFRC522-python library. As it turned out, the SPI setting for the Raspberry Pi 3 is different from the setting in earlier motherboards; This instruction helped us understand.


The devices were powered with a power bank, successfully donated to all participants of the hackathon, and the Internet had to be shared with the iPhone 5, since the hackathon’s Wi-Fi worked exclusively at 5 GHz, this did not work for Raspberry Pi.


2.2. Infrastructure and utilities


The organizers advised to take the eos-dev docker image, but we were confused by the lack of a description and documentation of the image. On the server, we continued to work with the prepared assembly, and locally, to avoid installing EOS systemically, we used eos-dev.


Immediately, the ability to quickly assemble and test was urgently needed. Ideal: build and run a locally executable file. However, it was impossible to ignore the fact that after assembly at the exit, it was necessary to obtain a WebAssembly and in the EOS environment - with the appropriate boost, libraries, system contracts. The necessary compilation parameters could be peeped at eosiocpp.in , however we decided not to play this game. A predictable result, albeit a bit slower, is more important than a quick solution with a potential rake. Therefore, to build took eoscpp, located in the container eos-dev.


It was more difficult with the launch, I had to raise the local EOS blockchain, and again there was no ready-made solution. Only software. This is how the first version of the launch infrastructure appeared. The idea is to hide the mounting and configuration details and get a self-consistent set of four to five “buttons” for typical actions. Less control, but less opportunity to make a mistake, plus time savings.


The main components of EOS are the nodeos demos, keosd daemons, the cleos console utility and the eoscpp compiler:



It immediately became clear that the cleos commands related to calls to keosd are not working. Since an error was generated indicating that keosd was unavailable on the network, we spent time diagnosing network problems on the docker network. However, strace showed that the matter is not in the network: cleos addresses to the wrong address, always on localhost (and in the case of our infrastructure, different daemons have different network addresses in a separate docker network). A bug in cleos was diagnosed: a keosd availability check, which is performed before any command associated with the purses, takes into account the port passed in the arguments, but does not take the address into account. In terms of the hackathon, as a workaround, they switched to the host network in docker.


The next step was the utility of compiling contracts using the compiler in the container ( commit ). Input and output directories were mounted. And finally, the ability to load the contract into the blockchain and send transactions ( commit ). Again - utilities in a consistent style, simple “buttons”. This ended the basic infrastructure, but the surprises continued: we stumbled upon the problem of C-functions of working with memory (in more detail later).


Finally, they began to set up accounts in one file (each contract and participant require separate accounts) right along with pairs of keys that are automatically created when the blockchain starts, so that one team could raise the test environment. One copy of this environment and deployed on eos-hackathon.smartz.io.


2.3. Smart contracts


2.3.1. Electricity supplier and billing contract


After the start of the hackathon, we started to lay out the structure of contracts according to the scheme above. The system consisted of the following contracts:



In the supplier contract, most of the usual CRUD operations are involved: adding users, tariffs, meters, increasing or decreasing the user's balance. More complex methods were responsible for receiving data from the counter, calling the contract to calculate the payment (billing), debiting funds from the user's personal account after the callback from the billing contract. The required contract for billing was determined based on the user's tariff.


In the contract for billing, after the call, a payment was calculated and a method was called to write off the payment from the user's account. Throwing the main logic, we even thought, if we are not doing too simple contracts. A little later, after the deployment of contracts to the node and their testing, it became clear that the contracts may be simple, but there are nuances. :)


After the deployment it turned out that the expected contract calls from each other did not work. Not enough rights. Unlike the smart contracts in Ethereum, where the contract is called from the contract on behalf of the calling contract, at EOS the whole chain begins with the initiator of the transaction. When you call a contract from the contract, it is checked whether the initiator has allowed the contract to do this.


Mentors immediately suggested how to act in a simple case. Rights are added as follows (via calling the eosio system smart contract):


 ./cleos push action eosio updateauth '{"account":"electricity","permission":"active","parent":"owner","auth":{"keys":[{"key":"EOS7oPdzdvbHcJ4k9iZaDuG4Foh9YsjQffTGniLP28FC8fbpCDgr5","weight":1}],"threshold":1,"accounts":[{"permission":{"actor":"supplier","permission":"eosio.code"},"weight":1}],"waits":[]}}' -p electricity 

In this case, the electricity account permits the supplier contract to call other contracts on its behalf. You can read more about rights in Technical WP EOS . In our billing , the contract supplier called the contract billing , and that, in turn, again called the supplier . Adding by analogy rights in this form did not work:


 ./cleos push action eosio updateauth '{"account":"electricity","permission":"active","parent":"owner","auth":{"keys":[{"key":"EOS7oPdzdvbHcJ4k9iZaDuG4Foh9YsjQffTGniLP28FC8fbpCDgr5","weight":1}],"threshold":1,"accounts":[{"permission":{"actor":"supplier","permission":"eosio.code"},"weight":1},{"permission":{"actor":"billelectro","permission":"eosio.code"},"weight":1}],"waits":[]}}' -p electricity 

An error was issued: Invalid authority. Here, the mentors no longer came to help us: they said that they did not do this themselves. And who did? Maybe only Dan Larimer. We could not quickly find the cause of the error in the EOS code and have already begun to think about alternatives, without a chain of calls. It was beautifully prevented by the fact that the mechanism for calling other contracts in EOS is also different from the broadcast. When calling another contract, this call is placed in a queue and will be executed only after the current call is made. It is impossible to call the contract and after the call read the data recorded by this contract.


Later, all the same, we found in the EOS code the reason for the error when setting rights for two contracts. It turns out that the accounts in the list of rights must be sorted by account: unique keys and sorted ( authority.hpp ). After changing the order of accounts, the rights update worked, and our contract system began to operate.


2.3.2. The problem of C-functions of working with memory


It is ridiculous to say, but in the end we could not use ready-made functions for parsing numbers (!) To read the billing configuration. Following std::istringstream function env.calloc std::istringstream up, which is rather strange. And when using atof and similar, as well as sscanf - env.realloc up. The mentioned functions of working with the memory of the standard C library for some reason were not in the course of loading the code into nodeos. The C ++ memory functions worked at that.


Of course, during the execution of the WebAssembly contract, it is not the standard memory allocator that is used, but its own sandbox provided by each transaction under certain conditions. Also, support for C-functions of working with memory on top of this sandbox has been implemented for quite a long time; they are implemented in standard EOS contracts . Probably something went wrong at the linking stage.


Having spent about an hour searching for a way out, including using one of the mentors, we decided not to continue and make a workaround: write our own code that solves the problem of parsing numbers. The datastream EOS mechanism did not suit us: we needed the ability to save data packets of different structures in one field and form them by hand (the very configurations of the billing systems).


2.3.3. Billing purchases


In the second breath, which was opened thanks to either the power engineers or the early breakfast, they wrote a billing for purchases in the store. The general scheme of work is as follows:


  1. The supplier creates a billing contract and enters it in its general contract.
  2. At the exit from the store, the supplier establishes a framework that can read RFID, interact with EOS and have their accounts prescribed in the billing contract.
  3. Each product in the store is equipped with an RFID tag, all tags are written in the billing contract.
  4. The buyer pays for the product by scanning its RFID, and the product is removed from the billing contract.
  5. At the exit of the store, the frame additionally reads the RFID purchases. If the goods are still in the store - the transaction does not pass, and the frame should sound the alarm (yes, in fact, you can not even send the transaction, but just read the table).

There is no point in stopping the contract code itself: it is a standard C ++ 14 with some EOS-specific constructs and libraries. Better to say in the EOSIO Wiki and EOSIO Developer Portal .


2.3.4. Frontend


The frontend-part of the project was implemented using React. Instead of the usual many Redux decided to use MobX, which greatly speeds up development and allows you to manage a global state without a headache.


The front-blockchain integration phase was not as smooth as expected. The eosjs package is being finalized very actively, followed by the EOS wallet for the Scatter browser. In this bundle, this often causes problems. And it's not a fact that the code that worked yesterday will work fine today. We have come to this rake (and not the first time). An hour of attempts and debugging in a half asleep state - the problem is solved.


eos. eosjs Scatter.


We remind you! Scatter eosjs, .


. - EOS: ( , ) (read-only ).


:


  sendTransaction(funcName, data) { return this.scatter .suggestNetwork(this.network) .then(() => this.scatter.getIdentity({ accounts: [this.network] })) .then((identity) => { let accountName = this.getAccountName(identity); // wrap eos instance const eos = this.scatter.eos(this.network, Eos, this.configEosInstance); return eos.transaction(accountName, (contract) => { contract[funcName](data, { authorization: accountName }); }); }); } 

: , — . : , EOS. : identity , — accounts ( ). getAccountName ( identity ).


Scatter . Scatter — Eos . 9 eos , :


  1. this.network — .
  2. Eos — eosjs.
  3. this.configEosInstance — Eos (. eosjs).

transaction accountName callback , callback ' — , accountName . callback 'e , — .


:


  readTable(data) { this.eos = this.scatter.eos(this.network, Eos, this.configEosInstance); return this.eos.getTableRows({ json: true, limit: 1, ...data, }); } 

eos .


, Materialize, Semantic Ant, . UI, . - . UI.


3.


Raspberry Pi «» RFID, . «». 0,3125 ⋅, «» Raspberry Pi , , , .



. , , !


4.


( ) . 69 - , . ( 5 ), -10 . .


, CEO (, ) Everipedia. , . , 30 . - , .


, , , . . Raspberry Pi .


, , , , . : , , , , , . , .


15 . . : 26- , , , . — .


, . , , . — . -10 . , . , .


5.


AngelHack . , . — , , . , , , — .


26 IoT- EOS. , .


— UI ( — -- Smartz ), . - , blockchain-ready , , — . :)



, , «», «» . . 5 . , 100 , . SensorPay !


:


Yuvasee (entrepreneur)
algs (hardware & backend developer)
therealal (architect, backend developer, devops)
bolbu (frontend developer)
quantum (blockchain & backend developer)

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


All Articles