diff --git a/.maintain/config/ropsten_pangolin.sample.yml b/.maintain/config/ropsten_pangolin.sample.yml index d53bb9655..6d3e6332b 100644 --- a/.maintain/config/ropsten_pangolin.sample.yml +++ b/.maintain/config/ropsten_pangolin.sample.yml @@ -30,6 +30,11 @@ ethereum: topics: # set authorities topic - 0x91d6d149c7e5354d1c671fe15a5a3332c47a38e15e8ac0339b24af3c1090690f + backing: + address: "0xd5FC8F2eB94fE6AAdeE91c561818e1fF4ea2C041" + topics: + - "0x0c403c4583ff520bad94bf49975b3547a573f7157070022cf8c9a023498d4d11" + - "0xf70fbddcb43e433da621898f5f2628b0a644a77a4389ac2580c5b1de06382fe2" # (optional) the person who will relay darwinia data to ethereum # Disable by removing or commenting relayer: diff --git a/Cargo.lock b/Cargo.lock index c3d3e0641..4ba5a678c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4178,7 +4178,7 @@ dependencies = [ [[package]] name = "substrate-subxt" version = "0.15.0" -source = "git+https://github.com/wuminzhe/substrate-subxt.git?branch=darwinia#a4a9a497b616714aa5653292da10154ffc09a11c" +source = "git+https://github.com/wuminzhe/substrate-subxt.git?branch=darwinia#c9163befd3bdf3062bdb4912c50933083ff69eba" dependencies = [ "dyn-clone", "frame-metadata", @@ -4210,7 +4210,7 @@ dependencies = [ [[package]] name = "substrate-subxt-proc-macro" version = "0.15.0" -source = "git+https://github.com/wuminzhe/substrate-subxt.git?branch=darwinia#a4a9a497b616714aa5653292da10154ffc09a11c" +source = "git+https://github.com/wuminzhe/substrate-subxt.git?branch=darwinia#c9163befd3bdf3062bdb4912c50933083ff69eba" dependencies = [ "heck", "proc-macro-crate", diff --git a/darwinia/src/darwinia.rs b/darwinia/src/darwinia.rs index 3e9fcb26c..79b9b99f0 100644 --- a/darwinia/src/darwinia.rs +++ b/darwinia/src/darwinia.rs @@ -9,7 +9,7 @@ use substrate_subxt::{ use primitives::{ //todo move to e2d - frame::ethereum::backing::VerifiedProofStoreExt, + frame::ethereum::{backing::VerifiedProofStoreExt, issuing::VerifiedIssuingProofStoreExt}, runtime::DarwiniaRuntime, }; @@ -274,4 +274,17 @@ impl Darwinia { .await? .unwrap_or(false)) } + + /// Check if should issuing sync + pub async fn verified_issuing( + &self, + block_hash: web3::types::H256, + tx_index: u64, + ) -> Result { + Ok(self + .subxt + .verified_issuing_proof((block_hash.to_fixed_bytes(), tx_index), None) + .await? + .unwrap_or(false)) + } } diff --git a/darwinia/src/from_ethereum/api.rs b/darwinia/src/from_ethereum/api.rs index 35a8292d1..c4fd69e33 100644 --- a/darwinia/src/from_ethereum/api.rs +++ b/darwinia/src/from_ethereum/api.rs @@ -16,6 +16,7 @@ use primitives::{ ethereum::{ backing::{Redeem, RedeemCallExt}, game::{AffirmationsStoreExt, EthereumRelayerGame}, + issuing::{RedeemErc20, RedeemErc20CallExt, RegisterErc20, RegisterErc20CallExt}, relay::{ Affirm, AffirmCallExt, ConfirmedBlockNumbersStoreExt, EthereumRelay, PendingRelayHeaderParcelsStoreExt, SetConfirmedParcel, @@ -309,4 +310,70 @@ impl Ethereum2Darwinia { Some(real) => voting_state.contains(real), } } + + /// register erc20 + pub async fn register_erc20( + &self, + account: &Account, + proof: EthereumReceiptProofThing, + ) -> Result { + match &account.0.real { + Some(real) => { + let call = RegisterErc20 { + _runtime: PhantomData::default(), + proof, + }; + + let ex = self.darwinia.subxt.encode(call).unwrap(); + Ok(self + .darwinia + .subxt + .proxy( + &account.0.signer, + real.clone(), + Some(ProxyType::EthereumBridge), + &ex, + ) + .await?) + } + None => Ok(self + .darwinia + .subxt + .register_erc20(&account.0.signer, proof) + .await?), + } + } + + /// redeem erc20 + pub async fn redeem_erc20( + &self, + account: &Account, + proof: EthereumReceiptProofThing, + ) -> Result { + match &account.0.real { + Some(real) => { + let call = RedeemErc20 { + _runtime: PhantomData::default(), + proof, + }; + + let ex = self.darwinia.subxt.encode(call).unwrap(); + Ok(self + .darwinia + .subxt + .proxy( + &account.0.signer, + real.clone(), + Some(ProxyType::EthereumBridge), + &ex, + ) + .await?) + } + None => Ok(self + .darwinia + .subxt + .redeem_erc20(&account.0.signer, proof) + .await?), + } + } } diff --git a/primitives/src/byte.rs b/primitives/src/byte.rs index 47415f5d8..8078865f4 100644 --- a/primitives/src/byte.rs +++ b/primitives/src/byte.rs @@ -16,9 +16,9 @@ macro_rules! hex { let mut s = String::new(); for i in $bytes { s.push_str(&format!("{:02x}", i)); - } - s - }}; + } + s + }}; } /// Convert hex string to `Vec` or `[u8; n]` @@ -29,21 +29,21 @@ macro_rules! bytes { let mut h = $hex; if h.starts_with("0x") { h = &h[2..]; - } + } (0..h.len()) .step_by(2) .map(|i| u8::from_str_radix(&h[i..i + 2], 16)) .collect::, _>>() .unwrap_or_default() - }}; + }}; // Convert hex to [u8; $bits] ($hex:expr, $bits:expr) => {{ let mut hash = [0_u8; $bits]; hash.copy_from_slice(&bytes!($hex)); hash - }}; + }}; } /// Implement serde for big array diff --git a/primitives/src/chain/ethereum/receipt.rs b/primitives/src/chain/ethereum/receipt.rs index 58abcf061..94c988cc5 100644 --- a/primitives/src/chain/ethereum/receipt.rs +++ b/primitives/src/chain/ethereum/receipt.rs @@ -19,6 +19,10 @@ pub enum RedeemFor { Deposit, /// Redeem for set authorities SetAuthorities, + /// Redeem for register erc20 token + RegisterErc20Token, + /// Redeem for erc20 token + RedeemErc20Token, } impl Default for RedeemFor { diff --git a/primitives/src/frame/ethereum/backing.rs b/primitives/src/frame/ethereum/backing.rs index 4af199750..8b8fabe8c 100644 --- a/primitives/src/frame/ethereum/backing.rs +++ b/primitives/src/frame/ethereum/backing.rs @@ -2,9 +2,9 @@ use crate::chain::ethereum::{EthereumReceiptProofThing, RedeemFor}; use codec::{Decode, Encode}; use core::marker::PhantomData; -use substrate_subxt_proc_macro::{module, Call, Event, Store}; -use substrate_subxt::system::System; use substrate_subxt::balances::Balances; +use substrate_subxt::system::System; +use substrate_subxt_proc_macro::{module, Call, Event, Store}; /// Ethereum Relay Pallet #[module] diff --git a/primitives/src/frame/ethereum/issuing.rs b/primitives/src/frame/ethereum/issuing.rs new file mode 100644 index 000000000..980889dd2 --- /dev/null +++ b/primitives/src/frame/ethereum/issuing.rs @@ -0,0 +1,43 @@ +//! Darwinia Ethereum Issuing +use crate::chain::ethereum::EthereumReceiptProofThing; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use substrate_subxt::{balances::Balances, system::System}; +use substrate_subxt_proc_macro::{module, Call, Store}; + +/// Ethereum Issuing Pallet +#[module] +pub trait EthereumIssuing: System + Balances { + /// Ethereum transaction index + type EthereumTransactionIndex: 'static + Encode + Decode + Send + Default + Clone + Sync; +} + +// Call + +/// Submit register erc20 token +#[derive(Clone, Debug, PartialEq, Call, Encode)] +pub struct RegisterErc20 { + /// Runtime marker + pub _runtime: PhantomData, + /// Ethereum Receipt Proof + pub proof: EthereumReceiptProofThing, +} + +/// Submit redeem erc20 token +#[derive(Clone, Debug, PartialEq, Call, Encode)] +pub struct RedeemErc20 { + /// Runtime marker + pub _runtime: PhantomData, + /// Ethereum Receipt Proof + pub proof: EthereumReceiptProofThing, +} + +/// verified proof Storage +#[derive(Clone, Debug, Eq, PartialEq, Store, Decode, Encode)] +pub struct VerifiedIssuingProof { + #[store(returns = Option)] + /// Receipt tx hash + pub map: ([u8; 32], u64), + /// Runtime marker + pub _runtime: PhantomData, +} diff --git a/primitives/src/frame/ethereum/mod.rs b/primitives/src/frame/ethereum/mod.rs index 1987623f2..8ff0d33f9 100644 --- a/primitives/src/frame/ethereum/mod.rs +++ b/primitives/src/frame/ethereum/mod.rs @@ -3,4 +3,5 @@ pub mod backing; pub mod game; +pub mod issuing; pub mod relay; diff --git a/primitives/src/frame/proxy.rs b/primitives/src/frame/proxy.rs index 2a3351004..446b14d4b 100644 --- a/primitives/src/frame/proxy.rs +++ b/primitives/src/frame/proxy.rs @@ -1,10 +1,7 @@ //! Frame Proxy use codec::{Decode, Encode}; use core::marker::PhantomData; -use substrate_subxt::{ - system::System, - Encoded, -}; +use substrate_subxt::{system::System, Encoded}; use substrate_subxt_proc_macro::{module, Call}; /// The subset of the `frame_proxy::Trait` that a client must implement. diff --git a/primitives/src/frame/sudo.rs b/primitives/src/frame/sudo.rs index b87667668..747934a26 100644 --- a/primitives/src/frame/sudo.rs +++ b/primitives/src/frame/sudo.rs @@ -2,10 +2,7 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::weights::Weight; -use substrate_subxt::{ - system::System, - Encoded, -}; +use substrate_subxt::{system::System, Encoded}; use substrate_subxt_proc_macro::{module, Call, Store}; /// The subset of the `frame_sudo::Trait` that a client must implement. diff --git a/primitives/src/frame/technical_committee.rs b/primitives/src/frame/technical_committee.rs index d09d1746c..2ddf59210 100644 --- a/primitives/src/frame/technical_committee.rs +++ b/primitives/src/frame/technical_committee.rs @@ -2,10 +2,7 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; -use substrate_subxt::{ - system::System, - Encoded, -}; +use substrate_subxt::{system::System, Encoded}; use substrate_subxt_proc_macro::{module, Call, Store}; /// The subset of the `frame_council::Trait` that a client must implement. diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 69e310dbf..8ef0845a5 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -48,9 +48,7 @@ pub mod runtime; #[cfg(test)] mod tests { use super::runtime::DarwiniaRuntime; - use substrate_subxt::{ - ClientBuilder, - }; + use substrate_subxt::ClientBuilder; // use super::frame::ethereum::relay::PendingRelayHeaderParcelsStoreExt; use super::frame::technical_committee::MembersStoreExt; @@ -60,7 +58,8 @@ mod tests { .set_url("ws://100.64.200.3:10000") .skip_type_sizes_check() .build() - .await.unwrap(); + .await + .unwrap(); println!("-----------"); let block_number = 1; @@ -78,7 +77,8 @@ mod tests { .set_url("ws://100.64.200.3:10000") .skip_type_sizes_check() .build() - .await.unwrap(); + .await + .unwrap(); let members = client.members(None).await.unwrap(); for member in members { @@ -92,7 +92,8 @@ mod tests { .set_url("wss://pangolin-rpc.darwinia.network") .skip_type_sizes_check() .build() - .await.unwrap(); + .await + .unwrap(); let members = client.members(None).await.unwrap(); for member in members { diff --git a/primitives/src/runtime.rs b/primitives/src/runtime.rs index 89d2b5716..70f4b32e8 100644 --- a/primitives/src/runtime.rs +++ b/primitives/src/runtime.rs @@ -8,7 +8,10 @@ use crate::{ }, frame::{ bridge::relay_authorities::EthereumRelayAuthorities, - ethereum::{backing::EthereumBacking, game::EthereumRelayerGame, relay::EthereumRelay}, + ethereum::{ + backing::EthereumBacking, game::EthereumRelayerGame, issuing::EthereumIssuing, + relay::EthereumRelay, + }, proxy::Proxy, sudo::Sudo, technical_committee::TechnicalCommittee, @@ -144,6 +147,10 @@ impl EthereumBacking for DarwiniaRuntime { type EthereumTransactionIndex = u64; } +impl EthereumIssuing for DarwiniaRuntime { + type EthereumTransactionIndex = u64; +} + impl Proxy for DarwiniaRuntime { type ProxyType = ProxyType; } diff --git a/src/cmd/info_d2e.rs b/src/cmd/info_d2e.rs index 188007a85..07ba07d47 100644 --- a/src/cmd/info_d2e.rs +++ b/src/cmd/info_d2e.rs @@ -49,7 +49,13 @@ impl fmt::Display for TxProofWithMMRProof { } /// Get Darwinia to Ethereum Info -pub async fn exec(network: String, txblock: u64, mmrblock: u64, signblock: u64) -> Result<()> { +pub async fn exec( + network: String, + txblock: u64, + mmrblock: u64, + signblock: u64, + istoken: bool, +) -> Result<()> { std::env::set_var("RUST_LOG", "info,darwinia_bridger"); env_logger::init(); @@ -72,9 +78,11 @@ pub async fn exec(network: String, txblock: u64, mmrblock: u64, signblock: u64) .await?; let event_proof = darwinia .get_event_proof( - array_bytes::hex2bytes( - "f8860dda3d08046cf2706b92bf7202eaae7a79191c90e76297e0895605b8b457", - ) + array_bytes::hex2bytes(if istoken { + "e66f3de22eed97c730152f373193b5a0485b407d88f37d5fd6a2c59e5a696691" + } else { + "f8860dda3d08046cf2706b92bf7202eaae7a79191c90e76297e0895605b8b457" + }) .unwrap(), header.hash(), ) diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index ab704adbe..3608a01f3 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -103,6 +103,9 @@ enum Opt { /// sign block number #[structopt(short, long)] signblock: u64, + /// is token or ring/kton + #[structopt(short, long)] + istoken: bool, }, /// Sign MMR root SignMMRRoot { @@ -157,7 +160,8 @@ pub async fn exec() -> Result<()> { txblock, mmrblock, signblock, - } => info_d2e::exec(network, txblock, mmrblock, signblock).await?, + istoken, + } => info_d2e::exec(network, txblock, mmrblock, signblock, istoken).await?, Opt::SignMMRRoot { network, mmrblock } => sign_mmr_root::exec(network, mmrblock).await?, Opt::AffirmForce { block } => { affirm_force::exec(block).await?; diff --git a/src/config.rs b/src/config.rs index e3ca4793b..af31a0d93 100644 --- a/src/config.rs +++ b/src/config.rs @@ -43,6 +43,8 @@ pub struct EthereumContract { pub issuing: EthereumContractTuple, /// Relay Contract pub relay: EthereumContractTuple, + /// Backing Contract + pub backing: EthereumContractTuple, } /// Ethereum Relayer @@ -207,7 +209,7 @@ impl Settings { impl Default for Settings { fn default() -> Self { - let config_file = PathBuf::from(".maintain/config/ropsten_crab.sample.yml"); + let config_file = PathBuf::from(".maintain/config/ropsten_pangolin.sample.yml"); let mut settings = Config::default(); settings.merge(File::from(config_file)).unwrap(); settings.try_into().unwrap() diff --git a/src/service/ethereum.rs b/src/service/ethereum.rs index 512c37932..ed9203f37 100644 --- a/src/service/ethereum.rs +++ b/src/service/ethereum.rs @@ -40,6 +40,10 @@ pub struct ContractAddress { pub bank: H256, /// relay pub relay: H256, + /// register + pub register: H256, + /// lock token on ethereum and redeem from darwinia + pub lock: H256, } /// Ethereum transaction service @@ -47,7 +51,7 @@ pub struct ContractAddress { /// This service can check and scan darwinia txs in Ethereum pub struct EthereumService { contracts: ContractAddress, - filters: [FilterBuilder; 3], + filters: [FilterBuilder; 4], web3: Web3, darwinia: Darwinia, scan_from: u64, @@ -146,7 +150,7 @@ impl EthereumService { async fn do_scan( web3: Web3, contracts: ContractAddress, - filters: [FilterBuilder; 3], + filters: [FilterBuilder; 4], from: u64, to: u64, ) -> BridgerResult> { @@ -195,6 +199,24 @@ impl EthereumService { block, index, } + } else if l.topics.contains(&contracts.register) { + EthereumTransaction { + tx_hash: EthereumTransactionHash::RegisterErc20Token( + l.transaction_hash.unwrap_or_default(), + ), + block_hash: l.block_hash.unwrap_or_default(), + block, + index, + } + } else if l.topics.contains(&contracts.lock) { + EthereumTransaction { + tx_hash: EthereumTransactionHash::RedeemErc20Token( + l.transaction_hash.unwrap_or_default(), + ), + block_hash: l.block_hash.unwrap_or_default(), + block, + index, + } } else { EthereumTransaction { tx_hash: EthereumTransactionHash::Deposit( @@ -217,7 +239,7 @@ impl EthereumService { darwinia: Darwinia, web3: Web3, contracts: ContractAddress, - filters: [FilterBuilder; 3], + filters: [FilterBuilder; 4], scan_from: u64, relay_service: Recipient, redeem_service: Recipient, @@ -256,7 +278,9 @@ impl EthereumService { } for tx in &txs { - if darwinia.verified(tx.block_hash, tx.index).await? { + if darwinia.verified(tx.block_hash, tx.index).await? + || darwinia.verified_issuing(tx.block_hash, tx.index).await? + { trace!( " This ethereum tx {:?} has already been redeemed.", tx.enclosed_hash() @@ -290,19 +314,23 @@ impl EthereumService { let ring_topics = contract.ring.topics.clone().unwrap(); let kton_topics = contract.kton.topics.clone().unwrap(); let relay_topics = contract.relay.topics.clone().unwrap(); + let backing_topics = contract.backing.topics.clone().unwrap(); ContractAddress { bank: H256::from_slice(&bytes!(bank_topics[0].as_str())), kton: H256::from_slice(&bytes!(kton_topics[0].as_str())), ring: H256::from_slice(&bytes!(ring_topics[0].as_str())), relay: H256::from_slice(&bytes!(relay_topics[0].as_str())), + register: H256::from_slice(&bytes!(backing_topics[0].as_str())), + lock: H256::from_slice(&bytes!(backing_topics[1].as_str())), } } /// Parse log filter from config - pub fn parse_filter(config: &Settings) -> [FilterBuilder; 3] { + pub fn parse_filter(config: &Settings) -> [FilterBuilder; 4] { let filters = [ &config.ethereum.contract.bank, &config.ethereum.contract.issuing, + &config.ethereum.contract.backing, &config.ethereum.contract.relay, ] .iter() @@ -320,7 +348,12 @@ impl EthereumService { .topics(Some(topics), None, None, None) }) .collect::>(); - [filters[0].clone(), filters[1].clone(), filters[2].clone()] + [ + filters[0].clone(), + filters[1].clone(), + filters[2].clone(), + filters[3].clone(), + ] } /// get_latest_block_number diff --git a/src/service/extrinsics.rs b/src/service/extrinsics.rs index ef4489714..618960100 100644 --- a/src/service/extrinsics.rs +++ b/src/service/extrinsics.rs @@ -107,6 +107,7 @@ impl ExtrinsicsService { } } + #[allow(clippy::too_many_arguments)] async fn send_extrinsic( ethereum2darwinia: Option, darwinia2ethereum: Option, @@ -149,6 +150,30 @@ impl ExtrinsicsService { } } } + RedeemFor::RegisterErc20Token => { + if let Some(ethereum2darwinia) = ðereum2darwinia { + if let Some(relayer) = ðereum2darwinia_relayer { + let ex_hash = + ethereum2darwinia.register_erc20(&relayer, proof).await?; + info!( + "register erc20 token tx {:?} with extrinsic {:?}", + ethereum_tx.tx_hash, ex_hash + ); + } + } + } + RedeemFor::RedeemErc20Token => { + if let Some(ethereum2darwinia) = ðereum2darwinia { + if let Some(relayer) = ðereum2darwinia_relayer { + let ex_hash = + ethereum2darwinia.redeem_erc20(&relayer, proof).await?; + info!( + "redeem erc20 token tx {:?} with extrinsic {:?}", + ethereum_tx.tx_hash, ex_hash + ); + } + } + } _ => { if let Some(ethereum2darwinia) = ðereum2darwinia { if let Some(relayer) = ðereum2darwinia_relayer { diff --git a/src/service/redeem.rs b/src/service/redeem.rs index 64b411366..89b82193f 100644 --- a/src/service/redeem.rs +++ b/src/service/redeem.rs @@ -22,6 +22,10 @@ pub enum EthereumTransactionHash { Token(H256), /// SetAuthoritiesEvent SetAuthorities(H256), + /// RegisterErc20Token + RegisterErc20Token(H256), + /// RedeemErc20Token + RedeemErc20Token(H256), } /// Reedeemable Ethereum transaction @@ -44,6 +48,8 @@ impl EthereumTransaction { EthereumTransactionHash::Token(h) => h, EthereumTransactionHash::Deposit(h) => h, EthereumTransactionHash::SetAuthorities(h) => h, + EthereumTransactionHash::RegisterErc20Token(h) => h, + EthereumTransactionHash::RedeemErc20Token(h) => h, } } } @@ -189,6 +195,8 @@ impl RedeemService { EthereumTransactionHash::Deposit(_) => RedeemFor::Deposit, EthereumTransactionHash::Token(_) => RedeemFor::Token, EthereumTransactionHash::SetAuthorities(_) => RedeemFor::SetAuthorities, + EthereumTransactionHash::RegisterErc20Token(_) => RedeemFor::RegisterErc20Token, + EthereumTransactionHash::RedeemErc20Token(_) => RedeemFor::RedeemErc20Token, }; let ex = Extrinsic::Redeem(redeem_for, proof, tx); diff --git a/src/service/subscribe/mod.rs b/src/service/subscribe/mod.rs index 226443233..810e050a6 100644 --- a/src/service/subscribe/mod.rs +++ b/src/service/subscribe/mod.rs @@ -217,5 +217,4 @@ impl SubscribeService { } Ok(()) } - } diff --git a/src/tools.rs b/src/tools.rs index b4f41db96..57d607731 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -37,8 +37,8 @@ pub async fn set_cache(data_dir: PathBuf, filename: &str, value: u64) -> Result< Ok(()) } -use actix::Recipient; use crate::service::extrinsics::{Extrinsic, MsgExtrinsic}; +use actix::Recipient; use tokio::time::{delay_for, Duration}; /// send extrinsic to extrinsics_service @@ -53,7 +53,7 @@ pub async fn send_extrinsic(extrinsics_service: &Recipient, ex: Ex } else { break; } - }, + } Err(send_err) => { error!("{:?}", send_err); delay_for(Duration::from_secs(30)).await; @@ -61,4 +61,3 @@ pub async fn send_extrinsic(extrinsics_service: &Recipient, ex: Ex } } } -