Skip to content

ETP8: Space Bridge #8

@svanurin

Description

@svanurin

About Space Bridge

Metadata

  • Status: draft
  • Date: 2022 Aug
  • Author: kzhidanov

Summary

This document specifies functioning of a cross chain bridge between Enecuum and other blockchains that support smartcontract functionality.

Basics

The bridge allows to transfer funds between various blockchains. Its functioning is backed by smart contracts deployed in each chain and by a coalition of nodes, that manage assets via smart-contracts.

General description of the swap procedure

The way of funds transfer depends on the origin of the asset. Let's distinguish original asset and its wrapped copy. For example, USDT ERC20 and ENQ tokens are being original in the Ethereum and Enecuum networks respeсtively, and their wrapped copies will be created in case of bridge swap into other networks. Thus there is a different types of asset transfers (lock/burn in the sender network, creation/minting/unlock). The type of transfer is determined automatically based on the asset's origin and the transfer destination.

To avoid liquidity fragmentation the rule is being set: if in a given network an assent was minted by the bridge smart contract then the asset must be burnt during the transfer from that network, and on the other hand, if it was locked than it must be unlocked during an incoming transfer. Here is a three networks and one asset example with the asset origin in the Network1:

Network1 -> Network2 -- LOCK -> MINT
Network2 -> Network1 -- BURN -> UNLOCK
Network2 -> Network3 -- BURN -> MINT
Network1 -> Network3 -- LOCK -> MINT
Network3 -> Network1 -- BURN -> UNLOCK
Network3 -> Network2 -- BURN -> MINT

Fractional values

On incoming funds transactions (lock method) smart contract in the senders network performs following checks. The transfer is possible if all of them are success.

dst_token_decimals = MIN(origin_token_decimals, target_network_decimals)
IF dst_token_decimals < src_token_decimals
    IF  amount % (10 ** (src_decimals - dst_decimals)) == 0
    THEN   true
    ELSE   false
ELSE true

Value dst_amount is calculated as

IF  dst_decimals < src_decimals
THAN    dst_amount = amount / (10 ** (dst_decimals - src_decimals))
ELSE dst_amount = amount * (10 ** (src_decimals - dst_decimals))

Enecuum network features

Token types and tickers

From Ethereum-compatible networks only ERC-20 standard tokens are handled by the bridge. With the transfer from Enecuum to Ethereum-compatible network the token of ERC-20 standard is being created.

During wrapped token creation in Enecuum network the owner of the wrapped token is set to 030000000000000000000000000000000000000000000000000000000000000000, indubitably not a user-owned public key. Thus the access to mint and burn functions of the token is possible only via smart-contracts.

In Enecuum network ticker of the wrapped token is set as "SBXXXX", where XXXX are the first four characters of the original tokens ticker (AKA symbol).

Smartcontracts

Each Space Bridge enabled blockchain has a deployed multisig smartcontract that allows to perform predefined actions (mint, burn, token issue, token transfer) on receiving enough confirming transactions from authorized actors. Multisig contracts contain data on bridge state and configuration structure.

Parameters:

  • network_id - network id (namespace is Space Bridge specific)
  • threshold - number of validators signatures, required for assets transfers

Array called minted contains data on issued wrapped assets {wrapped_hash, origin, origin_hash, origin_decimals}, where

  • wrapped_hash - hash of wrapped token
  • origin - network id of the original asset
  • origin_hash - hash of the original asset
  • origin_decimals - decimals of the original asset

Array called known_networks contains data on networks connected to the bridge {id, decimals}, where

  • id - network id
  • decimals - maximum possible token decimals value for the network

Array called transfers contains data on performed transfers {src_address, dst_address, src_network, src_hash, nonce}, where

  • src_address - sender address in the source network
  • dst_address - receiver address in the destination network
  • src_network - source network id
  • src_hash - source network token hash
  • nonce - subsequent number of the transfer

Array called validators contains validators public keys.

Methods

network_id

parameters:

  • none

Method returns network_id value.

transfers

Parameters:

  • dst_address

Method returns values from transfers array, filtered by dst_address value.

minted

Parameters:

  • wrapped_hash

Method returns values from minted array, filtered by wrapped_hash value.

Lock

Parameters:

  • dst_address - receiver address in the destination network
  • dst_network - destination network id
  • amount - amount to transfer
  • nonce - last same source and destination locked transfer's nonce plus one

According to the algorithm, smart contract either transfers the amount of assets to its account or burns it.

The maximum possible destination decimals are checked: if the amount cannot be writted in destination network without loss, the lock call is rejected.

