Skip to content
Camil Băncioiu edited this page Sep 11, 2019 · 31 revisions

Architecture and Components of the Node

Overview of the tasks performed by the Node

The Elrond Network is a peer-to-peer network of machines called Nodes. The Nodes perform all the tasks required by the Elrond protocol:

Because of the complicated tasks listed above must be performed reliably, in a reproducible manner and as efficient as possible, the Node must be a complex piece of software.

The special case of Observer Nodes

TODO


The tasks performed by the Node, in more detail

Receiving Transactions

The main purpose of the Nodes in the Elrond Network is to execute the Transactions requested by its users. When a user wants to request the execution of a Transaction, they submit it to any Node's REST API - doing this causes the Node to propagate the Transaction throughout the rest of the Network, which will lead to its execution and its eventual inclusion in the blockchain. This means that the REST API of the Nodes collectively represents the "entry point" through which the Network receives information and requests from the outside world. Note that people who run Nodes on their own machines may choose to disable the REST API, allowing their Nodes to focus strictly on producing blocks for the Network. On the other hand, there are Nodes in the Network that are dedicated "entry points" and never produce Blocks (but contribute meaningfully in other ways). See the last paragraph of this section for details.

While any user can submit a desired Transaction to any Node directly through its REST API, there is a convenient method of submitting Transactions to the Network at large: Elrond provides a Web application which, among other features, enables its users to easily submit Transactions to the Network without having to worry about technicalities such as composing a valid Transaction, signing it and selecting an appropriate Node to submit it to. This Web application is the Elrond Wallet, currently submitting Transactions to the Testnet and accessible at testnet.elrond.com/#/wallet.

To create a Transaction, the Wallet takes user input, such as the destination Account and the sum of ERD to transfer, then uses this input to perform the operations described in the section Creating a Transaction. The resulting Transaction is passed to the Proxy, which is a separate application which maintains a list of Nodes to submit Transactions to - these Nodes are selected in such manner that any Transaction submitted to them will be processed by the Network as soon and as efficiently as possible. The Proxy will then submit the Transaction on behalf of the user to the REST API of one of its listed Nodes, selected for (1) being online at the moment and (2) being located within the Shard to which the Sender's Account belongs (see Executing Transactions for the reason of this second criterion). After receiving the Transaction on its REST API, that specific Node will propagate the Transaction throughout the Network, which will lead to its execution. TODO maybe rephrase? it's hard to read

Note that the Nodes enlisted by the Proxy for Transaction submission are not just some random Nodes - they are specific Nodes maintained by Elrond, which do not ever participate in Consensus, also known as Observer Nodes (as opposed to the normal Nodes, called Validator Nodes). Observer Nodes thus act as a default dedicated "entry point" into the Network. Moreover, Observer Nodes play an important role in the health and stability of the Network, because they act as fast propagators of information and have a lot of storage space. This means that the performance requirements of running a Node can be lowered for normal Nodes, and the machines that run normal Nodes are not as stressed as they would be without Observers. It is worth repeating here, though, that submitting a Transaction through the Wallet, and implicitly to the Observer Nodes maintained by Elrond, is completely optional - any Node of the Network will accept Transactions to propagate, given it has not disabled its REST API.

Executing Transactions

In its very simplest form, a Transaction is the atomic transfer of ERD between two specific Accounts. After a Transaction is executed, commited to the blockchain and declared final, the Transaction takes full effect and cannot ever be reverted anymore. More complex Transactions also require executing SmartContracts and storing the results, but the principle remains the same for all Transactions:

  • Transactions must arrive at Nodes through Gossip
  • Transactions involve two Accounts (Sender and Destination)
  • Transactions are executed atomically (a Transaction has no effect until it is executed completely)
  • Transactions are incremental changes of the global State (?)
  • Transactions are stored in bulk in the form of Blocks
  • Transactions must be first declared final before taking full effect
  • Transactions cannot be reverted once declared final

Nodes receive Transactions from the outside world on their REST API, which they propagate throughout the Network for execution. The execution of a Transaction involves a set of verifications against any faults in the Transaction, then its inclusion in a Block that was proposed and validated by the Nodes participating in a Consensus process.

