Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sovereign Chains Cross-Chain Execution #1033

Open
wants to merge 19 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/sovereign/cross-chain-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Introduction

When we take a look at the blockchain industry, we observe a segregated ecosystem lacking cohesion, interoperability, teamwork. Many strive to reach the top independently. The vision lead to the Blockchain Revolution, knows as “Web3” — a new era of the internet that is user-centered, emphasizing data ownership and decentralized trust.

Sovereign Chains will dismantle the barriers between isolated blockchain networks by allowing smart contracts to seamlessly interact across different Sovereign Chains and the main MultiversX chain.
This cross-chain interoperability is crucial for fostering an environment where decentralized apps (dApps) can utilize functionalities or assets from across the ecosystem.

## What is Cross-Chain Execution?

Cross-Chain Execution is the ability of smart contracts or decentralized applications on one blockchain to invoke actions on another blockchain. This feature allows for seamless communication and interaction between different blockchain networks, enabling developers to build applications that are chain agnostic.


## Cross-Chain Execution within Sovereign Chains

This feature is enabled by using multiple smart contracts, each one with its unique role and set of functionalities. Here is the high-level description for some of the cross chain smart contracts:

#### ESDT-Safe
All the heavy lifting is being done by the *ESDT-Safe* Smart Contract. This is the bridge contract, it will facilitate the transfer and mapping of assets and funds between Sovereign Chains and the MultiversX mainchain.

There are two modules implemented in the bridging mechanism inside any Sovereign Chains, [*From Sovereign*](from-sovereign.md) and [*To Sovereign*](to-sovereign.md). We will go deeper into the most important endpoints in both modules in the following sections.

:::note
The naming for those modules has been chosen this way to represent the direction of the execution. In the following sections we will be referring to `FromSovereign` as the execution starts within the Sovereign Chain and `ToSovereign` as the destination of the execution is a Sovereign Chain.
:::

#### Fee-Market
Since every Sovereign Chain will have a customizable fee logic, it was paramount that this configuration had to be separated into a different contract. Rules such as: fee token, fee percentages, whitelists are all stored inside this contract.

#### Header-Verifier
All the *BLS keys* of all the validator will be stored inside this contract. The main role for the Header-Verifier contract is to verify signatures for the operations that are created inside the Sovereign Chain.

