Decentralized Applications , or ĐApps, are applications that run on decentralized platforms.
It pertains to concepts such as Distributed Ledger Technologies (DLT) or Blockchain . So when it comes to ĐApps , we often assume that they are blockchain applications.
Because it runs on decentralized platforms, it will also require special architectures to achieve high system security and reliability.
In this article we will analyze some of the architectural requirements for the application , and also give suggestions about the patterns that should be applied to the backend of the system.
DApp in general and Blockchain in particular are very wide, in this article we will take an example with the most popular application platforms currently: Ethereum, EOS, TRON. Of course, this knowledge can still be applied to other decentralized platforms.
- How to store private key in backend like?
- Decentralized and Semi-Decentralized architecture.
- Solve low-level issues such as network errors, event handling.
The greatest value that Blockchain and other decentralized technologies can bring is that it is possible to build applications that run exactly according to the design that no one can affect the behavior of the application, or falsify existing data.
However, at present, the calculation and storage on common blockchain platforms such as Ethereum, EOS, TRON are still very limited, so in the APP systems, we still have to combine with the front-end and back-end. traditional.
Therefore, sometimes not entirely 100% of applications are decentralized, but may be partially centralized, or semi-decentralized depending on the problem. In the future, when decentralized platforms are scaled, we will be able to comprehensively enable fully-decentralized applications, including from the frontend, backend, or anything else.
However, at the present time, for those limited reasons we need to combine many different designs, including traditional centralized and decentralized architectures to solve the problem. that we are facing. Specifically:
- Use a server to host the backend or frontend of the application
- You still have to use a backend to interact with the systems that have been built before. You can’t always do the same thing over and over again, especially when it comes to complex business logic and stable running.
- The storage capacity of the blockchain is limited (due to the limitation of the block size), so the large data we still have to store in traditional servers. There are actually decentralized storage like IFPS or Filecoin, but the stability of these services is still questionable. Moreover, they are still in the process of development, maybe we should wait more time for them to become truly mature products.
So for the time being, almost any decentralized system, we still have to build a backend for it. And in this article, we will discuss how to design backend effectively.
DPlatform = Decentralized Platform – i.e. decentralized platforms like Ethereum / EOS / TRON.
We have 2 common architectures of the current ĐApp:
Client ⇔ ĐPlatform: fully decentralized applications
With this architecture, our client (browser or mobile app) will interact directly with the decentralized platform through wallet software such as Metamask, Trust or hardware wallets like Trezor or Ledger.
For example: CryptoKitties, forum Delegate Call of Loom; electronic wallets such as Metamask, Trust, TRON wallet; Decentralized exchanges such as Etherdelta, Kyber.
ĐPlatform ⇔ Client ⇔ Backend ⇔ ĐPlatform: semi-centralized applications
This is the most popular architecture today.
An example is famous exchanges like Bitfinex or Poliniex or Binance. All virtual currencies traded on these exchanges are stored on a traditional database. We deposit money by transferring money to a specific address of the exchange (ĐPlatform ⇔ Client), and when withdrawing money, we will place an order and wait for the backend to transfer money back to us (Backend ⬌ ĐPlatform). And of course, all the other interactions within the app are the same as in the traditional app and do not affect theplatform (Client ⬌ Backend).
Another example using a semi-decentralized architecture is Etherscan.io : We can get all the ethereum-related actions here, ie actions with DPlatform, but the site itself is hosted on a backend. fixed, and also provides APIs / UIs like traditional web sites.
We will take a simple example to understand the flow of a transaction on these systems and how it is handled:
- Listen to events on the network by polling continuously.
- Once the event is captured, we will proceed with the business logic and send a corresponding transaction.
- Sign a transaction with private key.
- After the transaction is sent, we continue polling the network to check its status.
- If the transaction takes too long and has not been verified, it could be due to many of the reasons mentioned above, in this case we will send it back by re-signing the transaction & sending it with a higher amount of gas so that verify earlier.
- Transactions are mined. Here we can continue to implement our logical bussiness.
Backend for ĐApp
All interactions for decentralized networks can be encapsulated in 2 points:
- Listen for events and Read state of the network.
- Send transaction to change the network state.
There are some outstanding issues when we implement the above:
- With Ethereum, the event is not stable . There are dozens of reasons why we can’t receive events when transactions happen, such as network errors, fetching too many events at once, events that can disappear or be changed due to network forks, etc. This problem, we need to build a mechanism to sync events and ensure reliability.
- Similarly, transactions in Ethereum are also unstable , there are dozens of reasons leading to failed transactions such as false nonce, insufficient gas, false signatures, logical errors … We need recovery or resend mechanisms. back to those transactions.
- Security: It is difficult to guarantee 100% that the private key on the backend is never exposed. Instead, we can design more appropriate options, so that even if attacked, the damage is still too small compared to the effort that the attacker spent.
Ok we’ll go into detail how to solve each of the above problems.
Listen Network Events
Because all actions on the decentralized network are asynchronous, transactions take different amounts of time to mine, so in order to capture the status of transactions fully and promptly, we Must use to listen to the event.
For example, with an ERC-20 contract, all money transfers will generate an event
Transfer . In the application, we need to listen to this event to implement the business logic of the app, such as sending notifications, emails, or simply updating the balance for an account.
As we have said above, the event in the decentralized network is unstable, there are many cases where we cannot listen to the event directly. Therefore we need to build a backend to be able to perform the event sync more effectively.
Depending on each problem, there will be different designs, we will give a design for the following example to improve the way to listen directly to the event by using the message bus
How it works would be as follows:
- The backend will continuously poll the network to get events. Whenever a new event occurs, it will be sent to the message hub in order to be processed at any time. One thing to note here is that if we take too many events at the same time, it can lead to failed requests, which can be solved by limiting the number of requests per block accordingly.
- Message Bus (eg RabbitMQ) will transfer the event to each suitable backend for processing. Each backend will only handle which events it subscribe to, avoiding listening and redundant handling of unnecessary events.
Message bus is an effective way to store and distribute events, of course we can still handle them in other ways such as callback, socket … However, then, we will have to implement more segmentation. coordinate events, plus monitoring to ensure that the events are delivered to the right backend.
When sending a transaction, we will have to perform the following steps:
- Prepare raw transactions: This is important because it determines whether the majority of transactions are successfully sent or not. Gas, nonce, input … are the things that we need to pay attention to setup properly. There are many support libraries we do like
- Sign the transaction: we will sign the transaction with the
- Send and resend transaction (in case the transaction waits too long or fails). There is no guarantee that transactions will always be mined 100%. Perhaps due to the network error that we could not send, the amount of gas is too low, leading the miners not to put it into the block to confirm. In those cases, we will have to resend the transaction, pay attention to keep the nonce, and increase the gas so that the transaction will take priority to block.
Combining the design of event listener and send transaction above, we get an architecture like the one below, in this case an example of a payment application:
- The user will call a function in the smart contract, after successful backend will perform a transaction charge money.
- The backend listens and captures the event in step 1, it will execute the transaction charge money.
- Once the transaction charge has been mined, the backend will receive another event and continue the logic behind.
The transaction is signed and sent by the private key . So the security of the private key is very important. There are many sophisticated security options that have been implemented here , here , and here . Some solutions store private keys in geo-distributed databases (data is stored in many different locations but almost does not affect the performance of the system), some are stored in special hardware. . In the end, however, we still had to use private to sign the transaction, that is, in theory, there was no way to guarantee 100% private keys could not be exposed.
Therefore, instead of thinking of an absolute guarantee for a private key, another option is to design the system so that even if the private key is exposed, its damage is almost negligible.
How is this done? Instead of the user calling the task directly, the user only calls the task trigger function, and the actual task will be performed by another account on the system, called the operator account . And this design is called the Operational Accounts Pattern .
At this time, even if the system is attacked and exposes the private key, then:
- The attacker will only steal a very small amount of Ether that has been deposited into the operator account.
- Other transactions and the smart contract itself are not affected at all.
- We can replace the operator account that was hacked by another account quickly.
Therefore the damage done is very small (probably not worth the effort that the attacker spent to obtain the private key).
Apart from the above option, Vault is also an option. This is an Ethereum plugin to store and manage Ethereum accounts.
Above we have gone through a few common options for designing backend effectively for the Application. Hopefully it can be of some help to developers working with decentralized systems in general and blockchain in particular.
It can be said that designing a safe and reliable system is really difficult . There is no guarantee that today’s safe design will be safe tomorrow. Except for this design .
Keep up to date with new architectures and new design patterns for your system