Executing any kind of Transaction requires a non-negligible computational effort to be expended by the Nodes. This effort involves the consumption of time, computational power and electrical energy - real-world resources which cost real-world money. To compensate people who run Nodes on their machines, the Elrond Protocol dictates that Nodes will receive compensation for their work, paid in ERD. But the exact amount of real-world resources consumed by a Node to execute a Transaction is difficult to quantify, therefore an abstract unit called Gas is used instead, which is then used to calculate the amount of ERD received by Nodes as compensation for their work. Measuring computational effort in Gas instead of CPU cycles, Joules or the market price of hardware is a simplification, of course, but it is very useful and flexible. The exact amounts of Gas consumed by Nodes for the tasks it performs is described in the section Gas consumed by operations.

In principle, executing a Transaction, would be a straightforward process, regardless of its type, but in practice, it is complicated by the fact that the Elrond Network is sharded (i.e. fragmented) in order to achieve higher efficiency. More specifically, Sharding affects the execution of Transactions due to State Sharding, which separates and distributes the information required to execute Transactions across a carefully controlled number of Shards. As a direct consequence, information about the Accounts themselves is divided throughout the Network. And to execute Transactions, a Node requires information about both the Sender Account and Destination Account. But because a Node is assigned to a single Shard at a given moment in time, it might find itself in the situation where it must execute a Cross-Shard Transaction, i.e. a Transaction where the Sender Account and Destination Account are located in different Shards. In contrast, Transactions where the Sender Account and Destination Account are located in the same Shard are called Intra-Shard Transactions. It is important to remember that the execution of any Transaction is always initiated by Nodes belonging to the Shard of the Sender Account, regardless of the type of Transaction. This approach enables a relatively simple solution to the challenge posed by Cross-Shard Transactions, because the Nodes that initiate their execution already have half the required information: they have the Sender Account information by virtue of belonging to the Shard of the Sender Account.

To keep the Transactions in a Block organized, they are grouped in Miniblocks, which are lists of Transactions of the same type. For example, there are Miniblocks containing value-transferring Cross-Shard Transactions where the current Shard is the Source Shard, and there are Miniblocks containing SmartContract results from Cross-Shard Transactions where current Shard is the Destination Shard. See Types of Miniblocks within a Block for more details.

As one would expect, Intra-Shard Transactions are easier to execute and will finalize earlier than Cross-Shard Transactions, which require extra steps. The Elrond Network aims to organize the Account Space in a way that minimizes the number of Cross-Shard Transactions and maximizes the number of Intra-Shard Transactions. See the subsections below for details on how both types of Transactions are executed.

Executing Intra-Shard Transactions

An Intra-Shard Transaction is a Transaction between two Accounts assigned to the same Shard. This is the simpler kind of Transaction to execute, as opposed to executing Cross-Shard Transactions, which require extra steps.

As emphasized in section Executing Transactions, the execution of a Transaction is always initiated by Nodes that belong to the Shard of the Sender Account, regardless of the type of Transaction. Since an Intra-Shard Transaction involves Accounts assigned to the same Shard (as its name implies), the Nodes executing it already have all the information required.

The steps of execution are as follows:

  1. Certain Nodes of the Shard begin the Consensus process.
  2. The Leader Node of the Consensus group proposes a Block which includes the Intra-Shard Transaction. This results in the sum of ERD being deducted from the Sender Account and then added to the Destination Account.
  3. The rest of the Nodes in the Consensus group replicate the proposed Block from their own available information.
  4. If all Nodes in the Consensus group agree on the same resulting Block, then the Block is notarized by the Metachain and the Consensus process ends.
  5. Later, after some more Blocks have been notarized by the Metachain, the Block that contained the executed Intra-Shard Transaction is declared final, which means that all the Transactions it contains are now final as well, including the Intra-Shard Transaction discussed here.

Note that Intra-Shard Transactions are stored in Miniblocks within a Block, separate from Cross-Shard Transactions (which are also stored in their own Miniblocks). See the linked sections for more details.

Executing Cross-Shard Transactions

An Cross-Shard Transaction is a Transaction between two Accounts assigned to different Shard. This is the more complex kind of Transaction to execute, as opposed to executing Intra-Shard Transactions, which are simpler and require fewer steps.