> The source code can be found at the official [MultiversX Sovereign Chain SCs repository](https://github.com/multiversx/mx-sovereign-sc).

164 changes: 164 additions & 0 deletions docs/sovereign/from-sovereign.md
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Execution starting from a Sovereign Chain
![From Sovereign](../../static/sovereign/from-sovereign.png)
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

For the user — whether an External Owned Account (EOA) like a user wallet or another smart contract — procedure is simple. An address will be able to perform a multiTransfer deposit for various types of tokens:
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For the user — whether an External Owned Account (EOA) like a user wallet or another smart contract — procedure is simple. An address will be able to perform a multiTransfer deposit for various types of tokens:
For the user — whether an External Owned Account (EOA) like a user wallet or another smart contract — procedure is simple. An address will be able to perform a multi tokens transfer with various types of tokens:

- Fungible Tokens
- (Dynamic) Non-Fungible Tokens
- (Dynamic) Semi-Fungible Tokens
- (Dynamic) Meta ESDT Tokens

When making the deposit, the user specifies:
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
1. A destination address on the Sovereign Chain
2. `TransferData` if the execution contains a smart contract call, which contains gas, function and arguments

For each deposit in the ESDT-Safe smart contract inside a Sovereign Chain, an outgoing operation will be sent to the bridge service that calls the Header-Verifier and ESDT-Safe contracts in the MultiversX MainChain.

:::note
Here is the [link](https://github.com/multiversx/mx-sovereign-sc/blob/main/esdt-safe/src/to_sovereign/create_tx.rs) to the `deposit` endpoint
:::

Each action that can be executed remotely through this contract is called an *Operation*. The endpoint responsible for executing those operations is called `execute_operations`.
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

#### Execution from inside the Sovereign Chain to the MainChain flow
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
1. User deposits token to the ESDT-Safe smart contract on Sovereign.
2. Outgoing *Operations* are created at the end of the round.
3. Validators sign all the outgoing *Operations*.
4. Leader sends *Operations* to the bridge service.
5. Bridge service sends the *Operations* to the Header-Verifier for registration and verification, and then to ESDT-Safe for execution.
6. At the end of the execution success/fail, a confirmation event will be added which will be received in sovereign through the observer and then the cross chain transfer will be completed.

### Header-Verifier SC
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

As mentioned in the [Introduction](cross-chain-execution.md) the Header-Verifier smart contract is to verify signatures, store the *BLS Keys* of the validators and register incoming *Operations*.
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
```rust
#[endpoint(registerBridgeOps)]
fn register_bridge_operations(
&self,
signature: BlsSignature<Self::Api>,
bridge_operations_hash: ManagedBuffer,
operations_hashes: MultiValueEncoded<ManagedBuffer>,
)
```

Any transaction before being executed has to be registered in this smart contract. The reason behind registering any incoming *Operation* is to create a history of all already registered *Operations* and with that an already registered *Operation* doesn't need to be re-registered and re-executed by the validators.
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

The registering endpoint follows this flow:
1. If the incoming `bridge_operations_hash` is not in the `hash_of_hashes_history` storage mapper, otherwise, the endpoint will return a panic.
2. If the hash of all `operations_hashes` and `bridge_operations_hash` are equal, otherwise, the endpoint will return a panic.
3. All the `operations_hashes` are registered in the smart contract's storage with the `OperationsHashStatus::NotLocked` status.
4. The `bridge_operations_hash` is inserted in the `hash_of_hashes_history` storage mapper.
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

```rust
#[endpoint(lockOperationHash)]
fn lock_operation_hash(&self, hash_of_hashes: ManagedBuffer, operation_hash: ManagedBuffer)
```

The Header-Verifier has a system in place for locking *Operation* hashes. Locking those registered hashes prevents any unwanted behaviour when executing or removing an *Operation* hash. Remember that the execution of *Operations* can only be done by the ESDT-Safe smart contract. This endpoint when called will follow this flow:

1. Check if the caller is the ESDT-Safe smart contract.
2. Check if the *Operation* is registered.
3. If the hash is not locked set the status in the storage as locked or else return panic.

:::note
The hash can be in two different states: `OperationHashStatus::NotLocked` or `OperationHashStatus::Locked`
:::

```rust
#[endpoint(removeExecutedHash)]
fn remove_executed_hash(&self, hash_of_hashes: &ManagedBuffer, operation_hash: &ManagedBuffer)
```

After registering and executing an *Operation* the status of the hash associated to it must be removed from the Header-Verifier's internal storage. This endpoint will be called by the ESDT-Safe smart contract after the execution of the *Operation* is successful. The steps are pretty clear:

1. Check if the caller is the ESDT-Safe smart contract.
2. Remove the status of the hash from storage.


Before talking about the logic of the endpoint, let’s give some more context about the *Operation*.
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

:::note
The source code for the following structures can be found [here](https://github.com/multiversx/mx-sovereign-sc/blob/main/common/transaction/src/lib.rs)
:::

```rust
#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, ManagedVecItem, Clone)]
pub struct Operation<M: ManagedTypeApi> {
pub to: ManagedAddress<M>,
pub tokens: ManagedVec<M, OperationEsdtPayment<M>>,
pub data: OperationData<M>,
}
```

- `to`: specifies the destination of the *Operation*
- `tokens`: represents one or more token transfers associated with the operation
- `data`: encapsulates additional instructions or parameters that guide the execution of the operation

```rust
pub struct OperationEsdtPayment<M: ManagedTypeApi> {
pub token_identifier: TokenIdentifier<M>,
pub token_nonce: u64,
pub token_data: EsdtTokenData<M>,
}
```

This struct describes a single token transfer action within an *Operation*. Each Operation can have one or more of such payments, with that enabling the transfer of a variety of tokens during a cross-chain transaction.

- `token_identifier`: used for the identification of the token
- `token_nonce`: if the token is Non-Fungible or Semi-Fungible, it will have a custom nonce, if not the value will be 0
- `token_data`: a structure holding metadata and additional token-related details used for minting, token creation (for more details about this structure, please visit the official ESDT Documentation)
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

:::note
You can use the [System SC API](../developers/developer-reference/sc-api-functions#get_esdt_token_data) method as reference.
:::
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

```rust
pub struct OperationData<M: ManagedTypeApi> {
pub op_nonce: TxId,
pub op_sender: ManagedAddress<M>,
pub opt_transfer_data: Option<TransferData<M>>,
}
```

`OperationData` encapsulates the needed information for the *Operation* that needs to be executed. This isn’t just another data definition, we’ve already seen data-related fields elsewhere. Instead, it centralizes the contextual information that *Operation* needs before, during, and after execution.

- `op_nonce`: is used for the identification of each *Operation*
- `op_sender`: represents the original sender of the *Operation*
- `opt_transfer_data`: an optional `TransferData` field, when present, contains details about the cross-chain execution of another Smart Contract

```rust
pub struct TransferData<M: ManagedTypeApi> {
pub gas_limit: GasLimit,
pub function: ManagedBuffer<M>,
pub args: ManagedVec<M, ManagedBuffer<M>>,
}
```

`TransferData` represents the description of the remote execution of another Smart Contract.

- `gas_limit`: specifies the needed gas for the execution of all other endpoints.
- `function`: the name of the endpoint that will be executed.
- `args`: the arguments for the calls.


:::note
The source code for the endpoint can be found [here](https://github.com/multiversx/mx-sovereign-sc/blob/main/esdt-safe/src/from_sovereign/transfer_tokens.rs)
:::
### Executing an *Operation*

```rust
#[endpoint(executeBridgeOps)]
fn execute_operations(
&self,
hash_of_hashes: ManagedBuffer,
operation: Operation<Self::Api>
)
```
- `hash_of_hashes`: hash of all hashes of the operations that were sent in a round
- `operation`: the details of the cross-chain execution

1. Calculate the hash of the *Operation* received as a parameter.
2. Verify that the given *Operation’s* hash is registered by the Header-Verifier Smart Contract.
3. Mint tokens or get them from the account.
4. Distribute the tokens.
5. Emit confirmation event or fail event if needed.
48 changes: 48 additions & 0 deletions docs/sovereign/to-sovereign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Execution going to a Sovereign Chain
![To Sovereign](../../static/sovereign/to-sovereign.png)
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved

The ability to transfer tokens from the Main Chain to any Sovereign Chain is essential, since every Sovereign can connect to the Main MultiversX Chain. As a result, the customizable Sovereign can leverage any token available on the default network.

The transfer of tokens is done by the *ESDT-Safe* contract after calling the `deposit` endpoint inside the `to-sovereign` module.

#### Main Chain deposit to Sovereign Chain depositing flow
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
1. User deposits the tokens he wishes to transfer in the ESDT-Safe contract deployed on the Main Chain.
2. An observer is monitoring the Main Chain.
3. Sovereign network receives extended shard header.
4. Incoming transactions processor handles and processes the new transaction.

### Depositing
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
```rust
#[payable("*")]
#[endpoint]
fn deposit(
&self,
to: ManagedAddress,
optional_transfer_data: OptionalValueTransferDataTuple<Self::Api>,
)
```

One key aspect of cross chain transfers from MultiversX Main Chain to a Sovereign Chain is being able to transfer tokens and also execute a smart contract call within single transaction. This endpoint first checks if the current Sovereign Chain has any enabled fees.

axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
- There can be maximum of 10 transfers per deposit.
- The gas limit must be under the specified limit.
- The endpoint that has to be executed is not blacklisted.

At the end of the `deposit()` endpoint, an event will be emitted and then the bridging process is complete.


```rust
#[event("deposit")]
fn deposit_event(
&self,
#[indexed] dest_address: &ManagedAddress,
#[indexed] tokens: &MultiValueEncoded<MultiValue3<TokenIdentifier, u64, EsdtTokenData>>,
event_data: OperationData<Self::Api>,
)
```

This log event will emit the destination address and the tokens which will be transferred to the Sovereign Chain.

:::note
Here is the [link](https://github.com/multiversx/mx-sovereign-sc/blob/main/esdt-safe/src/to_sovereign/create_tx.rs) to the `deposit` endpoint.
:::
9 changes: 9 additions & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,15 @@ const sidebars = {
"sovereign/solana-l2",
],
},
{
type: "category",
label: "Cross-Chain Execution",
items: [
"sovereign/cross-chain-execution",
"sovereign/to-sovereign",
"sovereign/from-sovereign",
]
},
"sovereign/validators",
],
},
Expand Down
Binary file added static/sovereign/from-sovereign.png
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
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 static/sovereign/to-sovereign.png
axenteoctavian marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading