Skip to content

Commit

Permalink
Merge pull request #17 from AElfProject/feat/Core_Introduction_And_Im…
Browse files Browse the repository at this point in the history
…plementation

Added new files for Architecture_Core Introduction and Implementation
  • Loading branch information
AelfHongliang authored Jun 20, 2024
2 parents a5f66a4 + 4efa16d commit 6a46e13
Show file tree
Hide file tree
Showing 25 changed files with 317 additions and 15 deletions.
57 changes: 57 additions & 0 deletions docs/Architecture/Core/Implementation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Design Principles

![image](node-archi.png)

The diagram above shows the conceptual structure of the node and the separation between the OS and Kernel.

## OS

The **OS layer** implements the application and infrastructure layers for the network. It also handles high-level network events and jobs, such as synchronizing the chain in response to a block announcement. Additionally, the OS layer includes the RPC implementation for the exposed API.

## Kernel

The **Kernel** contains smart contract execution primitives and definitions. It also defines the components necessary for accessing the blockchain's data. Various managers use the storage layer to access the underlying database.

The Kernel also introduces the concept of plugins. The diagram shows that side chain modules are implemented as plugins.

## Structure of the Project

To understand AElf's structure, this section provides an overview of the solution.

Conceptually, AElf is built on two main layers: **OS** and **Kernel**.

- **OS Layer:**
- Contains high-level definitions for a node and endpoints like RPC and P2P.
- **Kernel Layer:**
- Contains logic and definitions for smart contracts and consensus.

AElf has a native runtime for smart contracts implemented in **C#**, for contracts written in C#. This implementation is found in the **AElf.Runtime.CSharp.\*** projects.

A significant part of AElf is the side chain framework. It is mainly implemented in the **AElf.CrossChain** namespace, defining the main abstractions in the **core** project and an implementation with gRPC in the **AElf.Crosschain.Grpc** project.

The **AElf.Test** solution folder contains all the tests. Ensuring maximum coverage of the main functional aspects is crucial for maintaining the quality of our system.

Other projects implement libraries we use, like the crypto library, and others for infrastructure, like the database library. While not as critical, they are still worth exploring.

## Jobs and Event Handlers

Event handlers implement the logic that reacts to external and internal events. They represent the higher levels of the application, being called by the framework in a domain-agnostic manner. Event handlers, primarily using other services, influence the state of the chain.

## Modules

Our architecture is based on modules that are wired together at runtime. Any new module must inherit from **AElfModule**.

To implement a new module, follow these steps:

1. Write the event handler or the job.
2. Implement the interface and create a manager or infrastructure layer interface if needed.
3. Implement the infrastructure layer interface in the same project if no additional dependencies are required.
4. Implement the infrastructure layer interface in another project if it requires third-party dependencies (e.g., adding gRPC, MongoDB, or MySQL in the new project).

**Example:** The P2P network module.

The networking code is defined across two modules: **CoreOSAElfModule** and **GrpcNetworkModule**. The OS core defines and implements the application service (used by other components of the node) as it is part of the application/domain logic. The infrastructure layer (like the server endpoint) is defined in the OS core modules but implemented in another project that relies on a third party, such as gRPC.

## Testing

When writing a new component, event handler, or method, it is important for AElf's quality to consider the corresponding unit test. As previously mentioned, we have a solution-wide test folder where we place all the tests.
39 changes: 39 additions & 0 deletions docs/Architecture/Core/Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Application Pattern

We follow generally accepted good practices in programming, especially those that align with our project needs. Some of these practices are specific to C#, while others pertain to general Object-Oriented Programming (OOP) principles like **SOLID** and **DRY**.

## Domain-Driven Design (DDD)

Although it's uncommon for blockchain projects, we adhere to a Domain-Driven Design (DDD) approach in our development style. This approach is partly due to the compatibility of our main framework with DDD, making it a natural design philosophy for us.

### Key Points of DDD:

- **Four Traditional Layers:**
- **Presentation:** This corresponds to any type of dApp (Decentralized Application).
- **Application:** Exposed services mapped to different domains.
- **Domain:** Specific events related to our blockchain system and domain objects.
- **Infrastructure:** Third-party libraries for database, networking, etc.