As emphasized in section Executing Transactions, the execution of a Transaction is always initiated by Nodes that belong to the Shard of the Sender Account, regardless of the type of Transaction. This means that a Node executing a Cross-Shard Transaction has only half the information it requires to completely execute the Transaction, namely the Sender Account. To execute the Transaction in full, the Node would hypothetically require the Destination Account to be retrieved from the Shard it is assigned to. But this would go against the main advantage of Sharding, namely to keep information separated and process it separately in order to gain performance. To avoid this contradiction, Elrond Nodes employ an alternative method of executing Cross-Shard Transactions: they execute the Cross-Shard Transaction in half inside the Shard of the Sender Account, then send a partial result to the Shard of the Destination Account for completion. The steps are as follows:

  1. Certain Nodes of the Shard begin the Consensus process.
  2. The Leader Node of the Consensus group proposes a Block which includes the half-executed Cross-Shard Transaction. This results in the sum of ERD being deducted from the Sender Account, but the sum is not added to the Destination Account, because the information on the Destination Account is stored by Nodes of a different Shard.
  3. The rest of the Nodes in the Consensus group replicate the proposed Block from their own available information.
  4. If all Nodes in the Consensus group agree on the same resulting Block, then the Block is notarized by the Metachain and the Consensus process ends.
  5. Nodes from the Shard of the Destination Account receive, through Gossip, the notarized Block from the Metachain
  6. The Nodes from the Shard of the Destination Account notice that the Block contains a half-executed Transaction, of which their Shard is the Destination.
  7. These Nodes then execute the second half of the Transaction, namely adding the transferred sum of ERD to the Destination Account during their own Consensus process and add the fully executed Transaction to the Block they produce.
  8. If all Nodes in the Consensus group of Shard of the Destination Account agree on the same resulting Block, then the block is notarized by the Metachain, ending their Consensus.
  9. Later, after some more Blocks have been notarized by the Metachain, the Block that contained the fully executed Cross-Shard Transaction is declared final, which means that all the Transactions it contains are now final as well, including the Cross-Shard Transaction discussed here.

Note that Cross-Shard Transactions are stored in Miniblocks within a Block, separate from Intra-Shard Transactions (which are also stored in their own Miniblocks). See the linked sections for more details.

Executing SmartContracts

  • Describe how a Transaction with a SmartContract call looks like
  • Describe how a Transaction with a SmartContract deployment looks like
  • Connect this section with the Processor components
  • The SC is run by a VM
  • The VM is a specialized piece of software, dedicated to running SCs; it doesn't resemble VirtualBox or VMWare
  • The VM needs access to info such as Accounts, Blocks, Transactions etc because SCs might need such info
  • Describe here what any SC VM must be able to do: take specialized input, provide specialized output, meter the execution in Gas units, provide "logs"

TODO

A Transaction is usually a value-transferring process, with a relatively simple execution, but there is a special kind of Transactions which, on top of transferring value, also request the execution of custom code. Such custom code must have already been published to the Blockchain in the form of a SmartContract (TODO link) before anyone could request its execution. Publishing a SmartContract to the Blockchain is called deploying the SmartContract (performed as a special Transaction), while requesting the execution of code from within a SmartContract is referred to as calling the SmartContract. Both the deployment of a SmartContract or a call to one will require a component dedicated to handling SmartContracts: the Virtual Machine.

Producing Blocks

TODO

Building a Block

TODO

Gas

  • TxFee = GasPrice ⨯ ConsumedGas
  • TxFeeLimit = GasPrice ⨯ GasLimit
  • Gas = quantum of computational work performed for the network, compensated in ERD
  • Gas is "spent" executing Transactions and also storing data
  • Describe how Gas is metered during the execution of Transactions

Gas consumed by operations

TODO

Timekeeping

TODO

Propagation of information

Each Node is constantly listening to its connected Peers for incoming information of all kind, packaged as atomic Messages. A Message may arrive to the Node because it was either:

  • sent by a Peer directly to the Node (thus the Message has a sender Node and a destination Node)
  • broadcasted by a Node to all its Peers

Most often, Messages are propagated being broadcast by a Node to its Peers. Nodes will automatically re-broadcast many of the received Messages to their Peers, to ensure the widest propagation of information. This repeated broadcasting is called Gossiping. Furthermore, to ensure efficient and orderly communication, Messages are assigned to predefined Topics (i.e. "categories" of Messages). The Node that constructs a new Message must assign a single Topic to it, based on the type of information it contains and what audience it is directed at. There are many Topics defined by the Network, but a single Node will only listen to a specific subset of them. See Topics of interest for a Node for details.

Whenever a Message arrives at the Node, it firstly assesses whether the Message is of interest or not. This is easy to verify: if the Topic of the Message is not of interest, then the Message is promptly discarded and forgotten. But if the Message is assigned to a Topic of interest to the Node, a complex process begins: the Message is passed to the specific Interceptor that handles the corresponding Topic, and it is now the responsibility of the Interceptor to deal with the Message somehow. There are multiple types of Interceptors - one for each type of information that a Message can contain.

Most Interceptors (?) have one task: to put the information contained by the received Message into the Data Pool of the Node. The Data Pool thus acts as a reservoir of information for most tasks performed by the Node. After being exposed to the Network for a while, a Node will have its Data Pool teeming with Transactions, Block Headers and other types of information propagated through the Network, all arrived as Messages at some time in the past. Whenever the Node must perform some task, e.g. to produce a Block, it will use the information found in the Data Pool.

In case the Data Pool does not contain certain pieces of information required by the Node at a given moment, the Node must create Request Messages (?), which it sends to some of its Peers. If a Peer has the information requested by the Node, it packages that piece of information as a Message and sends it back directly to the Node. If the requested information is missing from the Peers that have been asked for it, they sit silently and do nothing (they don't propagate the original request further). The Node that needs information must try again with some other Peers. See Requesting information from Peers for details.

A special case where a different form of propagation of information takes place is the Consensus process. See the linked section for details.

The Topic-broadcasting functionality itself is available in the libp2p library, which the Node uses for all its peer-to-peer communication.

Gossip

A Node will automatically re-broadcast to its Peers most Messages that it receives (e.g. containing Transactions or other information), in order to propagate information throughout the Network. This is called Gossiping. Note that Direct Messages are never re-broadcast, thus they are not subject to Gossip. All other Messages are considered "of general interest", thus are broadcast to as many Nodes as possible. However, not all Peers of the Node will care about each and every Message - they will simply ignore those Messages that arrive on a Topic that doesn't interest them. Because of this selective interest on Topics, it would be a waste of bandwidth and processing power to always broadcast every Message to all Peers. Consider the following example:

  1. Message m1 arrives at Node A on the Topic Tx0. Let's say that this Message is both valid and interesting to A.
  2. A wants to propagate m1 to its Peers: they are Nodes B, C, D and E.
  3. But A knows that C and E do not care about any Message that arrives on Tx0, thus they would ignore m1 if it were to be sent to them.
  4. A knows that only B and D would accept m1.
  5. Therefore A will only send m1 to B and D, to save resources.

Thus, to make communication more efficient, Nodes will be mindful about the Topics of interest of their Peers and will actually perform selective broadcasting: a Node will exclude a Peer from the broadcasting of a Message if it knows that the Message will be uninteresting to the respective Peer, as described in the example above.

To summarize: any Message m on Topic T will be sent by Node X to a certain Node Y, if the following conditions are met:

  1. The Nodes X and Y must be Peers
  2. The Message m is valid. Invalid Messages are ignored and dropped.
  3. X must have declared interest in Topic T (see Topics of interest). Otherwise, the Message is ignored and dropped.
  4. Y must also have declared interest in Topic T
  5. X must be aware that Y has declared its interest in Topic T

Direct Messages

TODO

The peers of a Node

TODO

Types of information propagated through the network

Transactions

Unsigned Transactions

Block headers

Block header nonces

Requesting information from Peers

TODO

  • Requests: when and how are requests generated?
  • Requests: generated (?) when a Processor needs info, queries the Data Pool, doesn't find that info, then generates a request and broadcasts it. But which component exactly generates the Request? Is it the Data Pool? And what does that specific Processor do when it finds out that the information is missing and must wait? If it enters Consensus and it suddenly realizes some information is missing, will it simply wait during the Consensus?
  • Resolvers: they send a reply back directly to the requesting Peer (the PeerID is in the Message containing the initial request)

Participating in Consensus

TODO

The Nodes that participate in Consensus

TODO

Communication during Consensus

TODO

Rewarding Nodes for work

TODO

Detecting malicious activity

TODO

   


Info to integrate:

  • ☐ Synchronization: at the beginning of each round, the Node checks if it is synchronized with the rest of the peers. Synched = has all the current pieces of info (blocks, block headers, nonces etc) and can move on to processing. Not synched = must start requesting information from the Network.

Notes:

  • Narrative entry-point The Node is ready and connected to the network. It has been busy processing transactions, proposing and validating blocks for some days now. Let's see, in more detail, what happens to a transaction once it arrives at the Node, from initial validation until being saved in a final block on the blockchain, notarized by the Metachain.
Clone this wiki locally