Skip to content

Commit

Permalink
Merge pull request #329 from synapseweb3/chore/update-readme
Browse files Browse the repository at this point in the history
chore: update README.md
  • Loading branch information
jjyr authored Sep 15, 2023
2 parents 87ac8f5 + 33b59ba commit 6b5aee4
Show file tree
Hide file tree
Showing 15 changed files with 1,477 additions and 1,177 deletions.
2,287 changes: 1,208 additions & 1,079 deletions Cargo.lock

Large diffs are not rendered by default.

153 changes: 83 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,94 @@

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?logo=apache)](LICENSE)

Forcerelay aims to allow [CKB](https://github.com/nervosnetwork/ckb)
and chains built on [Axon](https://github.com/axonweb3/axon) gain the ability to
interact with Ethereum and Cosmos-SDK chains via [IBC protocol](https://github.com/cosmos/ibc).

Forcerelay is based on [Hermes](https://github.com/informalsystems/hermes), which helps Cosmos-SDK chains to interact with each other. Many thanks to them!

## **WARNING**

This repo is still in early stage. Your issues or PRs are welcome.

## Using Forcerelay/Eth

Create or modify `~/.hermes/config.toml`. Here is an example:

```toml
[global]
log_level = 'trace'

[mode]

[mode.clients]
enabled = true
refresh = true
misbehaviour = true

[mode.connections]
enabled = false

[mode.channels]
enabled = false

[mode.packets]
enabled = true
clear_interval = 100
clear_on_start = true
tx_confirmation = true

[[chains]]
id = 'ibc-eth-1'
genesis_time = 1606824023
genesis_root = "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"
initial_checkpoint = "0x6c668d888d2b892bf26c25ee937ed43b48048dedda971ff33ebaaae7b2bd3890"
key_name = 'relayer_eth_wallet'
rpc_addr_pool = [
"https://www.lightclientdata.org",
]
rpc_port = 8545
[chains.forks]
genesis = { epoch = 0, fork_version = "0x00000000" }
altair = { epoch = 74240, fork_version = "0x01000000" }
bellatrix = { epoch = 144896, fork_version = "0x02000000" }
capella = { epoch = 194048, fork_version = "0x03000000" }

[[chains]]
id = 'ckb-multi-client-4-1'
ckb_rpc = "https://testnet.ckbapp.dev"
ckb_indexer_rpc = "https://testnet.ckbapp.dev"
lightclient_contract_typeargs = "0x75ca34f9f1c28cf16d160fc485d5ed4a2a8f34424ec5854a7579ca82e72b7671"
lightclient_lock_typeargs = "0x8d5300c03081b19a28a30d2e8202a467ab19fafa5285c86896db035e783186d5"
minimal_updates_count = 1
key_name = "relayer_ckb_wallet"
data_dir = "./ckb_mmr_storage_multi_client_4_1"
client_type_args = { cells_count = 4, type_id = "0x673e557da4c8381638fc808956aa27e384cf66d9a63899a6e4e932c2395f7a40" }

Forcerelay is an IBC-Compatible bridge that aims to connect the Nervos ecosystem and the Cosmos ecosystem, and it also serves as a standard cross-chain solution within the Nervos ecosystem.

Forcerelay is built on [Hermes](https://github.com/informalsystems/hermes), and will continuously adapt to newer `Hermes` versions.

At present, Forcerelay exclusively supports Axon, CKB, and Cosmos-SDK chains, prioritizing their seamless operation. While it currently doesn't encompass other well-established EVM chains, such as Ethereum, Arbitrum, and BSC, this is primarily due to the intricacies of running a light-client from these chains on EVM. Nevertheless, our roadmap includes plans for implementing mainstream EVM chains, and we'll keep you informed on our progress in this regard.

## Progress Tracking
Forcerelay is under active development, and we welcome any issues and pull requests. Here's an overview of our current progress:
- [x] Implement connection and channel layer of IBC protocol between Axon and CKB
- [ ] Upgrade the underline Hermes from v1.4.0 to the latest v1.6.0
- [ ] Implement packet layer of IBC protocol between Axon and CKB
- [ ] Implement entire IBC protocol between Axon and Cosmos-SDK chains
- [ ] Implement entire IBC protocol between CKB and Cosmos-SDK chains

## Architecture Design
Cosmos IBC is a cross-chain protocol designed based on light-client technology. In case of homogeneous chains, it's supposed to implement only one light-client module to run the protocol. However, in case of Forcerelay, chains are non-homogeneous, so we have to implement at least three light-client modules to run IBC protocol between Axon, CKB and Cosmos.

Contract development environment is sensitive to the blockchain's VM implementation, which means the different VM implementation requires us to provide different light-client solutions, even the solutions come from the same blockchain.

High level diagram of Forcerelay:
![Alt text](docs/Forcerelay.jpg)

## Usage Guidance
Take the IBC cross-chain betwwen Axon and CKB as an example.

### Contract Deployment on Axon
Before deploying [ibc-solidity-contract](https://github.com/synapseweb3/ibc-solidity-contract) on Axon, please make sure Node.js and Yarn are installed:

```
$ git clone https://github.com/synapseweb3/ibc-solidity-contract
$ cd ibc-solidity-contract
$ echo AXON_HTTP_RPC_URL="YOUR_AXON_URL" > .env
$ yarn migrate > migrate.log | tail -f migrate.log
```

Run command `forcerelay eth-ckb --ethereum-chain-id ibc-eth-1 --ckb-chain-id ibc-ckb-1` to start up relay of ETH headers to CKB network.
After running `yarn migrate`, the `OwnableIBCHandler` address is listing in console, we record it and mark as **`YOUR_IBC_AXON_ADDRESS`** to use later.

### Contract Deployment on CKB
Detailed deployment steps can be found in [ibc-ckb-contracts](https://github.com/synapseweb3/ibc-ckb-contracts). Alternatively, you can find pre-deployed contracts TYPE_ARGS on both testnet and mainnet:
||Mainnet|Testnet|
|-|-|-|
|connection|WIP|WIP|
|channel|WIP|WIP|
|packet|WIP|WIP|
|utility|WIP|WIP|
|escrow|WIP|WIP|

Warn: relayer is still under rapid development and the configuration example and command above may be outdated.
### Business Module Registration
When deploying the Solidity contract on Axon, an initial ICS20 transfer module is automatically registered in `OwnableIBCHandler` on port `port-0` during the contract migration process. This registration is open to all users. For detailed instructions on how to register your own business module, visit [ibc-solidity-contract](https://github.com/synapseweb3/ibc-solidity-contract) repository.

## Using Forcerelay/Axon (WIP)
Unlike Axon, business modules cannot be registered directly with a contract on CKB. To address this, we have introduced [forcerelay-ckb-sdk](https://github.com/synapseweb3/forcerelay-ckb-sdk), designed to facilitate the distribution and calling of custom modules.

Run `cargo test -p ibc-test --lib --all-features -- tests::integration_test --exact --nocapture --ignored` to check Ckb endpoint, which tests connection and channel layers between two Ckb chains
It's important to note that the IBC port on CKB corresponds to the `LOCK_HASH` of your wallet cell on CKB, which we will refer to as `WALLET_LOCK_HASH` for future use.

Run `Forcerelay create connection --a-chain axon-0 --b-chain axon-1` and `Forcerelay create channel --a-chain axon-0 --a-connection connection-0 --a-port mock-port-0 --b-port mock-port-0` to create connection and channel between two Axon chains
### Installation and Setting
We recommand you to download the pre-compiled binary, or you can compile mannully from the source code, which requires `Rust ^v1.72.0` installed:

## Rqeuirements
```
$ git clone https://github.com/synapseweb3/forcerelay
$ cd forcerelay
$ cargo install -p ibc-relayer-cli
$ forcerelay --version
```

Forcerelay can be executed by specifying a configuration file in the command line, otherwise it will access the `~/.hermes/config.toml`. We provide a pre-generated [example](https://github.com/synapseweb3/forcerelay/blob/main/config.toml) configuration file written for Axon and CKB (Testnet). To run Forcerelay, only minimal modifications are needed to this configuration:

```
websocket_addr = "ws://<YOUR_AXON_URL>:<WS_PORT>"
rpc_addr = "http://<YOUR_AXON_URL>:<HTTP_PORT>/"
contract_address = "<YOUR_IBC_AXON_ADDRESS>"
```

This project requires Rust `^1.65.0`.
### Import Secret Key
Before running Forcerelay, accounts should be prepared on both blockchain networks and imported accordingly. In the case of Axon and CKB, Forcerelay needs its own Axon and CKB accounts with a sufficient amount of token balance. Follow these steps to import Secp256k1 secret keys:

```
$ forcerelay keys add --chain axon-0 --secret-file <SECRET_KEY_PATH>
$ forcerelay keys add --chain ckb4ibc-0 --secret-file <SECRET_KEY_PATH>
```

### Connect and Start Forcerelay
Establishing IBC channels on both sides of Axon and CKB is required to run Forcerelay:

```
$ forcerelay create channel \
--a-chain axon-0 --b-chain ckb4ibc-0 \
--a-port port-0 --b-port <WALLET_LOCK_HASH> \
--new-client-connection
$ forcerelay start --config <YOUR_CONFIG_PATH>
```
136 changes: 129 additions & 7 deletions crates/relayer-cli/src/commands/keys/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use eyre::eyre;
use hdpath::StandardHDPath;
use ibc_relayer::{
chain::ChainType,
config::{ChainConfig, Config},
config::{AddressType, ChainConfig, Config},
keyring::{
AnySigningKeyPair, KeyRing, Secp256k1KeyPair, SigningKeyPair, SigningKeyPairSized, Store,
},
Expand All @@ -34,13 +34,19 @@ use crate::conclude::Output;
///
/// `keys add [OPTIONS] --chain <CHAIN_ID> --mnemonic-file <MNEMONIC_FILE>`
///
/// The key-file and mnemonic-file flags can't be given at the same time, this will cause a terminating error.
/// The command to restore a key from a file containing secret key:
///
/// `keys add [OPTIONS] --chain <CHAIN_ID> --secret-file <SECRET_FILE>`
///
/// The key-file, mnemonic-file, secret-file flags can't be given at the same time, this will cause a terminating error.
/// If successful the key will be created or restored, depending on which flag was given.
#[derive(Clone, Command, Debug, Parser, PartialEq, Eq)]
#[clap(
override_usage = "forcerelay keys add [OPTIONS] --chain <CHAIN_ID> --key-file <KEY_FILE>
forcerelay keys add [OPTIONS] --chain <CHAIN_ID> --mnemonic-file <MNEMONIC_FILE>"
forcerelay keys add [OPTIONS] --chain <CHAIN_ID> --mnemonic-file <MNEMONIC_FILE>
forcerelay keys add [OPTIONS] --chain <CHAIN_ID> --secret-file <SECRET_FILE>"
)]
pub struct KeysAddCmd {
#[clap(
Expand Down Expand Up @@ -71,6 +77,16 @@ pub struct KeysAddCmd {
)]
mnemonic_file: Option<PathBuf>,

#[clap(
long = "secret-file",
required = true,
value_name = "SECRET_FILE",
help_heading = "FLAGS",
help = "Path to file containing hex-encoded secret key to restore from",
group = "add-restore"
)]
secret_file: Option<PathBuf>,

#[clap(
long = "key-name",
value_name = "KEY_NAME",
Expand Down Expand Up @@ -131,9 +147,13 @@ impl Runnable for KeysAddCmd {
Ok(result) => result,
};

// Check if --key-file or --mnemonic-file was given as input.
match (self.key_file.clone(), self.mnemonic_file.clone()) {
(Some(key_file), _) => {
// Check if --key-file or --mnemonic-file or --secret-file was given as input.
match (
self.key_file.clone(),
self.mnemonic_file.clone(),
self.secret_file.clone(),
) {
(Some(key_file), _, _) => {
let key = add_key(
&opts.config,
&opts.name,
Expand All @@ -156,7 +176,7 @@ impl Runnable for KeysAddCmd {
.exit(),
}
}
(_, Some(mnemonic_file)) => {
(_, Some(mnemonic_file), _) => {
let key = restore_key(
&mnemonic_file,
&opts.name,
Expand All @@ -180,6 +200,25 @@ impl Runnable for KeysAddCmd {
.exit(),
}
}
(_, _, Some(secret_file)) => {
let key =
parse_key_from_secret(&secret_file, &opts.name, &opts.config, self.overwrite);

match key {
Ok(key) => Output::success_msg(format!(
"Parsed key '{}' ({}) on chain {}",
opts.name,
key.account(),
opts.config.id(),
))
.exit(),
Err(e) => Output::error(format!(
"An error occurred parsing the key on chain {} from file {:?}: {}",
self.chain_id, secret_file, e
))
.exit(),
}
}
// This case should never trigger.
// The 'required' parameter for the flags will trigger an error if both flags have not been given.
// And the 'group' parameter for the flags will trigger an error if both flags are given.
Expand Down Expand Up @@ -255,6 +294,40 @@ pub fn restore_key(
Ok(key_pair)
}

pub fn parse_key_from_secret(
secret_file: &Path,
key_name: &str,
config: &ChainConfig,
overwrite: bool,
) -> eyre::Result<AnySigningKeyPair> {
let secret_key =
fs::read_to_string(secret_file).map_err(|_| eyre!("error reading the secret file"))?;

let (account_prefix, address_type) = match config.r#type() {
ChainType::CosmosSdk => (config.cosmos().account_prefix.as_str(), AddressType::Cosmos),
ChainType::Eth => (
"eth",
AddressType::Ethermint {
pk_type: Default::default(),
},
),
ChainType::Axon => ("axon", AddressType::Axon),
ChainType::Ckb => ("ckb", AddressType::Ckb),
ChainType::Ckb4Ibc => ("ckb4ibc", AddressType::Ckb),
};
let key_pair = {
let mut keyring = KeyRing::new_secp256k1(Store::Test, account_prefix, config.id())?;

check_key_exists(&keyring, key_name, overwrite);

let key_pair = Secp256k1KeyPair::from_secret_key(secret_key.trim(), &address_type)?;

keyring.add_key(key_name, key_pair.clone())?;
key_pair.into()
};
Ok(key_pair)
}

/// Check if the key with the given key name already exists.
/// If it already exists and overwrite is false, abort the command with an error.
/// If overwrite is true, output a warning message informing the key will be overwritten.
Expand Down Expand Up @@ -284,6 +357,7 @@ mod tests {
chain_id: ChainId::from_string("chain_id"),
key_file: Some(PathBuf::from("key_file")),
mnemonic_file: None,
secret_file: None,
key_name: None,
hd_path: "m/44'/118'/0'/0/0".to_string(),
overwrite: false,
Expand All @@ -299,6 +373,7 @@ mod tests {
chain_id: ChainId::from_string("chain_id"),
key_file: None,
mnemonic_file: Some(PathBuf::from("mnemonic_file")),
secret_file: None,
key_name: None,
hd_path: "m/44'/118'/0'/0/0".to_string(),
overwrite: false
Expand All @@ -313,13 +388,36 @@ mod tests {
)
}

#[test]
fn test_keys_add_secret_key() {
assert_eq!(
KeysAddCmd {
chain_id: ChainId::from_string("chain_id"),
key_file: None,
mnemonic_file: None,
secret_file: Some(PathBuf::from("secret_file")),
key_name: None,
hd_path: "m/44'/118'/0'/0/0".to_string(),
overwrite: false,
},
KeysAddCmd::parse_from([
"test",
"--chain",
"chain_id",
"--secret-file",
"secret_file",
])
)
}

#[test]
fn test_keys_add_key_file_overwrite() {
assert_eq!(
KeysAddCmd {
chain_id: ChainId::from_string("chain_id"),
key_file: Some(PathBuf::from("key_file")),
mnemonic_file: None,
secret_file: None,
key_name: None,
hd_path: "m/44'/118'/0'/0/0".to_string(),
overwrite: true,
Expand All @@ -342,6 +440,7 @@ mod tests {
chain_id: ChainId::from_string("chain_id"),
key_file: None,
mnemonic_file: Some(PathBuf::from("mnemonic_file")),
secret_file: None,
key_name: None,
hd_path: "m/44'/118'/0'/0/0".to_string(),
overwrite: true,
Expand All @@ -357,6 +456,29 @@ mod tests {
)
}

#[test]
fn test_keys_add_secret_key_overwrite() {
assert_eq!(
KeysAddCmd {
chain_id: ChainId::from_string("chain_id"),
key_file: None,
mnemonic_file: None,
secret_file: Some(PathBuf::from("secret_file")),
key_name: None,
hd_path: "m/44'/118'/0'/0/0".to_string(),
overwrite: true,
},
KeysAddCmd::parse_from([
"test",
"--chain",
"chain_id",
"--secret-file",
"secret_file",
"--overwrite"
])
)
}

#[test]
fn test_keys_add_no_file_nor_mnemonic() {
assert!(KeysAddCmd::try_parse_from(["test", "--chain", "chain_id"]).is_err());
Expand Down
Loading

0 comments on commit 6b5aee4

Please sign in to comment.