Before go down into the technical details, a few words about the architecture. Since the project was written from scratch, special attention was paid the construction of a flexible architecture that would have maximum comfort while working on a project in the future, both within our team, and third-party developers. We commonly used boost and C++11, as it make us enjoy the work, very sorry if this will cause problems with building on older compilers to someone.The project has several layers of abstraction, each of which is isolated from the other. Let's go through this from down to up.
For the network, we use abstract tcp server, which in turn runs on boost.asio. This server is template class, parameterized with a simple binary protocol LEVIN. The operation of this protocol is organized through the exchange of packets, which are conventionally called the "command" and "notification". "Command" assumes synchronous response, while the "notification" does not imply any synchronous response.
An interesting feature of this protocol is that it is bi-directional, i.e. both parties are simultaneously acts as client and as server. At any time, either party can make an command request or notification to the other side. Using of this component looks like RPC - you just invoke remote command and get result. Handling remote requests looks like handlers map:
BEGIN_INVOKE_MAP2(currency_protocol_handler)
HANDLE_NOTIFY_T2(NOTIFY_NEW_BLOCK, ¤cy_protocol_handler::handle_notify_new_block)
HANDLE_NOTIFY_T2(NOTIFY_NEW_TRANSACTIONS, ¤cy_protocol_handler::handle_notify_new_transactions)
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_GET_OBJECTS, ¤cy_protocol_handler::handle_request_get_objects)
HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_GET_OBJECTS, ¤cy_protocol_handler::handle_response_get_objects)
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_CHAIN, ¤cy_protocol_handler::handle_request_chain)
HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, ¤cy_protocol_handler::handle_response_chain_entry)
END_INVOKE_MAP2()
With every command is associated a member-function, handler. It works with data arrived from the network as with ordinary C++ structures, due to key-value serialization.
In order that would not do the dirty job of packing / unpacking of network data every time for different structures, as well as for forward and backward capability used key-value serialization, mostly working on templates (see more details in keyvalue_serialization.h and portable_storage.h). The data schema is defined through the serialization map, that seems a very convenient way. All high-level protocols (p2p protocol, currency protocol, rpc commands) is declared as C++ structures with serialization maps.
struct NOTIFY_NEW_BLOCK
{
const static int ID = BC_COMMANDS_POOL_BASE + 1;
struct request
{
block_complete_entry b;
uint64_t current_blockchain_height;
uint32_t hop;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(b)
KV_SERIALIZE(current_blockchain_height)
KV_SERIALIZE(hop)
END_KV_SERIALIZE_MAP()
};
};
At this abstration level implemented support of the network topology, peer-lists exchange, periodic synchronization of nodes. P2p protocol class Itself does not know anything about currency core or about currency protocol, it is a template class, parameterized with the appropriate type of payload(currency protocol in that daemon). This means that on the basis of this class, you can implement other p2p networks, such as p2p-messenger, p2p-social network, etc. The p2p protocol commands are defined in src\p2p\p2p_protocol_defs.h
On the physical level p2p protocol commands and currency protocol commands lie in the same place, they are abstracted from each other only in the source code: one part of commands processed by handlers that is set in the p2p server, the rest of the commands chained to the currency protocol command handlers. Сurrency protocol is responsible for the implementation of the entire range of functions for synchronizing with other core network nodes, relay new blocks and transactions in the network as well as for serve requests for different parts of blockchain for other nodes. Currency protocol works directly with the core, although the core is given as template parameter. This allows you to write different tests, as well as use instead currency core any mock or proxy. The p2p protocol commands are defined in src\currency_protocol\currency_protocol_defs.h
Core itself does not know anything about the way of communication between the nodes, in fact it does not care what protocols work on a node. This core - it is absolutely passive object, it has no threads or internal events events - it always works as a state machine, the initiator of any operations inside the core is always external action - block came from the network, a locally mined block, transaction came from a network etc. Currency core is perhaps the most important part of the project, and for that would make sure that nothing is broken a lot of tests has been written, both for the whole core and for the some functions .
The currency core consists of three components: blockchain storage, transaction pool and miner. Blockchain storage - a key component of that is currently aggregates a functional of checking the network rules and switch between different branches, as well as acts as a shared database. Transaction pool - is a obviously place for transactions that is not in the blockchain yet.
Notice: Transaction and blocks are transmitted over the network in its particular format, but it is also absolutely not the same type of serialization such as key-value serialization that used at the p2p protocol level. If the p2p/currency protocol commands can be easy changed and expanded, the block and the transaction can not change that simple, because the serialized format of these structures is firmly fixed and participates in cryptographic protocols (hash, signature).
Usually in daemon running two network services: a p2p server that connects with other demons via the Internet and keeps actual the current state of blokchain, as well as rpc server which used for wallet synchronization and for integration with other services.
Currently rpc-api implemented in the minimal set, but we plan to implement the full range of mainteiners needs. In addition, the wallet also has its own rpc-service if it run with appropriate parameter. All RPC API related to the transfer of money implemented in the wallet.
A distinctive feature of the project is that the wallet and the daemon are separated into different applications. On the one hand it gives advantages with the security risks. Even if the daemon is compromised, to get access to the wallet will be much harder. On the other hand, this architecture makes wallet actually ready to work in the cloud.
Best way to understand transaction generation algorithm is to look at following diagram.
This operations is happening in wallet while transaction generation. The following check is verifying bye daemon:
More documentation is coming soon...