For more details, refer to our [coding standards](https://github.com/AElfProject/AElf/issues/1040) listed in our GitHub issue.

## Frameworks and Libraries

The primary programming language used for developing aelf is **C#**, and it's built with the **dotnet core** framework. This choice was made due to the excellent performance of the framework. Dotnet core is also cross-platform, supporting Windows, MacOS, and Linux. It is a dynamic and open-source framework, offering many advantages of modern development patterns and backed by major players in the IT industry.

### Key Frameworks and Tools:

- **ABP Framework:**

- We use the [ABP](https://abp.io/documents/abp/latest/Index) application framework.
- It is a natural fit for building blockchain nodes, which are sets of endpoints like RPC, P2P, and cross-chain communication, with higher-level protocols on top.

- **Testing:**

- We use the **XUnit** framework for unit tests.
- Additionally, we have custom-made frameworks for testing smart contracts.

- **Communication:**
- For cross-chain and P2P network communication, we use **gRPC**.
- We use **Protobuf** for serialization purposes.

By following these practices and utilizing these tools, we ensure that our development process is efficient, reliable, and scalable.
Binary file added docs/Architecture/Core/node-archi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions docs/Architecture/Cross Chain/Architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Side Chain and Main Chain Node Documentation

## Overview

Conceptually, a side chain node and a main chain node are similar. Both are independent blockchains with their own peer-to-peer networks and possibly their own ecosystems. This setup can be implemented at multiple levels. In terms of peer-to-peer networks, all side chains operate in parallel to each other but are linked to a main chain node through a cross-chain communication mechanism.

Through this link, messages are exchanged, and indexing is performed to ensure that transactions from the main chain or other side chains are verifiable in the side chain. Implementers can use AElf libraries and frameworks to build these chains.

**Key Role of the Main Chain:**

- The main chain's primary purpose is to index the side chains.
- Only the main chain indexes data about all the side chains.
- Side chains are independent and do not have knowledge about each other.
- When side chains need to verify transactions from other chains, they use the main chain as a bridge to provide cross-chain verification information.

## Node Level Architecture

In the current architecture, both the side chain node and the main chain node have one server and exactly one client. This forms the basis for AElf's two-way communication between the main chain and side chains. Both the server and the client are implemented as node plugins (a node has a collection of plugins). Interaction (listening and requesting) can start once both nodes are running.

![Node Level Architecture](side-chain-nodes.png)

The diagram above illustrates two nodes run by an entity: one main chain node and one side chain node. Note that the nodes don't have to be in the same physical location.

### Side Chain Lifetime

The lifetime of a side chain involves the following steps:

- **Request side chain creation.**
- **Wait for acceptance on the main chain.**
- **Start and initialize the side chain.** It will be indexed by the main chain automatically.
- **Cross-chain verification** is allowed only if the side chain is indexed correctly.

### Communication

When the side chain node starts, it will initiate various communications. Here are the main points of the protocol:

- **Initial Request:** When the side chain node starts for the first time, it will request the main chain node for a chain initialization context.
- **Handshake:** After initialization, the side chain is launched and will perform a handshake with the main chain node to signal that it is ready to be indexed.
- **Indexing Process:** During indexing, information about irreversible blocks will be exchanged between the side chain and the main chain. The main chain will write the final result in a block, which is calculated using the cross-chain data from all side chains. The side chain will also record the data in a contract from the main chain.

AElf provides cross-chain communication implementation using gRPC.

```protobuf
rpc RequestIndexingFromParentChain (CrossChainRequest) returns (stream acs7.ParentChainBlockData) {}
rpc RequestIndexingFromSideChain (CrossChainRequest) returns (stream acs7.SideChainBlockData) {}
```

### Cache

For effective indexing, a cache layer is used to store cross-chain data received from remote nodes, ensuring the data is available and correct. The cross-chain data is cached by chain ID and block height with a count limit. The cache layer provides the data if it is cached when the node needs it, thus decoupling the communication part and node running logic.

### Cross-Chain Contract

Apart from the data in blocks, most cross-chain data will be stored by the cross-chain contract. Cross-chain data cached by the node is packed into transactions during the mining process, and the calculated result is stored by the contract. The cross-chain data in the block is the result of side chain indexing calculations from this contract. Cross-chain verification can only work correctly with data from this contract.

### Data Flow

Conceptually, the node operates as described in the following diagram. The main/side chain node receives cross-chain data from the other side and stores it in local memory. The indexing transaction is packed by the miner, and the cross-chain data is written into the `State` through the `Crosschain Contract`.

![Data Flow](architecture-node.png)
140 changes: 140 additions & 0 deletions docs/Architecture/Cross Chain/Cross Chain Transfer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Cross Chain Transfer

Cross chain transfer is one of the most commonly used cases when it comes to cross chain verification. AElf already supports cross chain transfer functionality in its contract. This section will explain how to transfer tokens across chains. It assumes a side chain is already deployed and has been indexed by the main chain.

The transfer process will always use the same contract methods and follow these two steps:
- **Initiate the transfer**
- **Receive the tokens**

## Prepare

There are a few preparation steps required before a cross chain transfer, which need to be done only once for each chain. If these steps are already completed, you can skip this part.

Let's say you want to transfer token **FOO** from chain **A** to chain **B**. Before you start, make sure you understand how cross chain transaction verification works. Any input containing `MerklePath` in the following steps means that cross chain verification is needed. Refer to the [cross chain verification documentation](crosschain-verification) for more details.

- **Validate `Token Contract` address on chain A**

Send transaction `tx_1` to the `Genesis Contract` with the method `ValidateSystemContractAddress`. You need to provide `system_contract_hash_name` and the address of the `Token Contract`. `tx_1` should be successfully packed in the block.

```protobuf
rpc ValidateSystemContractAddress(ValidateSystemContractAddressInput) returns (google.protobuf.Empty){}
message ValidateSystemContractAddressInput {
aelf.Hash system_contract_hash_name = 1;
aelf.Address address = 2;
}
```

- **Register the token contract address of chain A on chain B**

Create a proposal for the `RegisterCrossChainTokenContractAddress` for the default parliament organization on chain B. Refer to the [Parliament contract documentation](../../reference/smart-contract-api/parliament) for more details. Apart from cross chain verification context, you also need to provide the origin data of `tx_1` and the `Token Contract` address on chain A.

```protobuf
rpc RegisterCrossChainTokenContractAddress (RegisterCrossChainTokenContractAddressInput) returns (google.protobuf.Empty) {}
message RegisterCrossChainTokenContractAddressInput {
int32 from_chain_id = 1;
int64 parent_chain_height = 2;
bytes transaction_bytes = 3;
aelf.MerklePath merkle_path = 4;
aelf.Address token_contract_address = 5;
}
```

- **Validate `TokenInfo` of FOO on chain A**

Send transaction `tx_2` to the `Token Contract` with the method `ValidateTokenInfoExists` on chain A. You need to provide `TokenInfo` of FOO. `tx_2` should be successfully packed in the block.

```protobuf
rpc ValidateTokenInfoExists(ValidateTokenInfoExistsInput) returns (google.protobuf.Empty){}
message ValidateTokenInfoExistsInput {
string symbol = 1;
string token_name = 2;
int64 total_supply = 3;
int32 decimals = 4;
aelf.Address issuer = 5;
bool is_burnable = 6;
int32 issue_chain_id = 7;
}
```

- **Create token FOO on chain B**

Send transaction `tx_3` to the `Token Contract` with the method `CrossChainCreateToken` on chain B. You need to provide the origin data of `tx_2` and the cross chain verification context of `tx_2`.

```protobuf
rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) {}
message CrossChainCreateTokenInput {
int32 from_chain_id = 1;
int64 parent_chain_height = 2;
bytes transaction_bytes = 3;
aelf.MerklePath merkle_path = 4;
}
```

## Initiate the Transfer

On the token contract of the source chain, the `CrossChainTransfer` method is used to trigger the transfer:

```protobuf
rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) { }
message CrossChainTransferInput {
aelf.Address to = 1;
string symbol = 2;
sint64 amount = 3;
string memo = 4;
int32 to_chain_id = 5;
int32 issue_chain_id = 6;
}
```

### The fields of the input:

- **to**: the target address to receive the token
- **symbol**: the symbol of the token to be transferred
- **amount**: the amount of the token to be transferred
- **memo**: a memo field for this transfer
- **to_chain_id**: the destination chain ID where the tokens will be received
- **issue_chain_id**: the chain ID where the token was issued

## Receive on the Destination Chain

On the destination chain where the tokens need to be received, the `CrossChainReceiveToken` method is used to trigger the reception:

```protobuf
rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) { }
message CrossChainReceiveTokenInput {
int32 from_chain_id = 1;
int64 parent_chain_height = 2;
bytes transfer_transaction_bytes = 3;
aelf.MerklePath merkle_path = 4;
}
rpc GetBoundParentChainHeightAndMerklePathByHeight (aelf.Int64Value) returns (CrossChainMerkleProofContext) {
option (aelf.is_view) = true;
}
message CrossChainMerkleProofContext {
int64 bound_parent_chain_height = 1;
aelf.MerklePath merkle_path_from_parent_chain = 2;
}
```

### The fields of the input:

- **from_chain_id**: the source chain ID from which the cross chain transfer was launched

- **parent_chain_height**: the height of the block on the main chain that contains the `CrossChainTransfer` transaction (for main chain to side chain transfer). For side chain to side chain or side chain to main chain transfer, it is the result of `GetBoundParentChainHeightAndMerklePathByHeight` (input is the height of the `CrossChainTransfer`), accessible in the `bound_parent_chain_height` field.

- **transfer_transaction_bytes**: the serialized form of the `CrossChainTransfer` transaction.

- **merkle_path**: obtained from the source chain. The construction of merkle path data differs among cases:
- **Main chain to side chain transfer**: merkle path from the main chain’s web API `GetMerklePathByTransactionIdAsync` (with `CrossChainTransfer` transaction ID as input).
- **Side chain to side chain or side chain to main chain transfer**:
- merkle path from the source chain’s web API `GetMerklePathByTransactionIdAsync` (with `CrossChainTransfer` transaction ID as input).
- output of `GetBoundParentChainHeightAndMerklePathByHeight` method in the `CrossChain Contract` (with `CrossChainTransfer` transaction’s block height as input). Path nodes are in the `merkle_path_from_parent_chain` field of the `CrossChainMerkleProofContext` object.
- Concatenate the above two merkle paths.
Empty file.
20 changes: 20 additions & 0 deletions docs/Architecture/Cross Chain/Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Introduction

One of the major issues with current blockchain systems is scalability. This is mainly due to **congestion problems** in existing blockchains. The core problem is that when a single chain needs to sequentially order and process transactions, a popular dApp consuming a lot of resources can negatively impact other dApps.

To address this issue, AElf introduced side chains in its initial design. The concept is that each side-chain handles one or more similar business scenarios, distributing different tasks across multiple chains to improve overall processing efficiency.

## Key Points:
- **Independent and Specialized**: Side-chains are designed to be independent and specialized, ensuring that the dApps running on them perform efficiently and smoothly.
- **Network Link**: There is a network link between the main-chain node and side-chain nodes, with communication indirectly facilitated through a Merkle root.

![image](introduction-topology.png)

*The diagram above illustrates the conceptual idea behind side chains.*

Side chains are isolated but still need a way to interact with each other. To enable cross-chain verification scenarios, AElf introduces a communication mechanism through **Merkle roots** and **indexing**.

## Overview
The following sections of this documentation will provide:
- An overview of the architecture of AElf's side chains.
- A guide explaining how to set up a main-chain and a side chain node.
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Architecture/Cross Chain/merkle-path.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Architecture/Cross Chain/merkle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 0 additions & 15 deletions docs/tutorials/RunningASideChain/index.md

This file was deleted.

0 comments on commit 6a46e13

Please sign in to comment.