-
Notifications
You must be signed in to change notification settings - Fork 12
Description
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;
}