IF *hash* IS_IN *contract.minted.wrapped_hash*
THEN burn(*hash*, *amount*)
ELSE lock(*hash*, *amount*)

Claim

Parameters:

  • ticket - a structure containing information about locked assets in the source network
  • signatures - an array of validator identifiers and signatures

The signatures array contains structures of {validator_id, validator_sign}:

  • validator_id - validator's public key
  • validator_sign - validator's signature

When creating a token, it is assigned the minimum from ticket's origin_decimals and networks maximum possible precision.

check_signature_array

FIND IN *transfers* {*src_address*, *dst_address*, *src_network*}
IF *nonce* != search_result.nonce + 1
THEN EXIT(invalid_nonce)

IF *contact.network_id* NOT_EQUAL_TO *ticket.dst_network*
THEN EXIT(invalid_network_identifier)

IF *origin_network* EQUAL TO *dst_network*
THEN transfer_asset(*dst_address*, *amount*, *origin_hash*)
ELSE *new_hash* = create_token(*amount*)
         INSERT {*new_hash*, *origin_hash*, *origin_network*} INTO *minted*
         transfer_asset(*dst_address*, *amount*, *new_hash*)
      
update the transfers array by key {src_address, dst_address, src_network, src_hash}

Claim_init

Relevant for Enecuum network
The claim_init method adds a ticket_hash to the transfers array.

Parameters:

  • ticket - a structure containing information about locked assets in the source network
  • ticket_hash - ticket hash
Validate(*ticket_hash*)

INSERT {ticket.src_address, ticket.dst_address, ticket.src_network, ticket.src_hash, ticket.nonce, ticket_hash} INTO `transfers` 

Claim_confirm

Relevant for Enecuum network
The smart contract contains an array of confirmations with fields {validator_id,validator_sign,ticket_hash}

Parameters:

  • validator_id - validator's public key
  • validator_sign - validator's signature
  • ticket_hash - transfer ID

When the threshold number of signatures is reached, the claim procedure is performed directly in the claim method, described above.

Network Nodes

Each of the network nodes operates independently of the others. The node's work consists of monitoring the state of each network and confirming the required actions in the required blockchain (lock/unlock/mint/burn) upon request.

Choreography and Algorithms

To move assets, the user sends the assets to a smart contract in the corresponding blockchain. The user sequentially sends a request to obtain confirmation of the fact of sending assets to known validators. Each validator independently verifies the correctness of the operation and, in case of successful verification, returns confirmation to the user with its signature. After receiving a sufficient number of confirmations, the user forms a transaction and sends it to the smart contract in the corresponding network.

Notify

After performing the lock action, the user sends the confirmed transaction hash in the message notify in the blockchain.

Confirm

The validator creates the confirmation structure based on the transaction passed in the notify message and the smart contract data in the sending and destination networks, signs it with its private key (a share of the private key).

To create the structure, the validator queries smart contracts in the sending and destination networks.

confirmation structure:

  • ticket - transfer data structure
  • validator_id - validator's public key
  • ticket_hash - ticket hash, calculated by hashing the following field: ticket_hash = HASH( dst_address | dst_network | amount | src_hash | src_address | src_network | origin_hash | origin_network | nonce | ticker | origin_decimals | name)
  • validator_sign - validator's signature placed on ticket_hash

ticket structure:

  • dst_address - receiver's address in the destination network - parameter of the lock method call in the sending network
  • dst_network - destination network ID - parameter of the lock method call in the sending network
  • amount - amount - based on the lock method call parameter in the sending network
  • src_hash - hash of the token in the sending network - parameter of the lock method call in the sending network
  • src_address - sender's address in the sending network - sender of the transaction in the sending network
  • src_network - sending network ID - network_id parameter in the sending network
  • origin_hash - hash of the token in the origin network - determined based on the contents of the minted array in the sending network
  • origin_network - origin network ID - determined based on the contents of the minted array in the sending network
  • origin_decimals - origin token decimals - determined by validators
  • nonce - transfer sequence number - determined based on the contents of the transfers array in the destination network
  • ticker - ticker of the token to be minted
  • name - name of the token to be minted

transfer_id = HASH(dst_address | dst_network | nonce | src_address | src_hash | src_network);

Calculation example for NodeJS

let calculate_transfer_id = function(ticket){
  let param_names = ["dst_address", "dst_network", "nonce", "src_address", "src_hash", "src_network"];
  let params_str = param_names.map(v => crypto.createHash('sha256').update(ticket[v].toString().toLowerCase()).digest('hex')).join("");
  let transfer_id = crypto.createHash('sha256').update(params_str).digest('hex');
  return transfer_id;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions