Skip to content

Commit

Permalink
feat(ibc-test): use ICS20TransferERC20, remove MockTransfer
Browse files Browse the repository at this point in the history
  • Loading branch information
jjyr committed Oct 20, 2023
1 parent 37b3c81 commit 4f0fd60
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 109 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ibc-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
env:
SRC_DIR: ${{ github.workspace }}/ibc-test-src
AXON_COMMIT: d03d2bb7cb3dcdc03319c3a74beeee6715e7f448
IBC_CONTRACT_COMMIT: f407f44289ef8abea31860b963f758dae1c16071
IBC_CONTRACT_COMMIT: 35290d79c9fa45583b00f228dd3ed7e8468ccc67
strategy:
fail-fast: false
matrix:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Detailed deployment steps can be found in [ibc-ckb-contracts](https://github.com
|escrow|WIP|WIP|

### 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.
When deploying the Solidity contract on Axon, an initial ICS20 transfer module is automatically registered in `OwnableIBCHandler` on port `transfer` 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.

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.

Expand Down Expand Up @@ -89,7 +89,7 @@ Establishing IBC channels on both sides of Axon and CKB is required to run Force
```
$ forcerelay create channel \
--a-chain axon-0 --b-chain ckb4ibc-0 \
--a-port port-0 --b-port <WALLET_LOCK_HASH> \
--a-port transfer --b-port <WALLET_LOCK_HASH> \
--new-client-connection
$ forcerelay start --config <YOUR_CONFIG_PATH>
```
```
54 changes: 37 additions & 17 deletions crates/relayer/src/chain/axon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ use tendermint_rpc::endpoint::broadcast::tx_sync::Response;
use self::{contract::OwnableIBCHandler, monitor::AxonEventMonitor};

type ContractProvider = SignerMiddleware<Provider<Ws>, Wallet<SigningKey>>;
type Contract = OwnableIBCHandler<ContractProvider>;
type IBCContract = OwnableIBCHandler<ContractProvider>;
type ERC20Contract = ERC20<ContractProvider>;

use super::{
client::ClientSettings,
Expand Down Expand Up @@ -122,6 +123,18 @@ lazy_static! {
Mutex::new(HashMap::new());
}

abigen!(
ERC20,
r"[
function totalSupply() external view returns (uint256)
function balanceOf(address account) external view returns (uint256)
function transfer(address to, uint256 amount) external returns (bool)
function allowance(address owner, address spender) external view returns (uint256)
function approve(address spender, uint256 amount) external returns (bool)
function transferFrom(address from, address to, uint256 amount) external returns (bool)
]"
);

pub struct AxonChain {
rt: Arc<TokioRuntime>,
config: AxonChainConfig,
Expand All @@ -145,12 +158,16 @@ impl AxonChain {
Ok(Arc::new(SignerMiddleware::new(self.client.clone(), wallet)))
}

fn contract(&self) -> Result<Contract, Error> {
Ok(Contract::new(
fn contract(&self) -> Result<IBCContract, Error> {
Ok(IBCContract::new(
self.config.contract_address,
self.contract_provider()?,
))
}

fn erc20_contract(&self, address: H160) -> Result<ERC20Contract, Error> {
Ok(ERC20::new(address, self.contract_provider()?))
}
}

impl ChainEndpoint for AxonChain {
Expand Down Expand Up @@ -308,22 +325,25 @@ impl ChainEndpoint for AxonChain {
self.light_client.check_misbehaviour(update, client_state)
}

// FIXME implement this after use a real ics token contract
fn query_balance(
&self,
_key_name: Option<&str>,
_denom: Option<&str>,
) -> Result<Balance, Error> {
// const DEFAULT_DENOM: &str = "AT";

// let key_name = key_name.unwrap_or(&self.config.key_name);
// let denom = denom.unwrap_or(DEFAULT_DENOM);
// let contract = self.ics20_contract()?;
// let wallet = self.get_wallet(key_name);
fn query_balance(&self, key_name: Option<&str>, denom: Option<&str>) -> Result<Balance, Error> {
let key_name = key_name.unwrap_or(&self.config.key_name);
let denom: &str =
denom.ok_or_else(|| Error::other_error("do not support default denom".into()))?;
let erc20_address = {
let denom = denom.trim_start_matches("0x");
let bytes = hex::decode(denom).map_err(Error::other)?;
H160::from_slice(&bytes)
};
let contract = self.erc20_contract(erc20_address)?;
let wallet = self.get_wallet(key_name)?;
let amount = self
.rt
.block_on(contract.balance_of(wallet.address()).call())
.map_err(|err| Error::query(format!("{err:?}")))?;

Ok(Balance {
amount: "100".to_owned(),
denom: "AT".to_owned(),
amount: format!("{amount:#x}"),
denom: denom.to_string(),
})
}

Expand Down
7 changes: 7 additions & 0 deletions crates/relayer/src/chain/ckb4ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,9 @@ impl ChainEndpoint for Ckb4IbcChain {
}

fn ibc_version(&self) -> Result<Option<Version>, Error> {
// TODO @jjy
// the ibc version should be matched with the CKB contract,
// IMO we can put it into the config of forcerelay or save the version in a cell
Ok(None)
}

Expand Down Expand Up @@ -749,6 +752,7 @@ impl ChainEndpoint for Ckb4IbcChain {
Ok(responses)
}

// TODO verify target height with Axon light client / store
fn verify_header(
&mut self,
_trusted: Height,
Expand Down Expand Up @@ -819,6 +823,7 @@ impl ChainEndpoint for Ckb4IbcChain {
Ok(vec![ckb_balance])
}

// TODO Need to align with CKB ibc contract
fn query_denom_trace(&self, _hash: String) -> Result<DenomTrace, Error> {
warn!("axon query_denom_trace() cannot implement");
Ok(DenomTrace {
Expand Down Expand Up @@ -849,6 +854,7 @@ impl ChainEndpoint for Ckb4IbcChain {
.onchain_light_clients
.keys()
.map(|client_type| {
// TODO query latest_height from light client cell (for example Axon metadata cell)
let client_id = self.config.lc_client_id(*client_type).unwrap();
let chain_id = self.config.lc_chain_id(&client_id.to_string()).unwrap();
IdentifiedAnyClientState {
Expand All @@ -869,6 +875,7 @@ impl ChainEndpoint for Ckb4IbcChain {
_include_proof: IncludeProof,
) -> Result<(AnyClientState, Option<MerkleProof>), Error> {
let chain_id = self.config.lc_chain_id(&request.client_id.to_string())?;
// TODO query latest_height
let client_state = CkbClientState {
chain_id,
latest_height: Height::default(),
Expand Down
2 changes: 1 addition & 1 deletion tools/ibc-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ We use chain-A chain-B, connection-A connection-B or channel-A channel-B to repr

`gaia` chain uses a builtin `transfer` port as the default port in IBC tests.

For Axon chain we use `port-0` as default port since it is defined in the [deployment script](https://github.com/synapseweb3/ibc-solidity-contract/blob/master/migrations/1_deploy_contracts.js#L84).
For Axon chain we use `transfer` as default port.

For CKB chain we uses `blake2b(b"transfer")` as default port.

Expand Down
50 changes: 23 additions & 27 deletions tools/ibc-test/src/framework/bootstrap/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,47 +37,45 @@ pub fn bootstrap_single_node(
chain_driver.rpc_address(),
);

let (process, miner_process) = match chain_driver.chain_type {
let validator = add_wallet(&chain_driver, "validator", use_random_id)?;
let user1 = add_wallet(&chain_driver, "user1", use_random_id)?;
let user2 = add_wallet(&chain_driver, "user2", use_random_id)?;
let relayer = match chain_driver.chain_type {
ChainType::Ckb => {
// FIXME @jjy use random pk as relayer once remove hardcoded deploy transactions
// let relayer = add_wallet(&chain_driver, "relayer", use_random_id)?;
add_ckb_devnet_relayer_wallet(&chain_driver, "relayer", use_random_id)?
}
ChainType::Axon => add_axon_devnet_relayer_wallet(&chain_driver, "relayer", use_random_id)?,
_ => add_wallet(&chain_driver, "relayer", use_random_id)?,
};

let (process, miner_process, denom) = match chain_driver.chain_type {
ChainType::Ckb => {
// FIXME @jjy
// currently the deploy tx is hard coded, which means relayer must be a fixed pk
let (node, miner) = prepare_ckb_chain(
&chain_driver.home_path,
chain_driver.rpc_port as u32,
&[],
// &[(&relayer, 5_198_735_037_00000000u64)],
);
(node, Some(miner))
let (node, miner) =
prepare_ckb_chain(&chain_driver.home_path, chain_driver.rpc_port as u32, &[]);

// TODO deploy a sUDT as default denom
let denom = Denom::base("ckb");
(node, Some(miner), denom)
}
ChainType::Axon => {
// TODO
let node = prepare_axon_chain(
let (node, denom) = prepare_axon_chain(
&chain_driver.home_path,
chain_driver.rpc_port as u32,
&[],
// &[(&relayer, 5_198_735_037_00000000u64)],
Some((&relayer, 1000000000000u64)),
)
.unwrap();
(node, None)
(node, None, denom)
}
_ => {
unreachable!()
}
};

let validator = add_wallet(&chain_driver, "validator", use_random_id)?;
let user1 = add_wallet(&chain_driver, "user1", use_random_id)?;
let user2 = add_wallet(&chain_driver, "user2", use_random_id)?;
let relayer = match chain_driver.chain_type {
ChainType::Ckb => {
// FIXME @jjy use random pk as relayer once remove hardcoded deploy transactions
// let relayer = add_wallet(&chain_driver, "relayer", use_random_id)?;
add_ckb_devnet_relayer_wallet(&chain_driver, "relayer", use_random_id)?
}
ChainType::Axon => add_axon_devnet_relayer_wallet(&chain_driver, "relayer", use_random_id)?,
_ => add_wallet(&chain_driver, "relayer", use_random_id)?,
};

info!(
"user wallet for chain {} - id: {}, address: {}",
chain_driver.chain_id, user1.id.0, user1.address.0,
Expand All @@ -97,8 +95,6 @@ pub fn bootstrap_single_node(
user2,
};

let denom = Denom::base("ckb");

let node = FullNode {
chain_driver,
denom,
Expand Down
96 changes: 51 additions & 45 deletions tools/ibc-test/src/framework/utils/axon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ const IBC_CONTRACTS_SRC_PATH: &str = "IBC_CONTRACTS_SRC_PATH";
pub(crate) fn prepare_axon_chain(
dir_path: &str,
port: u32,
genesis_wallets: &[(&Wallet, u64)],
) -> Result<ChildProcess> {
pre_mint: Option<(&Wallet, u64)>,
) -> Result<(ChildProcess, Denom)> {
println!("\n========== Prepare Axon node on port {port} ==========\n");

// read envs
Expand Down Expand Up @@ -77,20 +77,6 @@ pub(crate) fn prepare_axon_chain(
"/ip4/127.0.0.1/tcp/{}/p2p/QmNk6bBwkLPuqnsrtxpp819XLZY3ymgjs3p1nKtxBVgqxj",
port + 2
));

// genesis wallets
for (wallet, balance) in genesis_wallets {
let mut item = Table::new();
item.entry("address")
.or_insert(value(wallet.address.as_str()));
item.entry("balance")
.or_insert(value(hex::encode(balance.to_be_bytes())));
config_doc["accounts"]
.as_array_of_tables_mut()
.unwrap()
.push(item);
}

fs::write(&chain_config_path, config_doc.to_string())
.with_context(|| format!("write config to {:?}", &chain_config_path))?;

Expand All @@ -112,39 +98,59 @@ pub(crate) fn prepare_axon_chain(
wait_for_port(port);

// Deploy IBC contract
{
println!("deploying ibc contracts",);
let output = Command::new("yarn")
.arg("migrate")
.env("AXON_HTTP_RPC_URL", format!("http://localhost:{}", port))
.current_dir(&ibc_contracts_src_path)
.output()
.with_context(|| "yarn migrate")?;
// get contract address from output
check_command_output(&output, &working_dir)?;
let output = String::from_utf8(output.stdout.clone())?;
let contract_address = parsing_contract_address_from_output(&output, "OwnableIBCHandler")?;
let mock_transfer_contract_address =
parsing_contract_address_from_output(&output, "MockTransfer")?;
let transfer_contract_address =
parsing_contract_address_from_output(&output, "ICS20TransferERC20")?;
println!("deploying ibc contracts",);
let output = Command::new("yarn")
.arg("migrate")
.env("AXON_HTTP_RPC_URL", format!("http://localhost:{}", port))
.current_dir(&ibc_contracts_src_path)
.output()
.with_context(|| "yarn migrate")?;
// get contract address from output
check_command_output(&output, &working_dir)?;
let output = String::from_utf8(output.stdout.clone())?;
let contract_address = parsing_contract_address_from_output(&output, "OwnableIBCHandler")?;
let transfer_contract_address =
parsing_contract_address_from_output(&output, "ICS20TransferERC20")?;

println!("ibc handler deployed at {:#x}", contract_address);
println!("ibc handler deployed at {:#x}", contract_address);

// write deployment info
let deployment = DeployedContracts {
contract_address,
mock_transfer_contract_address,
transfer_contract_address,
image_cell_contract_address: ethers::types::H160::default(),
ckb_light_client_contract_address: ethers::types::H160::default(),
};
let path = working_dir.join(AXON_CONTRACTS_CONFIG_PATH);
std::fs::write(path, toml::to_string(&deployment)?)
.with_context(|| "write deployment info")?;
// deploy test ERC20 and mint token
let token_name = "AT";
let token_symbol = "AT";
let mut cmd = Command::new("yarn");
cmd.current_dir(&ibc_contracts_src_path)
.arg("truffle")
.arg("exec")
.arg("--network")
.arg("axon")
.arg("scripts/deploy_erc20.js")
.env("AXON_HTTP_RPC_URL", format!("http://localhost:{port}"))
.env("TOKEN_NAME", token_name)
.env("TOKEN_SYMBOL", token_symbol);
if let Some((mint_to, mint_amount)) = pre_mint {
cmd.env("MINT_TO", mint_to.address.as_str())
.env("MINT_AMOUNT", mint_amount.to_string());
}
let output = cmd.output().with_context(|| "yarn truffle")?;
// get contract address from output
check_command_output(&output, &working_dir)?;
let output = String::from_utf8(output.stdout.clone())?;
let token_address = parsing_contract_address_from_output(&output, "SimpleToken")?;
let denom = Denom::base(&format!("{token_address:#x}"));

println!("test token deployed at {:#x}", token_address);

// write deployment info
let deployment = DeployedContracts {
contract_address,
transfer_contract_address,
image_cell_contract_address: ethers::types::H160::default(),
ckb_light_client_contract_address: ethers::types::H160::default(),
};
let path = working_dir.join(AXON_CONTRACTS_CONFIG_PATH);
std::fs::write(path, toml::to_string(&deployment)?).with_context(|| "write deployment info")?;

Ok(chain_process)
Ok((chain_process, denom))
}

fn check_command_output(output: &Output, working_dir: &Path) -> Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion tools/ibc-test/src/framework/utils/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn transfer_port_id(chain_type: ChainType) -> PortId {
}
ChainType::Axon => {
// Axon default port ID
PortId::from_str("port-0").unwrap()
PortId::from_str("transfer").unwrap()
}
_ => {
unreachable!()
Expand Down
Loading

0 comments on commit 4f0fd60

Please sign in to comment.