From 92280bf6dd72eea0cfdddd409849262efa144139 Mon Sep 17 00:00:00 2001 From: Beqa Abuladze Date: Mon, 29 Jul 2024 15:48:49 +0400 Subject: [PATCH] feat: comment out everything collect_coins related, tests are wip --- pallets/creditcoin/src/lib.rs | 436 +-- pallets/creditcoin/src/ocw/tasks.rs | 8 +- .../creditcoin/src/ocw/tasks/collect_coins.rs | 2776 ++++++++--------- .../src/ocw/tasks/verify_transfer.rs | 48 +- pallets/creditcoin/src/types.rs | 64 +- pallets/creditcoin/src/types/collect_coins.rs | 288 +- 6 files changed, 1833 insertions(+), 1787 deletions(-) diff --git a/pallets/creditcoin/src/lib.rs b/pallets/creditcoin/src/lib.rs index c121e72fff..2dff1d41df 100644 --- a/pallets/creditcoin/src/lib.rs +++ b/pallets/creditcoin/src/lib.rs @@ -31,7 +31,7 @@ mod tests; #[macro_use] mod helpers; -pub mod migrations; +//pub mod migrations; pub mod ocw; mod types; @@ -39,13 +39,13 @@ mod types; pub mod test_utils; use crate::types::{BurnId, BurnInfo}; -use ocw::tasks::collect_coins::DeployedContract; +use ocw::tasks::verify_transfer::DeployedContract; pub use types::{ loan_terms, Address, AddressId, AskOrder, AskOrderId, AskTerms, BidOrder, BidOrderId, BidTerms, - Blockchain, BurnDetails, CollectedCoinsId, CollectedCoinsStruct, ContractType, DealOrder, + Blockchain, /*BurnDetails, CollectedCoinsId, CollectedCoinsStruct, ContractType,*/ DealOrder, DealOrderId, Duration, ExternalAddress, ExternalAmount, ExternalTxId, Guid, InterestRate, InterestType, LegacySighash, LoanTerms, Offer, OfferId, OrderId, RatePerPeriod, Task, TaskId, - TaskOutput, Transfer, TransferId, TransferKind, UnverifiedCollectedCoins, UnverifiedTransfer, + TaskOutput, Transfer, TransferId, TransferKind, /*UnverifiedCollectedCoins,*/ UnverifiedTransfer, }; pub(crate) use types::{DoubleMapExt, Id}; @@ -230,18 +230,18 @@ pub mod pallet { Transfer, >; - #[pallet::storage] - #[pallet::getter(fn collected_coins)] - pub type CollectedCoins = StorageMap< - _, - Identity, - CollectedCoinsId, - types::CollectedCoinsStruct, - >; + // #[pallet::storage] + // #[pallet::getter(fn collected_coins)] + // pub type CollectedCoins = StorageMap< + // _, + // Identity, + // CollectedCoinsId, + // types::CollectedCoinsStruct, + // >; - #[pallet::storage] - #[pallet::getter(fn collect_coins_contract)] - pub type CollectCoinsContract = StorageValue<_, DeployedContract, ValueQuery>; + // #[pallet::storage] + // #[pallet::getter(fn collect_coins_contract)] + // pub type CollectCoinsContract = StorageValue<_, DeployedContract, ValueQuery>; #[pallet::storage] #[pallet::getter(fn gate_contract)] @@ -267,9 +267,9 @@ pub mod pallet { /// [registered_address_id, registered_address] AddressRegistered(AddressId, Address), - /// Collecting coins from Eth ERC-20 has been registered and will be verified. - /// [collected_coins_id, registered_collect_coins] - CollectCoinsRegistered(CollectedCoinsId, types::UnverifiedCollectedCoins), + // /// Collecting coins from Eth ERC-20 has been registered and will be verified. + // /// [collected_coins_id, registered_collect_coins] + //CollectCoinsRegistered(CollectedCoinsId, types::UnverifiedCollectedCoins), /// An external transfer has been registered and will be verified. /// [registered_transfer_id, registered_transfer] @@ -282,12 +282,12 @@ pub mod pallet { /// [verified_transfer_id] TransferVerified(TransferId), - /// CollectCoins has been successfully verified and minted. - /// [collected_coins_id, collected_coins] - CollectedCoinsMinted( - types::CollectedCoinsId, - types::CollectedCoinsStruct, - ), + // /// CollectCoins has been successfully verified and minted. + // /// [collected_coins_id, collected_coins] + // CollectedCoinsMinted( + // types::CollectedCoinsId, + // types::CollectedCoinsStruct, + // ), /// An external transfer has been processed and marked as part of a loan. /// [processed_transfer_id] @@ -352,9 +352,9 @@ pub mod pallet { TransferFailedVerification(TransferId, VerificationFailureCause), - /// exchanging vested ERC-20 CC for native CC failed. - /// [collected_coins_id, cause] - CollectCoinsFailedVerification(CollectedCoinsId, VerificationFailureCause), + // /// exchanging vested ERC-20 CC for native CC failed. + // /// [collected_coins_id, cause] + // CollectCoinsFailedVerification(CollectedCoinsId, VerificationFailureCause), Burned(BurnId), } @@ -655,9 +655,9 @@ pub mod pallet { ::WeightInfo::on_initialize(ask_count, bid_count, offer_count, 0, 0) } - fn on_runtime_upgrade() -> Weight { - migrations::migrate::() - } + // fn on_runtime_upgrade() -> Weight { + // migrations::migrate::() + // } } #[pallet::call] @@ -1166,52 +1166,52 @@ pub mod pallet { Ok(()) } - #[transactional] - #[pallet::call_index(10)] - #[pallet::weight(::WeightInfo::request_collect_coins())] - pub fn request_collect_coins( - origin: OriginFor, - evm_address: ExternalAddress, - tx_id: ExternalTxId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let contract = Self::collect_coins_contract(); - - let pending = types::UnverifiedCollectedCoins { - to: evm_address, - tx_id, - contract, - contract_type: crate::types::ContractType::GCRE, - }; - - let collect_coins_id = TaskV2::::to_id(&pending); - - ensure!( - !>::is_persisted(&collect_coins_id), - Error::::CollectCoinsAlreadyRegistered - ); - - let deadline = T::TaskScheduler::deadline(); - - ensure!( - !T::TaskScheduler::is_scheduled(&deadline, &collect_coins_id), - Error::::CollectCoinsAlreadyRegistered - ); - - let address_id = AddressId::new::(&pending.contract.chain, &pending.to); - let address = Self::addresses(address_id).ok_or(Error::::NonExistentAddress)?; - ensure!(address.owner == who, Error::::NotAddressOwner); - - T::TaskScheduler::insert(&deadline, &collect_coins_id, Task::from(pending.clone())); - - Self::deposit_event(Event::::CollectCoinsRegistered( - collect_coins_id.into(), - pending, - )); - - Ok(()) - } + // #[transactional] + // #[pallet::call_index(10)] + // #[pallet::weight(::WeightInfo::request_collect_coins())] + // pub fn request_collect_coins( + // origin: OriginFor, + // evm_address: ExternalAddress, + // tx_id: ExternalTxId, + // ) -> DispatchResult { + // let who = ensure_signed(origin)?; + // + // let contract = Self::collect_coins_contract(); + // + // let pending = types::UnverifiedCollectedCoins { + // to: evm_address, + // tx_id, + // contract, + // contract_type: crate::types::ContractType::GCRE, + // }; + // + // let collect_coins_id = TaskV2::::to_id(&pending); + // + // ensure!( + // !>::is_persisted(&collect_coins_id), + // Error::::CollectCoinsAlreadyRegistered + // ); + // + // let deadline = T::TaskScheduler::deadline(); + // + // ensure!( + // !T::TaskScheduler::is_scheduled(&deadline, &collect_coins_id), + // Error::::CollectCoinsAlreadyRegistered + // ); + // + // let address_id = AddressId::new::(&pending.contract.chain, &pending.to); + // let address = Self::addresses(address_id).ok_or(Error::::NonExistentAddress)?; + // ensure!(address.owner == who, Error::::NotAddressOwner); + // + // T::TaskScheduler::insert(&deadline, &collect_coins_id, Task::from(pending.clone())); + // + // Self::deposit_event(Event::::CollectCoinsRegistered( + // collect_coins_id.into(), + // pending, + // )); + // + // Ok(()) + // } #[transactional] #[pallet::call_index(11)] @@ -1321,13 +1321,13 @@ pub mod pallet { #[transactional] #[pallet::call_index(16)] #[pallet::weight(match &task_output { - crate::TaskOutput::CollectCoins(..) => ::WeightInfo::persist_collect_coins(), + //crate::TaskOutput::CollectCoins(..) => ::WeightInfo::persist_collect_coins(), crate::TaskOutput::VerifyTransfer(..) => ::WeightInfo::persist_transfer(), })] pub fn persist_task_output( origin: OriginFor, deadline: T::BlockNumber, - task_output: TaskOutput, + task_output: TaskOutput, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; @@ -1346,62 +1346,62 @@ pub mod pallet { Transfers::::insert(&id, transfer); (id.clone().into_inner(), Event::::TransferVerified(id)) }, - TaskOutput::CollectCoins(id, collected_coins) => { - ensure!( - !CollectedCoins::::contains_key(&id), - non_paying_error(Error::::CollectCoinsAlreadyRegistered) - ); - - let address = Self::addresses(&collected_coins.to) - .ok_or(Error::::NonExistentAddress)?; - - match collected_coins.contract_type { - ContractType::GCRE => { - as Mutate>::mint_into( - &address.owner, - collected_coins.amount, - )?; - - CollectedCoins::::insert(&id, collected_coins.clone()); - ( - id.clone().into_inner(), - Event::::CollectedCoinsMinted(id, collected_coins), - ) - }, - ContractType::GATE => { - let faucet_address = Self::gate_faucet_account() - .ok_or(Error::::BurnGATEFaucetNotSet)?; - - let dest = Self::addresses(&collected_coins.to) - .ok_or(Error::::NonExistentAddress)?; - - let transfer = - as CurrencyT>::transfer( - &faucet_address, - &dest.owner, - collected_coins.amount, - ExistenceRequirement::AllowDeath, - ); - - match transfer { - Ok(_) => { - CollectedCoins::::insert(&id, collected_coins.clone()); - ( - id.clone().into_inner(), - Event::::CollectedCoinsMinted(id, collected_coins), - ) - }, - Err(_) => ( - id.clone().into_inner(), - Event::::CollectCoinsFailedVerification( - id, - VerificationFailureCause::InsufficientFaucetBalance, - ), - ), - } - }, - } - }, + // TaskOutput::CollectCoins(id, collected_coins) => { + // ensure!( + // !CollectedCoins::::contains_key(&id), + // non_paying_error(Error::::CollectCoinsAlreadyRegistered) + // ); + // + // let address = Self::addresses(&collected_coins.to) + // .ok_or(Error::::NonExistentAddress)?; + // + // match collected_coins.contract_type { + // ContractType::GCRE => { + // as Mutate>::mint_into( + // &address.owner, + // collected_coins.amount, + // )?; + // + // CollectedCoins::::insert(&id, collected_coins.clone()); + // ( + // id.clone().into_inner(), + // Event::::CollectedCoinsMinted(id, collected_coins), + // ) + // }, + // ContractType::GATE => { + // let faucet_address = Self::gate_faucet_account() + // .ok_or(Error::::BurnGATEFaucetNotSet)?; + // + // let dest = Self::addresses(&collected_coins.to) + // .ok_or(Error::::NonExistentAddress)?; + // + // let transfer = + // as CurrencyT>::transfer( + // &faucet_address, + // &dest.owner, + // collected_coins.amount, + // ExistenceRequirement::AllowDeath, + // ); + // + // match transfer { + // Ok(_) => { + // CollectedCoins::::insert(&id, collected_coins.clone()); + // ( + // id.clone().into_inner(), + // Event::::CollectedCoinsMinted(id, collected_coins), + // ) + // }, + // Err(_) => ( + // id.clone().into_inner(), + // Event::::CollectCoinsFailedVerification( + // id, + // VerificationFailureCause::InsufficientFaucetBalance, + // ), + // ), + // } + // }, + // } + // }, }; T::TaskScheduler::remove(&deadline, &task_id); @@ -1413,7 +1413,7 @@ pub mod pallet { #[pallet::call_index(17)] #[pallet::weight(match &task_id { crate::TaskId::VerifyTransfer(..) => ::WeightInfo::fail_transfer(), - crate::TaskId::CollectCoins(..) => ::WeightInfo::fail_collect_coins(), + //crate::TaskId::CollectCoins(..) => ::WeightInfo::fail_collect_coins(), })] pub fn fail_task( origin: OriginFor, @@ -1436,16 +1436,16 @@ pub mod pallet { Event::::TransferFailedVerification(transfer_id, cause), ) }, - TaskId::CollectCoins(collected_coins_id) => { - ensure!( - !CollectedCoins::::contains_key(&collected_coins_id), - Error::::CollectCoinsAlreadyRegistered - ); - ( - collected_coins_id.clone().into_inner(), - Event::::CollectCoinsFailedVerification(collected_coins_id, cause), - ) - }, + // TaskId::CollectCoins(collected_coins_id) => { + // ensure!( + // !CollectedCoins::::contains_key(&collected_coins_id), + // Error::::CollectCoinsAlreadyRegistered + // ); + // ( + // collected_coins_id.clone().into_inner(), + // Event::::CollectCoinsFailedVerification(collected_coins_id, cause), + // ) + // }, }; T::TaskScheduler::remove(&deadline, &task_id); Self::deposit_event(event); @@ -1468,17 +1468,17 @@ pub mod pallet { Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No }) } - #[transactional] - #[pallet::call_index(20)] - #[pallet::weight(::WeightInfo::set_collect_coins_contract())] - pub fn set_collect_coins_contract( - origin: OriginFor, - contract: DeployedContract, - ) -> DispatchResult { - ensure_root(origin)?; - CollectCoinsContract::::put(contract); - Ok(()) - } + // #[transactional] + // #[pallet::call_index(20)] + // #[pallet::weight(::WeightInfo::set_collect_coins_contract())] + // pub fn set_collect_coins_contract( + // origin: OriginFor, + // contract: DeployedContract, + // ) -> DispatchResult { + // ensure_root(origin)?; + // CollectCoinsContract::::put(contract); + // Ok(()) + // } #[transactional] #[pallet::call_index(21)] @@ -1573,67 +1573,67 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(25)] - #[pallet::weight(::WeightInfo::request_collect_coins_v2())] - pub fn request_collect_coins_v2( - origin: OriginFor, - contract: BurnDetails, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let contract_type; - let deployed_contract; - let tx_id; - let evm_address; - - match contract { - BurnDetails::GCRE(ext_addr, tx_hash) => { - deployed_contract = Self::collect_coins_contract(); - contract_type = ContractType::GCRE; - tx_id = tx_hash; - evm_address = ext_addr; - }, - BurnDetails::GATE(ext_addr, tx_hash) => { - deployed_contract = Self::gate_contract(); - contract_type = ContractType::GATE; - tx_id = tx_hash; - evm_address = ext_addr; - Self::gate_faucet_account().ok_or(Error::::BurnGATEFaucetNotSet)?; - }, - } - - let pending = types::UnverifiedCollectedCoins { - to: evm_address, - tx_id, - contract: deployed_contract, - contract_type, - }; - - let collect_coins_id = TaskV2::::to_id(&pending); - - ensure!( - !>::is_persisted(&collect_coins_id), - Error::::CollectCoinsAlreadyRegistered - ); - - let deadline = T::TaskScheduler::deadline(); - - ensure!( - !T::TaskScheduler::is_scheduled(&deadline, &collect_coins_id), - Error::::CollectCoinsAlreadyRegistered - ); - - let address_id = AddressId::new::(&pending.contract.chain, &pending.to); - let address = Self::addresses(address_id).ok_or(Error::::NonExistentAddress)?; - ensure!(address.owner == who, Error::::NotAddressOwner); - - T::TaskScheduler::insert(&deadline, &collect_coins_id, Task::from(pending.clone())); - - Self::deposit_event(Event::::CollectCoinsRegistered( - collect_coins_id.into(), - pending, - )); - - Ok(()) - } + // #[pallet::call_index(25)] + // #[pallet::weight(::WeightInfo::request_collect_coins_v2())] + // pub fn request_collect_coins_v2( + // origin: OriginFor, + // contract: BurnDetails, + // ) -> DispatchResult { + // let who = ensure_signed(origin)?; + // let contract_type; + // let deployed_contract; + // let tx_id; + // let evm_address; + // + // match contract { + // BurnDetails::GCRE(ext_addr, tx_hash) => { + // deployed_contract = Self::collect_coins_contract(); + // contract_type = ContractType::GCRE; + // tx_id = tx_hash; + // evm_address = ext_addr; + // }, + // BurnDetails::GATE(ext_addr, tx_hash) => { + // deployed_contract = Self::gate_contract(); + // contract_type = ContractType::GATE; + // tx_id = tx_hash; + // evm_address = ext_addr; + // Self::gate_faucet_account().ok_or(Error::::BurnGATEFaucetNotSet)?; + // }, + // } + // + // let pending = types::UnverifiedCollectedCoins { + // to: evm_address, + // tx_id, + // contract: deployed_contract, + // contract_type, + // }; + // + // let collect_coins_id = TaskV2::::to_id(&pending); + // + // ensure!( + // !>::is_persisted(&collect_coins_id), + // Error::::CollectCoinsAlreadyRegistered + // ); + // + // let deadline = T::TaskScheduler::deadline(); + // + // ensure!( + // !T::TaskScheduler::is_scheduled(&deadline, &collect_coins_id), + // Error::::CollectCoinsAlreadyRegistered + // ); + // + // let address_id = AddressId::new::(&pending.contract.chain, &pending.to); + // let address = Self::addresses(address_id).ok_or(Error::::NonExistentAddress)?; + // ensure!(address.owner == who, Error::::NotAddressOwner); + // + // T::TaskScheduler::insert(&deadline, &collect_coins_id, Task::from(pending.clone())); + // + // Self::deposit_event(Event::::CollectCoinsRegistered( + // collect_coins_id.into(), + // pending, + // )); + // + // Ok(()) + // } } } diff --git a/pallets/creditcoin/src/ocw/tasks.rs b/pallets/creditcoin/src/ocw/tasks.rs index 3da3acf9bc..7b161e32e0 100644 --- a/pallets/creditcoin/src/ocw/tasks.rs +++ b/pallets/creditcoin/src/ocw/tasks.rs @@ -1,4 +1,4 @@ -pub mod collect_coins; +//pub mod collect_coins; pub mod verify_transfer; use crate::ocw::errors::VerificationResult; @@ -34,9 +34,9 @@ where VerifyTransfer(unverified) => { unverified.forward_task(deadline).map(|c: crate::pallet::Call| c.into()) }, - CollectCoins(unverified) => { - unverified.forward_task(deadline).map(|c: crate::pallet::Call| c.into()) - }, + // CollectCoins(unverified) => { + // unverified.forward_task(deadline).map(|c: crate::pallet::Call| c.into()) + // }, } } } diff --git a/pallets/creditcoin/src/ocw/tasks/collect_coins.rs b/pallets/creditcoin/src/ocw/tasks/collect_coins.rs index 0fff4492d0..6a95c7666a 100644 --- a/pallets/creditcoin/src/ocw/tasks/collect_coins.rs +++ b/pallets/creditcoin/src/ocw/tasks/collect_coins.rs @@ -1,1388 +1,1388 @@ -use crate::ocw::{ - self, - errors::{VerificationFailureCause, VerificationResult}, - rpc::{self, EthTransaction, EthTransactionReceipt}, - OffchainResult, ETH_CONFIRMATIONS, -}; -use crate::pallet::{Config as CreditcoinConfig, Pallet}; -use crate::{ - types::{Blockchain, ContractType, UnverifiedCollectedCoins}, - ExternalAddress, ExternalAmount, -}; -use core::default::Default; -use ethabi::{Function, Param, ParamType, StateMutability, Token}; -use ethereum_types::U64; -use frame_support::{ensure, RuntimeDebug}; -use hex_literal::hex; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::H160; -use sp_runtime::SaturatedConversion; -#[cfg_attr(feature = "std", allow(unused_imports))] -use sp_std::prelude::*; - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct DeployedContract { - pub address: sp_core::H160, - pub chain: Blockchain, -} - -impl DeployedContract { - const DEFAULT_CHAIN: Blockchain = Blockchain::Ethereum; -} - -impl Default for DeployedContract { - fn default() -> Self { - let contract_chain: Blockchain = DeployedContract::DEFAULT_CHAIN; - let contract_address: H160 = - sp_core::H160(hex!("a3EE21C306A700E682AbCdfe9BaA6A08F3820419")); - Self { address: contract_address, chain: contract_chain } - } -} - -impl DeployedContract { - ///exchange has been deprecated, use burn instead - fn burn_vested_cc_abi() -> Function { - #[allow(deprecated)] - Function { - name: "burn".into(), - inputs: vec![Param { - name: "value".into(), - kind: ParamType::Uint(256), - internal_type: None, - }], - outputs: vec![Param { - name: "success".into(), - kind: ParamType::Bool, - internal_type: None, - }], - constant: Some(false), - state_mutability: StateMutability::NonPayable, - } - } -} - -pub fn validate_collect_coins( - external_address: &ExternalAddress, - receipt: &EthTransactionReceipt, - transaction: &EthTransaction, - eth_tip: U64, - contract_address: &H160, -) -> OffchainResult { - ensure!(receipt.is_success(), VerificationFailureCause::TaskFailed); - - let block_number = transaction.block_number.ok_or(VerificationFailureCause::TaskPending)?; - - let diff = (eth_tip) - .checked_sub(block_number) - .ok_or(VerificationFailureCause::TaskInFuture)?; - ensure!(diff.as_u64() >= ETH_CONFIRMATIONS, VerificationFailureCause::TaskUnconfirmed); - - if let Some(recipient) = &transaction.recipient { - ensure!(recipient == contract_address, VerificationFailureCause::IncorrectContract); - } else { - return Err(VerificationFailureCause::MissingReceiver.into()); - } - - if let Some(sender) = &transaction.sender { - ensure!(sender[..] == external_address[..], VerificationFailureCause::IncorrectSender) - } else { - return Err(VerificationFailureCause::MissingSender.into()); - } - - let transfer_fn = DeployedContract::burn_vested_cc_abi(); - ensure!(!transaction.is_input_empty(), VerificationFailureCause::EmptyInput); - - { - let selector = transaction.selector(); - if selector != transfer_fn.short_signature() { - log::error!( - "function selector mismatch, expected: {}, got: {}", - hex::encode(transfer_fn.short_signature()), - hex::encode(selector) - ); - return Err(VerificationFailureCause::AbiMismatch.into()); - } - } - - let inputs = transfer_fn.decode_input(transaction.input()).map_err(|e| { - log::error!("failed to decode inputs: {:?}", e); - VerificationFailureCause::AbiMismatch - })?; - - match inputs.get(0) { - Some(Token::Uint(value)) => Ok(ExternalAmount::from(value)), - _ => Err(VerificationFailureCause::IncorrectInputType.into()), - } -} - -impl Pallet { - ///Amount is saturated to u128, don't exchange more than u128::MAX at once. - pub fn verify_collect_coins_ocw( - u_cc: &UnverifiedCollectedCoins, - ) -> VerificationResult { - log::debug!("verifying OCW Collect Coins"); - let UnverifiedCollectedCoins { - to, - tx_id, - contract: DeployedContract { address, chain }, - contract_type, - } = u_cc; - let rpc_url = &chain.rpc_url()?; - let tx = ocw::eth_get_transaction(tx_id, rpc_url)?; - let tx_receipt = rpc::eth_get_transaction_receipt(tx_id, rpc_url)?; - let eth_tip = rpc::eth_get_block_number(rpc_url)?; - - let mut amount = validate_collect_coins(to, &tx_receipt, &tx, eth_tip, address)?; - - // GATE -> CTC is swapped 2:1 - if *contract_type == ContractType::GATE { - amount /= 2; - } - - let amount = amount.saturated_into::().saturated_into::(); - - Ok(amount) - } -} - -#[cfg(any(test, feature = "runtime-benchmarks"))] -pub(crate) mod testing_constants { - use super::{Blockchain, DeployedContract}; - - pub const CHAIN: Blockchain = DeployedContract::DEFAULT_CHAIN; -} - -#[cfg(test)] -pub(crate) mod tests { - - use super::*; - use crate::mock::{self, PendingRequestExt, RuntimeCall}; - use crate::types::ContractType; - use std::collections::HashMap; - - // txn.from has been overriden by 'generate_address_with_proof("collector")' - static RESPONSES: Lazy>> = Lazy::new(|| { - serde_json::from_slice(include_bytes!("../../tests/collectCoins.json")).unwrap() - }); - - static BLOCK_NUMBER: Lazy = Lazy::new(|| { - let responses = &*RESPONSES; - let bn = - responses["eth_getTransactionByHash"].result.clone().unwrap()["blockNumber"].clone(); - serde_json::from_value(bn).unwrap() - }); - - static BLOCK_NUMBER_STR: Lazy = Lazy::new(|| { - let responses = &*RESPONSES; - let bn = - responses["eth_getTransactionByHash"].result.clone().unwrap()["blockNumber"].clone(); - serde_json::from_value(bn).unwrap() - }); - - static VESTING_CONTRACT: Lazy = Lazy::new(|| { - let responses = &*RESPONSES; - let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["to"].clone(); - let val: String = serde_json::from_value(val).unwrap(); - let vesting_contract = hex::decode(val.trim_start_matches("0x")).unwrap(); - H160::from(<[u8; 20]>::try_from(vesting_contract.as_slice()).unwrap()) - }); - - // txn.from has been overriden by 'generate_address_with_proof("collector")' - static FROM: Lazy = Lazy::new(|| { - let responses = &*RESPONSES; - let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["from"].clone(); - serde_json::from_value(val).unwrap() - }); - - static INPUT: Lazy = Lazy::new(|| { - let responses = &*RESPONSES; - let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["input"].clone(); - let val: String = serde_json::from_value(val).unwrap(); - let input_bytes = hex::decode(val.trim_start_matches("0x")).unwrap(); - input_bytes.into() - }); - - pub(crate) static TX_HASH: Lazy = Lazy::new(|| { - let responses = &*RESPONSES; - let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["hash"].clone(); - serde_json::from_value(val).unwrap() - }); - - pub(crate) static RPC_RESPONSE_AMOUNT: Lazy = Lazy::new(|| { - let transfer_fn = DeployedContract::burn_vested_cc_abi(); - - let inputs = transfer_fn.decode_input(&(INPUT.0)[4..]).unwrap(); - - let amount = inputs.get(0).unwrap(); - if let Token::Uint(value) = amount { - ExternalAmount::from(value) - } else { - panic!("Not Token::Uint"); - } - }); - - use super::testing_constants::CHAIN; - use crate::helpers::extensions::HexToAddress; - use crate::helpers::non_paying_error; - use crate::mock::{ - roll_by_with_ocw, set_rpc_uri, AccountId, Balances, ExtBuilder, MockedRpcRequests, - OffchainState, RuntimeOrigin, RwLock, TaskScheduler, Test, - }; - use crate::ocw::tasks::TaskV2; - use crate::ocw::{ - errors::{OffchainError, VerificationFailureCause as Cause}, - rpc::{EthTransaction, EthTransactionReceipt}, - ETH_CONFIRMATIONS, - }; - use crate::tests::generate_address_with_proof; - use crate::types::{AddressId, CollectedCoinsId, CollectedCoinsStruct}; - use crate::{ocw::rpc::JsonRpcResponse, BurnDetails, ExternalAddress, ExternalTxId}; - use crate::{Pallet as Creditcoin, Task}; - use alloc::sync::Arc; - use assert_matches::assert_matches; - use frame_support::dispatch::Dispatchable; - use frame_support::{assert_noop, assert_ok, once_cell::sync::Lazy, traits::Currency}; - use frame_system::Pallet as System; - use frame_system::RawOrigin; - use pallet_offchain_task_scheduler::tasks::TaskScheduler as TaskSchedulerT; - use pallet_offchain_task_scheduler::Pallet as TaskSchedulerPallet; - use parity_scale_codec::Decode; - use sp_runtime::traits::{BadOrigin, IdentifyAccount}; - use sp_runtime::{ArithmeticError, TokenError}; - use std::convert::TryFrom; - - fn prepare_rpc_mocks() -> MockedRpcRequests { - let dummy_url = "dummy"; - let contract_chain = Creditcoin::::collect_coins_contract(); - set_rpc_uri(&contract_chain.chain, dummy_url); - - MockedRpcRequests::new(dummy_url, &TX_HASH, &BLOCK_NUMBER_STR, &RESPONSES) - } - - /// call from externalities context - pub(crate) fn mock_rpc_for_collect_coins(state: &Arc>) { - let mut rpcs = prepare_rpc_mocks(); - rpcs.mock_get_block_number(&mut state.write()); - } - - struct PassingCollectCoins { - to: ExternalAddress, - receipt: EthTransactionReceipt, - transaction: EthTransaction, - eth_tip: U64, - contract_address: H160, - } - - impl Default for PassingCollectCoins { - fn default() -> Self { - let base_height = *BLOCK_NUMBER; - let vesting_contract = *VESTING_CONTRACT; - let to = FROM.hex_to_address(); - let tx_from = H160::from(<[u8; 20]>::try_from(to.as_slice()).unwrap()); - - let mut transaction = EthTransaction::default(); - transaction.block_number = Some(base_height); - transaction.sender = Some(tx_from); - transaction.recipient = Some(vesting_contract); - transaction.set_input(&INPUT.0); - - Self { - to, - receipt: EthTransactionReceipt { status: Some(1u64.into()), ..Default::default() }, - transaction, - eth_tip: (base_height + ETH_CONFIRMATIONS), - contract_address: DeployedContract::default().address, - } - } - } - - impl PassingCollectCoins { - fn validate(self) -> OffchainResult { - let PassingCollectCoins { to, receipt, transaction, eth_tip, contract_address } = self; - super::validate_collect_coins(&to, &receipt, &transaction, eth_tip, &contract_address) - } - } - - fn assert_invalid(res: OffchainResult, cause: VerificationFailureCause) { - assert_matches!(res, Err(OffchainError::InvalidTask(c)) =>{ assert_eq!(c,cause); }); - } - - #[test] - fn valid() { - assert_matches!(PassingCollectCoins::default().validate(), Ok(_)); - } - - #[test] - fn txn_success() { - let mut pcc = PassingCollectCoins::default(); - pcc.receipt.status = Some(0u64.into()); - assert_invalid(pcc.validate(), Cause::TaskFailed); - } - - #[test] - fn pending() { - let mut transaction = EthTransaction::default(); - transaction.block_number = None; - let pcc = PassingCollectCoins { transaction, ..Default::default() }; - assert_invalid(pcc.validate(), Cause::TaskPending); - } - - #[test] - fn in_the_future() { - let pcc = PassingCollectCoins { eth_tip: 0u64.into(), ..Default::default() }; - assert_invalid(pcc.validate(), Cause::TaskInFuture); - } - - #[test] - fn unconfirmed() { - let mut pcc = PassingCollectCoins::default(); - pcc.eth_tip = pcc.transaction.block_number.unwrap(); - assert_invalid(pcc.validate(), Cause::TaskUnconfirmed); - } - - #[test] - fn missing_receiver() { - let mut pcc = PassingCollectCoins::default(); - pcc.transaction.recipient = None; - assert_invalid(pcc.validate(), Cause::MissingReceiver); - } - - #[test] - fn incorrect_contract() { - let mut pcc = PassingCollectCoins::default(); - let address = [0u8; 20]; - let address = H160::from(<[u8; 20]>::try_from(address.as_slice()).unwrap()); - pcc.transaction.recipient = Some(address); - assert_invalid(pcc.validate(), Cause::IncorrectContract); - } - - #[test] - fn missing_sender() { - let mut pcc = PassingCollectCoins::default(); - pcc.transaction.sender = None; - assert_invalid(pcc.validate(), Cause::MissingSender); - } - - #[test] - fn incorrect_sender() { - let mut pcc = PassingCollectCoins::default(); - let address = [0u8; 20]; - let address = H160::from(<[u8; 20]>::try_from(address.as_slice()).unwrap()); - pcc.transaction.sender = Some(address); - assert_invalid(pcc.validate(), Cause::IncorrectSender); - } - - #[test] - fn empty_input() { - let mut pcc = PassingCollectCoins::default(); - pcc.transaction.set_input(b""); - assert_invalid(pcc.validate(), Cause::EmptyInput); - } - - #[test] - fn amount_set() -> OffchainResult<()> { - let pcc = PassingCollectCoins::default(); - let PassingCollectCoins { to, receipt, transaction, eth_tip, contract_address } = pcc; - let amount = - super::validate_collect_coins(&to, &receipt, &transaction, eth_tip, &contract_address)?; - assert_eq!(amount, *RPC_RESPONSE_AMOUNT); - Ok(()) - } - - #[test] - fn fail_collect_coins_should_error_when_not_signed() { - let ext = ExtBuilder::default(); - let expected_collected_coins_id = - crate::CollectedCoinsId::new::(&CHAIN, &[0]); - - ext.build_offchain_and_execute_with_state(|_state, _pool| { - assert_noop!( - Creditcoin::::fail_task( - RuntimeOrigin::none(), - Test::unverified_transfer_deadline(), - expected_collected_coins_id.clone().into(), - Cause::AbiMismatch, - ), - BadOrigin - ); - }); - } - - #[test] - fn fail_collect_coins_should_error_when_no_authority() { - let ext = ExtBuilder::default(); - let (molly, _, _, _) = generate_address_with_proof("malicious"); - let expected_collected_coins_id = - crate::CollectedCoinsId::new::(&CHAIN, &[0]); - - ext.build_offchain_and_execute_with_state(|_state, _pool| { - assert_noop!( - Creditcoin::::fail_task( - RuntimeOrigin::signed(molly), - Test::unverified_transfer_deadline(), - expected_collected_coins_id.clone().into(), - Cause::AbiMismatch, - ), - crate::Error::::InsufficientAuthority - ); - }); - } - - #[test] - fn fail_collect_coins_should_fail_when_transfer_has_already_been_registered() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - - ext.build_offchain_and_execute_with_state(|_state, _pool| { - System::::set_block_number(1); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc), - CHAIN, - addr, - sign - )); - - let deadline = Test::unverified_transfer_deadline(); - - let pcc = PassingCollectCoins::default(); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &pcc.to[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = - crate::CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - assert_ok!(Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth.clone()), - deadline, - (collected_coins_id.clone(), collected_coins).into(), - )); - - assert_noop!( - Creditcoin::::fail_task( - RuntimeOrigin::signed(auth), - Test::unverified_transfer_deadline(), - collected_coins_id.into(), - Cause::AbiMismatch, - ), - crate::Error::::CollectCoinsAlreadyRegistered - ); - }); - } - - #[test] - fn fail_collect_coins_emits_events() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - let expected_collected_coins_id = - crate::CollectedCoinsId::new::(&CHAIN, &[0]); - - ext.build_offchain_and_execute_with_state(|_state, _pool| { - System::::set_block_number(1); - - assert_ok!(Creditcoin::::fail_task( - RuntimeOrigin::signed(auth), - Test::unverified_transfer_deadline(), - expected_collected_coins_id.clone().into(), - Cause::AbiMismatch, - )); - - let event = System::::events().pop().expect("an event").event; - assert_matches!( - event, - crate::mock::RuntimeEvent::Creditcoin(crate::Event::::CollectCoinsFailedVerification(collected_coins_id, cause)) => { - assert_eq!(collected_coins_id, expected_collected_coins_id); - assert_eq!(cause, Cause::AbiMismatch); - } - ); - }); - } - - #[test] - fn ocw_fail_collect_coins_works() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let expected_collected_coins_id = - crate::CollectedCoinsId::new::(&CHAIN, &[0]); - ext.build_offchain_and_execute_with_state(|_state, pool| { - crate::mock::roll_to(1); - let call = crate::Call::::fail_task { - task_id: expected_collected_coins_id.into(), - cause: Cause::AbiMismatch, - deadline: Test::unverified_transfer_deadline(), - }; - assert_ok!(TaskSchedulerPallet::::offchain_signed_tx(acct_pubkey.into(), |_| { - call.clone().into() - },)); - crate::mock::roll_to(2); - - assert_matches!(pool.write().transactions.pop(), Some(tx) => { - let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); - }); - }); - } - - #[test] - fn persist_collect_coins() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _| { - System::::set_block_number(1); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr, - sign - )); - - let deadline = Test::unverified_transfer_deadline(); - - let pcc = PassingCollectCoins::default(); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &pcc.to[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - let balance = >::total_balance; - - let pre_authority_balance = balance(&auth); - let pre_collector_balance = balance(&acc); - - assert_ok!(Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth.clone()), - deadline, - (collected_coins_id.clone(), collected_coins.clone()).into(), - )); - - let event = >::events().pop().expect("an event").event; - - assert_matches!( - event, - crate::mock::RuntimeEvent::Creditcoin(crate::Event::::CollectedCoinsMinted(id, item)) => { - assert_eq!(id, collected_coins_id); - assert_eq!(item, collected_coins); - } - ); - //do not mint into authority - assert_eq!(pre_authority_balance, balance(&auth)); - // assert on deposit - assert_eq!(pre_collector_balance.saturating_add(collected_coins.amount), balance(&acc)); - }); - } - - #[test] - fn persist_unregistered_address() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _| { - let pcc = PassingCollectCoins::default(); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &pcc.to[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - let deadline = Test::unverified_transfer_deadline(); - - assert_noop!( - Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth), - deadline, - (collected_coins_id, collected_coins).into(), - ), - crate::Error::::NonExistentAddress - ); - }); - } - - #[test] - fn persist_more_than_max_balance_should_error() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _| { - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc), - CHAIN, - addr, - sign - )); - - let pcc = PassingCollectCoins::default(); - - // lower free balance so that collect coins would overflow - let cash = >::minimum_balance(); - >::make_free_balance_be(&auth, cash); - - let collected_coins_id = - crate::CollectedCoinsId::new::(&CHAIN, &TX_HASH.hex_to_address()); - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &pcc.to[..]), - amount: u128::MAX, - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - - assert_noop!( - Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth), - Test::unverified_transfer_deadline(), - (collected_coins_id, collected_coins).into(), - ), - ArithmeticError::Overflow - ); - }); - } - - #[test] - fn request_collect_coins_should_fail_when_not_signed() { - let ext = ExtBuilder::default(); - ext.build_offchain_and_execute_with_state(|_, _pool| { - let (_, addr, _, _) = generate_address_with_proof("collector"); - - assert_noop!( - Creditcoin::::request_collect_coins( - RuntimeOrigin::none(), - addr, - TX_HASH.hex_to_address(), - ), - BadOrigin - ); - }); - } - - #[test] - fn request_persisted_not_reentrant() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _pool| { - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr.clone(), - sign - )); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - assert_ok!(Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth), - Test::unverified_transfer_deadline(), - (collected_coins_id, collected_coins).into(), - )); - - roll_by_with_ocw(1); - - assert_noop!( - Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(acc.clone()), - addr.clone(), - TX_HASH.hex_to_address(), - ), - crate::Error::::CollectCoinsAlreadyRegistered - ); - - // trying the same with v2 - let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); - assert_noop!( - Creditcoin::::request_collect_coins_v2(RuntimeOrigin::signed(acc), contract,), - crate::Error::::CollectCoinsAlreadyRegistered - ); - }); - } - - #[test] - fn request_pending_not_reentrant() { - let mut ext = ExtBuilder::default(); - ext.generate_authority(); - ext.build_offchain_and_execute_with_state(|_, _| { - System::::set_block_number(1); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr.clone(), - sign - )); - - assert_ok!(Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(acc.clone()), - addr.clone(), - TX_HASH.hex_to_address() - )); - - let collected_coins_id = - CollectedCoinsId::new::(&CHAIN, TX_HASH.hex_to_address().as_slice()); - - let event = >::events().pop().expect("an event").event; - assert_matches!( - event, - crate::mock::RuntimeEvent::Creditcoin(crate::Event::::CollectCoinsRegistered(collect_coins_id, pending)) => { - assert_eq!(collect_coins_id, collected_coins_id); - - let id = TaskV2::::to_id(&pending); - assert!( TaskScheduler::is_scheduled( &Test::unverified_transfer_deadline(), &id)); - - let UnverifiedCollectedCoins { to, tx_id, .. } = pending; - assert_eq!(to, addr); - assert_eq!(tx_id, TX_HASH.hex_to_address()); - } - ); - - assert_noop!( - Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(acc.clone()), - addr.clone(), - TX_HASH.hex_to_address(), - ), - crate::Error::::CollectCoinsAlreadyRegistered - ); - - assert!(Creditcoin::::collected_coins(collected_coins_id).is_none()); - - // trying the same with v2 - let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); - assert_noop!( - Creditcoin::::request_collect_coins_v2( - RuntimeOrigin::signed(acc), - contract, - ), - crate::Error::::CollectCoinsAlreadyRegistered - ); - }); - } - - #[test] - fn request_address_not_registered() { - let ext = ExtBuilder::default(); - ext.build_offchain_and_execute_with_state(|_, _| { - let (acc, addr, _, _) = generate_address_with_proof("collector"); - - assert_noop!( - Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(acc.clone()), - addr.clone(), - TX_HASH.hex_to_address(), - ), - crate::Error::::NonExistentAddress - ); - - // trying the same with v2 - let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); - assert_noop!( - Creditcoin::::request_collect_coins_v2(RuntimeOrigin::signed(acc), contract,), - crate::Error::::NonExistentAddress - ); - }); - } - - #[test] - fn request_not_owner() { - let ext = ExtBuilder::default(); - ext.build_offchain_and_execute_with_state(|_, _| { - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - let (molly, _, _, _) = generate_address_with_proof("malicious"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc), - CHAIN, - addr.clone(), - sign - )); - - assert_noop!( - Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(molly.clone()), - addr.clone(), - TX_HASH.hex_to_address(), - ), - crate::Error::::NotAddressOwner - ); - - // trying the same with v2 - let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); - assert_noop!( - Creditcoin::::request_collect_coins_v2( - RuntimeOrigin::signed(molly), - contract, - ), - crate::Error::::NotAddressOwner - ); - }); - } - - #[test] - fn persist_not_authority() { - let ext = ExtBuilder::default(); - ext.build_offchain_and_execute_with_state(|_, _| { - let (molly, addr, _, _) = generate_address_with_proof("malicious"); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - assert_noop!( - Creditcoin::::persist_task_output( - RuntimeOrigin::signed(molly), - Test::unverified_transfer_deadline(), - (collected_coins_id, collected_coins).into(), - ), - crate::Error::::InsufficientAuthority - ); - }); - } - - #[test] - fn persist_is_submitted() { - let mut ext = ExtBuilder::default(); - ext.generate_authority(); - ext.build_offchain_and_execute_with_state(|state, pool| { - mock_rpc_for_collect_coins(&state); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr.clone(), - sign - )); - - assert_ok!(Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(acc), - addr.clone(), - TX_HASH.hex_to_address() - )); - - let deadline = Test::unverified_transfer_deadline(); - - roll_by_with_ocw(1); - - assert!(!pool.read().transactions.is_empty()); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - let call = crate::Call::::persist_task_output { - task_output: (collected_coins_id, collected_coins).into(), - deadline, - }; - - assert_matches!(pool.write().transactions.pop(), Some(tx) => { - let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); - }); - }); - } - - #[test] - fn request_collect_coins_v2_will_submit_a_persist_task_output_call() { - let mut ext = ExtBuilder::default(); - ext.generate_authority(); - ext.build_offchain_and_execute_with_state(|state, pool| { - mock_rpc_for_collect_coins(&state); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr.clone(), - sign - )); - - let contract = BurnDetails::GCRE(addr.clone(), TX_HASH.hex_to_address()); - assert_ok!(Creditcoin::::request_collect_coins_v2( - RuntimeOrigin::signed(acc), - contract, - ),); - - let deadline = Test::unverified_transfer_deadline(); - - roll_by_with_ocw(1); - - assert!(!pool.read().transactions.is_empty()); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - let call = crate::Call::::persist_task_output { - task_output: (collected_coins_id, collected_coins).into(), - deadline, - }; - - assert_matches!(pool.write().transactions.pop(), Some(tx) => { - let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); - }); - }); - } - - #[test] - fn persist_not_reentrant() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _| { - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc), - CHAIN, - addr.clone(), - sign - )); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - assert_ok!(Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth.clone()), - Test::unverified_transfer_deadline(), - (collected_coins_id.clone(), collected_coins.clone()).into(), - )); - - assert_noop!( - Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth), - Test::unverified_transfer_deadline(), - (collected_coins_id, collected_coins).into(), - ), - non_paying_error(crate::Error::::CollectCoinsAlreadyRegistered) - ); - }); - } - - #[test] - fn unverified_collect_coins_are_removed() { - let mut ext = ExtBuilder::default(); - ext.generate_authority(); - ext.build_offchain_and_execute_with_state(|state, _| { - mock_rpc_for_collect_coins(&state); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr.clone(), - sign - )); - - assert_ok!(Creditcoin::::request_collect_coins( - RuntimeOrigin::signed(acc.clone()), - addr.clone(), - TX_HASH.hex_to_address() - )); - let deadline = Test::unverified_transfer_deadline(); - - roll_by_with_ocw(deadline); - - let collected_coins_id = - CollectedCoinsId::new::(&CHAIN, TX_HASH.hex_to_address().as_slice()) - .into_inner(); - - roll_by_with_ocw(1); - - assert!(!TaskScheduler::is_scheduled(&deadline, &collected_coins_id)); - - // trying the same with v2 - let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); - assert_ok!(Creditcoin::::request_collect_coins_v2( - RuntimeOrigin::signed(acc), - contract, - ),); - - roll_by_with_ocw(deadline); - let collected_coins_id = - CollectedCoinsId::new::(&CHAIN, TX_HASH.hex_to_address().as_slice()) - .into_inner(); - - roll_by_with_ocw(1); - assert!(!TaskScheduler::is_scheduled(&deadline, &collected_coins_id)); - }); - } - - #[test] - fn owner_credited() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _| { - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: RPC_RESPONSE_AMOUNT.as_u128(), - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr, - sign - )); - - assert_ok!(Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth.clone()), - Test::unverified_transfer_deadline(), - (collected_coins_id, collected_coins.clone()).into(), - )); - - assert_eq!( - frame_system::pallet::Account::::get(&acc).data.free, - collected_coins.amount - ); - }); - } - - #[test] - fn selector_mismatch() { - let ext = ExtBuilder::default(); - ext.build_offchain_and_execute_with_state(|state, _| { - mock_rpc_for_collect_coins(&state); - - let (_, to, ..) = generate_address_with_proof("collector"); - let tx_id = &TX_HASH.hex_to_address(); - - let rpc_url = &CHAIN.rpc_url().unwrap(); - let mut tx = rpc::eth_get_transaction(tx_id, rpc_url).unwrap(); - let tx_receipt = rpc::eth_get_transaction_receipt(tx_id, rpc_url).unwrap(); - let eth_tip = rpc::eth_get_block_number(rpc_url).unwrap(); - let PassingCollectCoins { contract_address, .. } = Default::default(); - validate_collect_coins(&to, &tx_receipt, &tx, eth_tip, &contract_address) - .expect("valid"); - // Forged selector - tx.set_input(b"ffffffff"); - assert_matches!( - validate_collect_coins(&to, &tx_receipt, &tx, eth_tip, &contract_address), - Err(OffchainError::InvalidTask(VerificationFailureCause::AbiMismatch)) - ); - }); - } - - #[test] - fn set_collect_coins_only_as_root() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let _auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_and_execute(|| { - let contract = DeployedContract { - address: sp_core::H160(hex!("aaaaabbbbbcccccdddddeeeeefffff08F3820419")), - chain: Blockchain::Rinkeby, - }; - assert_ok!(Creditcoin::::set_collect_coins_contract( - RawOrigin::Root.into(), - contract.clone() - )); - let from_storage = Creditcoin::::collect_coins_contract(); - assert_eq!(contract, from_storage); - assert_ne!(from_storage, DeployedContract::default()); - - let (acc, ..) = generate_address_with_proof("somebody"); - - assert_noop!( - Creditcoin::::set_collect_coins_contract( - RawOrigin::Signed(acc).into(), - contract.clone() - ), - BadOrigin - ); - - assert_noop!( - Creditcoin::::set_collect_coins_contract(RawOrigin::None.into(), contract), - BadOrigin - ); - }); - } - - #[test] - fn gcrecontract_value_query_is_default() { - let contract = DeployedContract::default(); - let ext = ExtBuilder::default(); - ext.build_and_execute(|| { - let value_query = Creditcoin::::collect_coins_contract(); - assert_eq!(contract, value_query); - }); - } - - #[test] - fn persist_minimum_existential_deposit_errors() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|_, _| { - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount: 1u128, - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GCRE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr, - sign - )); - - assert_eq!(Balances::total_balance(&acc), 0); - - assert_noop!( - Creditcoin::::persist_task_output( - RuntimeOrigin::signed(auth.clone()), - Test::unverified_transfer_deadline(), - (collected_coins_id, collected_coins).into(), - ), - TokenError::BelowMinimum - ); - }); - } - - #[test] - fn transaction_not_found() { - ExtBuilder::default().build_offchain_and_execute_with_state(|state, _| { - let mut rpcs = prepare_rpc_mocks(); - rpcs.get_transaction.set_empty_response(); - rpcs.mock_get_transaction(&mut state.write()); - - let (_, addr, _, _) = generate_address_with_proof("collector"); - let cc = UnverifiedCollectedCoins { - to: addr, - tx_id: TX_HASH.hex_to_address(), - contract: DeployedContract::default(), - contract_type: ContractType::GCRE, - }; - assert_matches!( - Creditcoin::::verify_collect_coins_ocw(&cc), - Err(OffchainError::InvalidTask(VerificationFailureCause::TransactionNotFound)) - ); - }); - } - - #[test] - fn unverified_collect_coins_is_removed_after_failing_the_task() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|state, _| { - let mut rpcs = prepare_rpc_mocks(); - rpcs.get_transaction.set_empty_response(); - rpcs.mock_get_transaction(&mut state.write()); - - let (_, addr, _, _) = generate_address_with_proof("collector"); - - let cc = UnverifiedCollectedCoins { - to: addr, - tx_id: TX_HASH.hex_to_address(), - contract: DeployedContract::default(), - contract_type: ContractType::GCRE, - }; - - let id = TaskV2::::to_id(&cc); - let deadline = TaskScheduler::deadline(); - - TaskScheduler::insert(&deadline, &id, Task::CollectCoins(cc.clone())); - - let call = - TaskV2::::persistence_call(&cc, TaskScheduler::deadline(), &id).unwrap(); - assert!(matches!(call, crate::Call::fail_task { .. })); - let c = RuntimeCall::from(call); - - assert_ok!(c.dispatch(RuntimeOrigin::signed(auth))); - assert!(!TaskScheduler::is_scheduled(&TaskScheduler::deadline(), &id)); - }); - } - - #[test] - fn unverified_collect_coins_is_removed_after_persisting_the_task() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|state, _| { - mock_rpc_for_collect_coins(&state); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc), - CHAIN, - addr.clone(), - sign - )); - - let cc = UnverifiedCollectedCoins { - to: addr, - tx_id: TX_HASH.hex_to_address(), - contract: DeployedContract::default(), - contract_type: ContractType::GCRE, - }; - - let id = TaskV2::::to_id(&cc); - let deadline = TaskScheduler::deadline(); - - TaskScheduler::insert(&deadline, &id, Task::CollectCoins(cc.clone())); - - let call = - TaskV2::::persistence_call(&cc, TaskScheduler::deadline(), &id).unwrap(); - assert!(matches!(call, crate::Call::persist_task_output { .. })); - let c = RuntimeCall::from(call); - - assert_ok!(c.dispatch(RuntimeOrigin::signed(auth))); - assert!(!TaskScheduler::is_scheduled(&TaskScheduler::deadline(), &id)); - }); - } - - #[test] - fn collect_coins_v2_gate_token_persist_is_submitted_and_amount_is_2_to_1() { - let mut ext = ExtBuilder::default(); - let acct_pubkey = ext.generate_authority(); - let auth = AccountId::from(acct_pubkey.into_account().0); - ext.build_offchain_and_execute_with_state(|state, pool| { - mock_rpc_for_collect_coins(&state); - - let (acc, addr, sign, _) = generate_address_with_proof("collector"); - - let _ = as Currency< - ::AccountId, - >>::deposit_creating(&auth, 1000000000000000000); // I hope this is enough - - assert_ok!(Creditcoin::::set_gate_faucet(RawOrigin::Root.into(), auth)); - - assert_ok!(Creditcoin::::register_address( - RuntimeOrigin::signed(acc.clone()), - CHAIN, - addr.clone(), - sign - )); - - let gate_contract = crate::BurnDetails::GATE(addr.clone(), TX_HASH.hex_to_address()); - - assert_ok!(Creditcoin::::request_collect_coins_v2( - RuntimeOrigin::signed(acc), - gate_contract - )); - - let deadline = Test::unverified_transfer_deadline(); - - roll_by_with_ocw(1); - - assert!(!pool.read().transactions.is_empty()); - - // The only important part of this test - let amount = RPC_RESPONSE_AMOUNT.as_u128(); - let amount = amount.saturating_div(2); - - let collected_coins = CollectedCoinsStruct { - to: AddressId::new::(&CHAIN, &addr[..]), - amount, - tx_id: TX_HASH.hex_to_address(), - contract_type: ContractType::GATE, - }; - let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); - - let call = crate::Call::::persist_task_output { - task_output: (collected_coins_id, collected_coins).into(), - deadline, - }; - - assert_matches!(pool.write().transactions.pop(), Some(tx) => { - let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); - }); - }); - } - - #[test] - fn request_collect_coins_v2_should_error_when_not_signed() { - ExtBuilder::default().build_and_execute(|| { - System::::set_block_number(1); - - let external_addr = ExternalAddress::default(); - let tx_id = ExternalTxId::default(); - let contract = BurnDetails::GATE(external_addr, tx_id); - - assert_noop!( - Creditcoin::::request_collect_coins_v2(RuntimeOrigin::none(), contract), - BadOrigin - ); - }); - } - - #[test] - fn request_collect_coins_v2_should_error_when_faucet_not_set() { - ExtBuilder::default().build_and_execute(|| { - System::::set_block_number(1); - - let who = AccountId::new([0; 32]); - let external_addr = ExternalAddress::default(); - let tx_id = ExternalTxId::default(); - let contract = BurnDetails::GATE(external_addr, tx_id); - - assert_noop!( - Creditcoin::::request_collect_coins_v2(RuntimeOrigin::signed(who), contract), - crate::Error::::BurnGATEFaucetNotSet - ); - }); - } -} +// use crate::ocw::{ +// self, +// errors::{VerificationFailureCause, VerificationResult}, +// rpc::{self, EthTransaction, EthTransactionReceipt}, +// OffchainResult, ETH_CONFIRMATIONS, +// }; +// use crate::pallet::{Config as CreditcoinConfig, Pallet}; +// use crate::{ +// types::{Blockchain, ContractType, UnverifiedCollectedCoins}, +// ExternalAddress, ExternalAmount, +// }; +// use core::default::Default; +// use ethabi::{Function, Param, ParamType, StateMutability, Token}; +// use ethereum_types::U64; +// use frame_support::{ensure, RuntimeDebug}; +// use hex_literal::hex; +// use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +// use scale_info::TypeInfo; +// use sp_core::H160; +// use sp_runtime::SaturatedConversion; +// #[cfg_attr(feature = "std", allow(unused_imports))] +// use sp_std::prelude::*; +// +// #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +// pub struct DeployedContract { +// pub address: sp_core::H160, +// pub chain: Blockchain, +// } +// +// impl DeployedContract { +// const DEFAULT_CHAIN: Blockchain = Blockchain::Ethereum; +// } +// +// impl Default for DeployedContract { +// fn default() -> Self { +// let contract_chain: Blockchain = DeployedContract::DEFAULT_CHAIN; +// let contract_address: H160 = +// sp_core::H160(hex!("a3EE21C306A700E682AbCdfe9BaA6A08F3820419")); +// Self { address: contract_address, chain: contract_chain } +// } +// } +// +// impl DeployedContract { +// ///exchange has been deprecated, use burn instead +// fn burn_vested_cc_abi() -> Function { +// #[allow(deprecated)] +// Function { +// name: "burn".into(), +// inputs: vec![Param { +// name: "value".into(), +// kind: ParamType::Uint(256), +// internal_type: None, +// }], +// outputs: vec![Param { +// name: "success".into(), +// kind: ParamType::Bool, +// internal_type: None, +// }], +// constant: Some(false), +// state_mutability: StateMutability::NonPayable, +// } +// } +// } +// +// pub fn validate_collect_coins( +// external_address: &ExternalAddress, +// receipt: &EthTransactionReceipt, +// transaction: &EthTransaction, +// eth_tip: U64, +// contract_address: &H160, +// ) -> OffchainResult { +// ensure!(receipt.is_success(), VerificationFailureCause::TaskFailed); +// +// let block_number = transaction.block_number.ok_or(VerificationFailureCause::TaskPending)?; +// +// let diff = (eth_tip) +// .checked_sub(block_number) +// .ok_or(VerificationFailureCause::TaskInFuture)?; +// ensure!(diff.as_u64() >= ETH_CONFIRMATIONS, VerificationFailureCause::TaskUnconfirmed); +// +// if let Some(recipient) = &transaction.recipient { +// ensure!(recipient == contract_address, VerificationFailureCause::IncorrectContract); +// } else { +// return Err(VerificationFailureCause::MissingReceiver.into()); +// } +// +// if let Some(sender) = &transaction.sender { +// ensure!(sender[..] == external_address[..], VerificationFailureCause::IncorrectSender) +// } else { +// return Err(VerificationFailureCause::MissingSender.into()); +// } +// +// let transfer_fn = DeployedContract::burn_vested_cc_abi(); +// ensure!(!transaction.is_input_empty(), VerificationFailureCause::EmptyInput); +// +// { +// let selector = transaction.selector(); +// if selector != transfer_fn.short_signature() { +// log::error!( +// "function selector mismatch, expected: {}, got: {}", +// hex::encode(transfer_fn.short_signature()), +// hex::encode(selector) +// ); +// return Err(VerificationFailureCause::AbiMismatch.into()); +// } +// } +// +// let inputs = transfer_fn.decode_input(transaction.input()).map_err(|e| { +// log::error!("failed to decode inputs: {:?}", e); +// VerificationFailureCause::AbiMismatch +// })?; +// +// match inputs.get(0) { +// Some(Token::Uint(value)) => Ok(ExternalAmount::from(value)), +// _ => Err(VerificationFailureCause::IncorrectInputType.into()), +// } +// } +// +// impl Pallet { +// ///Amount is saturated to u128, don't exchange more than u128::MAX at once. +// pub fn verify_collect_coins_ocw( +// u_cc: &UnverifiedCollectedCoins, +// ) -> VerificationResult { +// log::debug!("verifying OCW Collect Coins"); +// let UnverifiedCollectedCoins { +// to, +// tx_id, +// contract: DeployedContract { address, chain }, +// contract_type, +// } = u_cc; +// let rpc_url = &chain.rpc_url()?; +// let tx = ocw::eth_get_transaction(tx_id, rpc_url)?; +// let tx_receipt = rpc::eth_get_transaction_receipt(tx_id, rpc_url)?; +// let eth_tip = rpc::eth_get_block_number(rpc_url)?; +// +// let mut amount = validate_collect_coins(to, &tx_receipt, &tx, eth_tip, address)?; +// +// // GATE -> CTC is swapped 2:1 +// if *contract_type == ContractType::GATE { +// amount /= 2; +// } +// +// let amount = amount.saturated_into::().saturated_into::(); +// +// Ok(amount) +// } +// } +// +// #[cfg(any(test, feature = "runtime-benchmarks"))] +// pub(crate) mod testing_constants { +// use super::{Blockchain, DeployedContract}; +// +// pub const CHAIN: Blockchain = DeployedContract::DEFAULT_CHAIN; +// } +// +// #[cfg(test)] +// pub(crate) mod tests { +// +// use super::*; +// use crate::mock::{self, PendingRequestExt, RuntimeCall}; +// use crate::types::ContractType; +// use std::collections::HashMap; +// +// // txn.from has been overriden by 'generate_address_with_proof("collector")' +// static RESPONSES: Lazy>> = Lazy::new(|| { +// serde_json::from_slice(include_bytes!("../../tests/collectCoins.json")).unwrap() +// }); +// +// static BLOCK_NUMBER: Lazy = Lazy::new(|| { +// let responses = &*RESPONSES; +// let bn = +// responses["eth_getTransactionByHash"].result.clone().unwrap()["blockNumber"].clone(); +// serde_json::from_value(bn).unwrap() +// }); +// +// static BLOCK_NUMBER_STR: Lazy = Lazy::new(|| { +// let responses = &*RESPONSES; +// let bn = +// responses["eth_getTransactionByHash"].result.clone().unwrap()["blockNumber"].clone(); +// serde_json::from_value(bn).unwrap() +// }); +// +// static VESTING_CONTRACT: Lazy = Lazy::new(|| { +// let responses = &*RESPONSES; +// let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["to"].clone(); +// let val: String = serde_json::from_value(val).unwrap(); +// let vesting_contract = hex::decode(val.trim_start_matches("0x")).unwrap(); +// H160::from(<[u8; 20]>::try_from(vesting_contract.as_slice()).unwrap()) +// }); +// +// // txn.from has been overriden by 'generate_address_with_proof("collector")' +// static FROM: Lazy = Lazy::new(|| { +// let responses = &*RESPONSES; +// let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["from"].clone(); +// serde_json::from_value(val).unwrap() +// }); +// +// static INPUT: Lazy = Lazy::new(|| { +// let responses = &*RESPONSES; +// let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["input"].clone(); +// let val: String = serde_json::from_value(val).unwrap(); +// let input_bytes = hex::decode(val.trim_start_matches("0x")).unwrap(); +// input_bytes.into() +// }); +// +// pub(crate) static TX_HASH: Lazy = Lazy::new(|| { +// let responses = &*RESPONSES; +// let val = responses["eth_getTransactionByHash"].result.clone().unwrap()["hash"].clone(); +// serde_json::from_value(val).unwrap() +// }); +// +// pub(crate) static RPC_RESPONSE_AMOUNT: Lazy = Lazy::new(|| { +// let transfer_fn = DeployedContract::burn_vested_cc_abi(); +// +// let inputs = transfer_fn.decode_input(&(INPUT.0)[4..]).unwrap(); +// +// let amount = inputs.get(0).unwrap(); +// if let Token::Uint(value) = amount { +// ExternalAmount::from(value) +// } else { +// panic!("Not Token::Uint"); +// } +// }); +// +// use super::testing_constants::CHAIN; +// use crate::helpers::extensions::HexToAddress; +// use crate::helpers::non_paying_error; +// use crate::mock::{ +// roll_by_with_ocw, set_rpc_uri, AccountId, Balances, ExtBuilder, MockedRpcRequests, +// OffchainState, RuntimeOrigin, RwLock, TaskScheduler, Test, +// }; +// use crate::ocw::tasks::TaskV2; +// use crate::ocw::{ +// errors::{OffchainError, VerificationFailureCause as Cause}, +// rpc::{EthTransaction, EthTransactionReceipt}, +// ETH_CONFIRMATIONS, +// }; +// use crate::tests::generate_address_with_proof; +// use crate::types::{AddressId, CollectedCoinsId, CollectedCoinsStruct}; +// use crate::{ocw::rpc::JsonRpcResponse, BurnDetails, ExternalAddress, ExternalTxId}; +// use crate::{Pallet as Creditcoin, Task}; +// use alloc::sync::Arc; +// use assert_matches::assert_matches; +// use frame_support::dispatch::Dispatchable; +// use frame_support::{assert_noop, assert_ok, once_cell::sync::Lazy, traits::Currency}; +// use frame_system::Pallet as System; +// use frame_system::RawOrigin; +// use pallet_offchain_task_scheduler::tasks::TaskScheduler as TaskSchedulerT; +// use pallet_offchain_task_scheduler::Pallet as TaskSchedulerPallet; +// use parity_scale_codec::Decode; +// use sp_runtime::traits::{BadOrigin, IdentifyAccount}; +// use sp_runtime::{ArithmeticError, TokenError}; +// use std::convert::TryFrom; +// +// fn prepare_rpc_mocks() -> MockedRpcRequests { +// let dummy_url = "dummy"; +// let contract_chain = Creditcoin::::collect_coins_contract(); +// set_rpc_uri(&contract_chain.chain, dummy_url); +// +// MockedRpcRequests::new(dummy_url, &TX_HASH, &BLOCK_NUMBER_STR, &RESPONSES) +// } +// +// /// call from externalities context +// pub(crate) fn mock_rpc_for_collect_coins(state: &Arc>) { +// let mut rpcs = prepare_rpc_mocks(); +// rpcs.mock_get_block_number(&mut state.write()); +// } +// +// struct PassingCollectCoins { +// to: ExternalAddress, +// receipt: EthTransactionReceipt, +// transaction: EthTransaction, +// eth_tip: U64, +// contract_address: H160, +// } +// +// impl Default for PassingCollectCoins { +// fn default() -> Self { +// let base_height = *BLOCK_NUMBER; +// let vesting_contract = *VESTING_CONTRACT; +// let to = FROM.hex_to_address(); +// let tx_from = H160::from(<[u8; 20]>::try_from(to.as_slice()).unwrap()); +// +// let mut transaction = EthTransaction::default(); +// transaction.block_number = Some(base_height); +// transaction.sender = Some(tx_from); +// transaction.recipient = Some(vesting_contract); +// transaction.set_input(&INPUT.0); +// +// Self { +// to, +// receipt: EthTransactionReceipt { status: Some(1u64.into()), ..Default::default() }, +// transaction, +// eth_tip: (base_height + ETH_CONFIRMATIONS), +// contract_address: DeployedContract::default().address, +// } +// } +// } +// +// impl PassingCollectCoins { +// fn validate(self) -> OffchainResult { +// let PassingCollectCoins { to, receipt, transaction, eth_tip, contract_address } = self; +// super::validate_collect_coins(&to, &receipt, &transaction, eth_tip, &contract_address) +// } +// } +// +// fn assert_invalid(res: OffchainResult, cause: VerificationFailureCause) { +// assert_matches!(res, Err(OffchainError::InvalidTask(c)) =>{ assert_eq!(c,cause); }); +// } +// +// #[test] +// fn valid() { +// assert_matches!(PassingCollectCoins::default().validate(), Ok(_)); +// } +// +// #[test] +// fn txn_success() { +// let mut pcc = PassingCollectCoins::default(); +// pcc.receipt.status = Some(0u64.into()); +// assert_invalid(pcc.validate(), Cause::TaskFailed); +// } +// +// #[test] +// fn pending() { +// let mut transaction = EthTransaction::default(); +// transaction.block_number = None; +// let pcc = PassingCollectCoins { transaction, ..Default::default() }; +// assert_invalid(pcc.validate(), Cause::TaskPending); +// } +// +// #[test] +// fn in_the_future() { +// let pcc = PassingCollectCoins { eth_tip: 0u64.into(), ..Default::default() }; +// assert_invalid(pcc.validate(), Cause::TaskInFuture); +// } +// +// #[test] +// fn unconfirmed() { +// let mut pcc = PassingCollectCoins::default(); +// pcc.eth_tip = pcc.transaction.block_number.unwrap(); +// assert_invalid(pcc.validate(), Cause::TaskUnconfirmed); +// } +// +// #[test] +// fn missing_receiver() { +// let mut pcc = PassingCollectCoins::default(); +// pcc.transaction.recipient = None; +// assert_invalid(pcc.validate(), Cause::MissingReceiver); +// } +// +// #[test] +// fn incorrect_contract() { +// let mut pcc = PassingCollectCoins::default(); +// let address = [0u8; 20]; +// let address = H160::from(<[u8; 20]>::try_from(address.as_slice()).unwrap()); +// pcc.transaction.recipient = Some(address); +// assert_invalid(pcc.validate(), Cause::IncorrectContract); +// } +// +// #[test] +// fn missing_sender() { +// let mut pcc = PassingCollectCoins::default(); +// pcc.transaction.sender = None; +// assert_invalid(pcc.validate(), Cause::MissingSender); +// } +// +// #[test] +// fn incorrect_sender() { +// let mut pcc = PassingCollectCoins::default(); +// let address = [0u8; 20]; +// let address = H160::from(<[u8; 20]>::try_from(address.as_slice()).unwrap()); +// pcc.transaction.sender = Some(address); +// assert_invalid(pcc.validate(), Cause::IncorrectSender); +// } +// +// #[test] +// fn empty_input() { +// let mut pcc = PassingCollectCoins::default(); +// pcc.transaction.set_input(b""); +// assert_invalid(pcc.validate(), Cause::EmptyInput); +// } +// +// #[test] +// fn amount_set() -> OffchainResult<()> { +// let pcc = PassingCollectCoins::default(); +// let PassingCollectCoins { to, receipt, transaction, eth_tip, contract_address } = pcc; +// let amount = +// super::validate_collect_coins(&to, &receipt, &transaction, eth_tip, &contract_address)?; +// assert_eq!(amount, *RPC_RESPONSE_AMOUNT); +// Ok(()) +// } +// +// #[test] +// fn fail_collect_coins_should_error_when_not_signed() { +// let ext = ExtBuilder::default(); +// let expected_collected_coins_id = +// crate::CollectedCoinsId::new::(&CHAIN, &[0]); +// +// ext.build_offchain_and_execute_with_state(|_state, _pool| { +// assert_noop!( +// Creditcoin::::fail_task( +// RuntimeOrigin::none(), +// Test::unverified_transfer_deadline(), +// expected_collected_coins_id.clone().into(), +// Cause::AbiMismatch, +// ), +// BadOrigin +// ); +// }); +// } +// +// #[test] +// fn fail_collect_coins_should_error_when_no_authority() { +// let ext = ExtBuilder::default(); +// let (molly, _, _, _) = generate_address_with_proof("malicious"); +// let expected_collected_coins_id = +// crate::CollectedCoinsId::new::(&CHAIN, &[0]); +// +// ext.build_offchain_and_execute_with_state(|_state, _pool| { +// assert_noop!( +// Creditcoin::::fail_task( +// RuntimeOrigin::signed(molly), +// Test::unverified_transfer_deadline(), +// expected_collected_coins_id.clone().into(), +// Cause::AbiMismatch, +// ), +// crate::Error::::InsufficientAuthority +// ); +// }); +// } +// +// #[test] +// fn fail_collect_coins_should_fail_when_transfer_has_already_been_registered() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// +// ext.build_offchain_and_execute_with_state(|_state, _pool| { +// System::::set_block_number(1); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc), +// CHAIN, +// addr, +// sign +// )); +// +// let deadline = Test::unverified_transfer_deadline(); +// +// let pcc = PassingCollectCoins::default(); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &pcc.to[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = +// crate::CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// assert_ok!(Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth.clone()), +// deadline, +// (collected_coins_id.clone(), collected_coins).into(), +// )); +// +// assert_noop!( +// Creditcoin::::fail_task( +// RuntimeOrigin::signed(auth), +// Test::unverified_transfer_deadline(), +// collected_coins_id.into(), +// Cause::AbiMismatch, +// ), +// crate::Error::::CollectCoinsAlreadyRegistered +// ); +// }); +// } +// +// #[test] +// fn fail_collect_coins_emits_events() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// let expected_collected_coins_id = +// crate::CollectedCoinsId::new::(&CHAIN, &[0]); +// +// ext.build_offchain_and_execute_with_state(|_state, _pool| { +// System::::set_block_number(1); +// +// assert_ok!(Creditcoin::::fail_task( +// RuntimeOrigin::signed(auth), +// Test::unverified_transfer_deadline(), +// expected_collected_coins_id.clone().into(), +// Cause::AbiMismatch, +// )); +// +// let event = System::::events().pop().expect("an event").event; +// assert_matches!( +// event, +// crate::mock::RuntimeEvent::Creditcoin(crate::Event::::CollectCoinsFailedVerification(collected_coins_id, cause)) => { +// assert_eq!(collected_coins_id, expected_collected_coins_id); +// assert_eq!(cause, Cause::AbiMismatch); +// } +// ); +// }); +// } +// +// #[test] +// fn ocw_fail_collect_coins_works() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let expected_collected_coins_id = +// crate::CollectedCoinsId::new::(&CHAIN, &[0]); +// ext.build_offchain_and_execute_with_state(|_state, pool| { +// crate::mock::roll_to(1); +// let call = crate::Call::::fail_task { +// task_id: expected_collected_coins_id.into(), +// cause: Cause::AbiMismatch, +// deadline: Test::unverified_transfer_deadline(), +// }; +// assert_ok!(TaskSchedulerPallet::::offchain_signed_tx(acct_pubkey.into(), |_| { +// call.clone().into() +// },)); +// crate::mock::roll_to(2); +// +// assert_matches!(pool.write().transactions.pop(), Some(tx) => { +// let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); +// assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); +// }); +// }); +// } +// +// #[test] +// fn persist_collect_coins() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _| { +// System::::set_block_number(1); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr, +// sign +// )); +// +// let deadline = Test::unverified_transfer_deadline(); +// +// let pcc = PassingCollectCoins::default(); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &pcc.to[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// let balance = >::total_balance; +// +// let pre_authority_balance = balance(&auth); +// let pre_collector_balance = balance(&acc); +// +// assert_ok!(Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth.clone()), +// deadline, +// (collected_coins_id.clone(), collected_coins.clone()).into(), +// )); +// +// let event = >::events().pop().expect("an event").event; +// +// assert_matches!( +// event, +// crate::mock::RuntimeEvent::Creditcoin(crate::Event::::CollectedCoinsMinted(id, item)) => { +// assert_eq!(id, collected_coins_id); +// assert_eq!(item, collected_coins); +// } +// ); +// //do not mint into authority +// assert_eq!(pre_authority_balance, balance(&auth)); +// // assert on deposit +// assert_eq!(pre_collector_balance.saturating_add(collected_coins.amount), balance(&acc)); +// }); +// } +// +// #[test] +// fn persist_unregistered_address() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let pcc = PassingCollectCoins::default(); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &pcc.to[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// let deadline = Test::unverified_transfer_deadline(); +// +// assert_noop!( +// Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth), +// deadline, +// (collected_coins_id, collected_coins).into(), +// ), +// crate::Error::::NonExistentAddress +// ); +// }); +// } +// +// #[test] +// fn persist_more_than_max_balance_should_error() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc), +// CHAIN, +// addr, +// sign +// )); +// +// let pcc = PassingCollectCoins::default(); +// +// // lower free balance so that collect coins would overflow +// let cash = >::minimum_balance(); +// >::make_free_balance_be(&auth, cash); +// +// let collected_coins_id = +// crate::CollectedCoinsId::new::(&CHAIN, &TX_HASH.hex_to_address()); +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &pcc.to[..]), +// amount: u128::MAX, +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// +// assert_noop!( +// Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth), +// Test::unverified_transfer_deadline(), +// (collected_coins_id, collected_coins).into(), +// ), +// ArithmeticError::Overflow +// ); +// }); +// } +// +// #[test] +// fn request_collect_coins_should_fail_when_not_signed() { +// let ext = ExtBuilder::default(); +// ext.build_offchain_and_execute_with_state(|_, _pool| { +// let (_, addr, _, _) = generate_address_with_proof("collector"); +// +// assert_noop!( +// Creditcoin::::request_collect_coins( +// RuntimeOrigin::none(), +// addr, +// TX_HASH.hex_to_address(), +// ), +// BadOrigin +// ); +// }); +// } +// +// #[test] +// fn request_persisted_not_reentrant() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _pool| { +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// assert_ok!(Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth), +// Test::unverified_transfer_deadline(), +// (collected_coins_id, collected_coins).into(), +// )); +// +// roll_by_with_ocw(1); +// +// assert_noop!( +// Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(acc.clone()), +// addr.clone(), +// TX_HASH.hex_to_address(), +// ), +// crate::Error::::CollectCoinsAlreadyRegistered +// ); +// +// // trying the same with v2 +// let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); +// assert_noop!( +// Creditcoin::::request_collect_coins_v2(RuntimeOrigin::signed(acc), contract,), +// crate::Error::::CollectCoinsAlreadyRegistered +// ); +// }); +// } +// +// #[test] +// fn request_pending_not_reentrant() { +// let mut ext = ExtBuilder::default(); +// ext.generate_authority(); +// ext.build_offchain_and_execute_with_state(|_, _| { +// System::::set_block_number(1); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// assert_ok!(Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(acc.clone()), +// addr.clone(), +// TX_HASH.hex_to_address() +// )); +// +// let collected_coins_id = +// CollectedCoinsId::new::(&CHAIN, TX_HASH.hex_to_address().as_slice()); +// +// let event = >::events().pop().expect("an event").event; +// assert_matches!( +// event, +// crate::mock::RuntimeEvent::Creditcoin(crate::Event::::CollectCoinsRegistered(collect_coins_id, pending)) => { +// assert_eq!(collect_coins_id, collected_coins_id); +// +// let id = TaskV2::::to_id(&pending); +// assert!( TaskScheduler::is_scheduled( &Test::unverified_transfer_deadline(), &id)); +// +// let UnverifiedCollectedCoins { to, tx_id, .. } = pending; +// assert_eq!(to, addr); +// assert_eq!(tx_id, TX_HASH.hex_to_address()); +// } +// ); +// +// assert_noop!( +// Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(acc.clone()), +// addr.clone(), +// TX_HASH.hex_to_address(), +// ), +// crate::Error::::CollectCoinsAlreadyRegistered +// ); +// +// assert!(Creditcoin::::collected_coins(collected_coins_id).is_none()); +// +// // trying the same with v2 +// let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); +// assert_noop!( +// Creditcoin::::request_collect_coins_v2( +// RuntimeOrigin::signed(acc), +// contract, +// ), +// crate::Error::::CollectCoinsAlreadyRegistered +// ); +// }); +// } +// +// #[test] +// fn request_address_not_registered() { +// let ext = ExtBuilder::default(); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (acc, addr, _, _) = generate_address_with_proof("collector"); +// +// assert_noop!( +// Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(acc.clone()), +// addr.clone(), +// TX_HASH.hex_to_address(), +// ), +// crate::Error::::NonExistentAddress +// ); +// +// // trying the same with v2 +// let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); +// assert_noop!( +// Creditcoin::::request_collect_coins_v2(RuntimeOrigin::signed(acc), contract,), +// crate::Error::::NonExistentAddress +// ); +// }); +// } +// +// #[test] +// fn request_not_owner() { +// let ext = ExtBuilder::default(); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// let (molly, _, _, _) = generate_address_with_proof("malicious"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// assert_noop!( +// Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(molly.clone()), +// addr.clone(), +// TX_HASH.hex_to_address(), +// ), +// crate::Error::::NotAddressOwner +// ); +// +// // trying the same with v2 +// let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); +// assert_noop!( +// Creditcoin::::request_collect_coins_v2( +// RuntimeOrigin::signed(molly), +// contract, +// ), +// crate::Error::::NotAddressOwner +// ); +// }); +// } +// +// #[test] +// fn persist_not_authority() { +// let ext = ExtBuilder::default(); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (molly, addr, _, _) = generate_address_with_proof("malicious"); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// assert_noop!( +// Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(molly), +// Test::unverified_transfer_deadline(), +// (collected_coins_id, collected_coins).into(), +// ), +// crate::Error::::InsufficientAuthority +// ); +// }); +// } +// +// #[test] +// fn persist_is_submitted() { +// let mut ext = ExtBuilder::default(); +// ext.generate_authority(); +// ext.build_offchain_and_execute_with_state(|state, pool| { +// mock_rpc_for_collect_coins(&state); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// assert_ok!(Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(acc), +// addr.clone(), +// TX_HASH.hex_to_address() +// )); +// +// let deadline = Test::unverified_transfer_deadline(); +// +// roll_by_with_ocw(1); +// +// assert!(!pool.read().transactions.is_empty()); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// let call = crate::Call::::persist_task_output { +// task_output: (collected_coins_id, collected_coins).into(), +// deadline, +// }; +// +// assert_matches!(pool.write().transactions.pop(), Some(tx) => { +// let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); +// assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); +// }); +// }); +// } +// +// #[test] +// fn request_collect_coins_v2_will_submit_a_persist_task_output_call() { +// let mut ext = ExtBuilder::default(); +// ext.generate_authority(); +// ext.build_offchain_and_execute_with_state(|state, pool| { +// mock_rpc_for_collect_coins(&state); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// let contract = BurnDetails::GCRE(addr.clone(), TX_HASH.hex_to_address()); +// assert_ok!(Creditcoin::::request_collect_coins_v2( +// RuntimeOrigin::signed(acc), +// contract, +// ),); +// +// let deadline = Test::unverified_transfer_deadline(); +// +// roll_by_with_ocw(1); +// +// assert!(!pool.read().transactions.is_empty()); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// let call = crate::Call::::persist_task_output { +// task_output: (collected_coins_id, collected_coins).into(), +// deadline, +// }; +// +// assert_matches!(pool.write().transactions.pop(), Some(tx) => { +// let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); +// assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); +// }); +// }); +// } +// +// #[test] +// fn persist_not_reentrant() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// assert_ok!(Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth.clone()), +// Test::unverified_transfer_deadline(), +// (collected_coins_id.clone(), collected_coins.clone()).into(), +// )); +// +// assert_noop!( +// Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth), +// Test::unverified_transfer_deadline(), +// (collected_coins_id, collected_coins).into(), +// ), +// non_paying_error(crate::Error::::CollectCoinsAlreadyRegistered) +// ); +// }); +// } +// +// #[test] +// fn unverified_collect_coins_are_removed() { +// let mut ext = ExtBuilder::default(); +// ext.generate_authority(); +// ext.build_offchain_and_execute_with_state(|state, _| { +// mock_rpc_for_collect_coins(&state); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// assert_ok!(Creditcoin::::request_collect_coins( +// RuntimeOrigin::signed(acc.clone()), +// addr.clone(), +// TX_HASH.hex_to_address() +// )); +// let deadline = Test::unverified_transfer_deadline(); +// +// roll_by_with_ocw(deadline); +// +// let collected_coins_id = +// CollectedCoinsId::new::(&CHAIN, TX_HASH.hex_to_address().as_slice()) +// .into_inner(); +// +// roll_by_with_ocw(1); +// +// assert!(!TaskScheduler::is_scheduled(&deadline, &collected_coins_id)); +// +// // trying the same with v2 +// let contract = BurnDetails::GCRE(addr, TX_HASH.hex_to_address()); +// assert_ok!(Creditcoin::::request_collect_coins_v2( +// RuntimeOrigin::signed(acc), +// contract, +// ),); +// +// roll_by_with_ocw(deadline); +// let collected_coins_id = +// CollectedCoinsId::new::(&CHAIN, TX_HASH.hex_to_address().as_slice()) +// .into_inner(); +// +// roll_by_with_ocw(1); +// assert!(!TaskScheduler::is_scheduled(&deadline, &collected_coins_id)); +// }); +// } +// +// #[test] +// fn owner_credited() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: RPC_RESPONSE_AMOUNT.as_u128(), +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr, +// sign +// )); +// +// assert_ok!(Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth.clone()), +// Test::unverified_transfer_deadline(), +// (collected_coins_id, collected_coins.clone()).into(), +// )); +// +// assert_eq!( +// frame_system::pallet::Account::::get(&acc).data.free, +// collected_coins.amount +// ); +// }); +// } +// +// #[test] +// fn selector_mismatch() { +// let ext = ExtBuilder::default(); +// ext.build_offchain_and_execute_with_state(|state, _| { +// mock_rpc_for_collect_coins(&state); +// +// let (_, to, ..) = generate_address_with_proof("collector"); +// let tx_id = &TX_HASH.hex_to_address(); +// +// let rpc_url = &CHAIN.rpc_url().unwrap(); +// let mut tx = rpc::eth_get_transaction(tx_id, rpc_url).unwrap(); +// let tx_receipt = rpc::eth_get_transaction_receipt(tx_id, rpc_url).unwrap(); +// let eth_tip = rpc::eth_get_block_number(rpc_url).unwrap(); +// let PassingCollectCoins { contract_address, .. } = Default::default(); +// validate_collect_coins(&to, &tx_receipt, &tx, eth_tip, &contract_address) +// .expect("valid"); +// // Forged selector +// tx.set_input(b"ffffffff"); +// assert_matches!( +// validate_collect_coins(&to, &tx_receipt, &tx, eth_tip, &contract_address), +// Err(OffchainError::InvalidTask(VerificationFailureCause::AbiMismatch)) +// ); +// }); +// } +// +// #[test] +// fn set_collect_coins_only_as_root() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let _auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_and_execute(|| { +// let contract = DeployedContract { +// address: sp_core::H160(hex!("aaaaabbbbbcccccdddddeeeeefffff08F3820419")), +// chain: Blockchain::Rinkeby, +// }; +// assert_ok!(Creditcoin::::set_collect_coins_contract( +// RawOrigin::Root.into(), +// contract.clone() +// )); +// let from_storage = Creditcoin::::collect_coins_contract(); +// assert_eq!(contract, from_storage); +// assert_ne!(from_storage, DeployedContract::default()); +// +// let (acc, ..) = generate_address_with_proof("somebody"); +// +// assert_noop!( +// Creditcoin::::set_collect_coins_contract( +// RawOrigin::Signed(acc).into(), +// contract.clone() +// ), +// BadOrigin +// ); +// +// assert_noop!( +// Creditcoin::::set_collect_coins_contract(RawOrigin::None.into(), contract), +// BadOrigin +// ); +// }); +// } +// +// #[test] +// fn gcrecontract_value_query_is_default() { +// let contract = DeployedContract::default(); +// let ext = ExtBuilder::default(); +// ext.build_and_execute(|| { +// let value_query = Creditcoin::::collect_coins_contract(); +// assert_eq!(contract, value_query); +// }); +// } +// +// #[test] +// fn persist_minimum_existential_deposit_errors() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|_, _| { +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount: 1u128, +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GCRE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr, +// sign +// )); +// +// assert_eq!(Balances::total_balance(&acc), 0); +// +// assert_noop!( +// Creditcoin::::persist_task_output( +// RuntimeOrigin::signed(auth.clone()), +// Test::unverified_transfer_deadline(), +// (collected_coins_id, collected_coins).into(), +// ), +// TokenError::BelowMinimum +// ); +// }); +// } +// +// #[test] +// fn transaction_not_found() { +// ExtBuilder::default().build_offchain_and_execute_with_state(|state, _| { +// let mut rpcs = prepare_rpc_mocks(); +// rpcs.get_transaction.set_empty_response(); +// rpcs.mock_get_transaction(&mut state.write()); +// +// let (_, addr, _, _) = generate_address_with_proof("collector"); +// let cc = UnverifiedCollectedCoins { +// to: addr, +// tx_id: TX_HASH.hex_to_address(), +// contract: DeployedContract::default(), +// contract_type: ContractType::GCRE, +// }; +// assert_matches!( +// Creditcoin::::verify_collect_coins_ocw(&cc), +// Err(OffchainError::InvalidTask(VerificationFailureCause::TransactionNotFound)) +// ); +// }); +// } +// +// #[test] +// fn unverified_collect_coins_is_removed_after_failing_the_task() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|state, _| { +// let mut rpcs = prepare_rpc_mocks(); +// rpcs.get_transaction.set_empty_response(); +// rpcs.mock_get_transaction(&mut state.write()); +// +// let (_, addr, _, _) = generate_address_with_proof("collector"); +// +// let cc = UnverifiedCollectedCoins { +// to: addr, +// tx_id: TX_HASH.hex_to_address(), +// contract: DeployedContract::default(), +// contract_type: ContractType::GCRE, +// }; +// +// let id = TaskV2::::to_id(&cc); +// let deadline = TaskScheduler::deadline(); +// +// TaskScheduler::insert(&deadline, &id, Task::CollectCoins(cc.clone())); +// +// let call = +// TaskV2::::persistence_call(&cc, TaskScheduler::deadline(), &id).unwrap(); +// assert!(matches!(call, crate::Call::fail_task { .. })); +// let c = RuntimeCall::from(call); +// +// assert_ok!(c.dispatch(RuntimeOrigin::signed(auth))); +// assert!(!TaskScheduler::is_scheduled(&TaskScheduler::deadline(), &id)); +// }); +// } +// +// #[test] +// fn unverified_collect_coins_is_removed_after_persisting_the_task() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|state, _| { +// mock_rpc_for_collect_coins(&state); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// let cc = UnverifiedCollectedCoins { +// to: addr, +// tx_id: TX_HASH.hex_to_address(), +// contract: DeployedContract::default(), +// contract_type: ContractType::GCRE, +// }; +// +// let id = TaskV2::::to_id(&cc); +// let deadline = TaskScheduler::deadline(); +// +// TaskScheduler::insert(&deadline, &id, Task::CollectCoins(cc.clone())); +// +// let call = +// TaskV2::::persistence_call(&cc, TaskScheduler::deadline(), &id).unwrap(); +// assert!(matches!(call, crate::Call::persist_task_output { .. })); +// let c = RuntimeCall::from(call); +// +// assert_ok!(c.dispatch(RuntimeOrigin::signed(auth))); +// assert!(!TaskScheduler::is_scheduled(&TaskScheduler::deadline(), &id)); +// }); +// } +// +// #[test] +// fn collect_coins_v2_gate_token_persist_is_submitted_and_amount_is_2_to_1() { +// let mut ext = ExtBuilder::default(); +// let acct_pubkey = ext.generate_authority(); +// let auth = AccountId::from(acct_pubkey.into_account().0); +// ext.build_offchain_and_execute_with_state(|state, pool| { +// mock_rpc_for_collect_coins(&state); +// +// let (acc, addr, sign, _) = generate_address_with_proof("collector"); +// +// let _ = as Currency< +// ::AccountId, +// >>::deposit_creating(&auth, 1000000000000000000); // I hope this is enough +// +// assert_ok!(Creditcoin::::set_gate_faucet(RawOrigin::Root.into(), auth)); +// +// assert_ok!(Creditcoin::::register_address( +// RuntimeOrigin::signed(acc.clone()), +// CHAIN, +// addr.clone(), +// sign +// )); +// +// let gate_contract = crate::BurnDetails::GATE(addr.clone(), TX_HASH.hex_to_address()); +// +// assert_ok!(Creditcoin::::request_collect_coins_v2( +// RuntimeOrigin::signed(acc), +// gate_contract +// )); +// +// let deadline = Test::unverified_transfer_deadline(); +// +// roll_by_with_ocw(1); +// +// assert!(!pool.read().transactions.is_empty()); +// +// // The only important part of this test +// let amount = RPC_RESPONSE_AMOUNT.as_u128(); +// let amount = amount.saturating_div(2); +// +// let collected_coins = CollectedCoinsStruct { +// to: AddressId::new::(&CHAIN, &addr[..]), +// amount, +// tx_id: TX_HASH.hex_to_address(), +// contract_type: ContractType::GATE, +// }; +// let collected_coins_id = CollectedCoinsId::new::(&CHAIN, &collected_coins.tx_id); +// +// let call = crate::Call::::persist_task_output { +// task_output: (collected_coins_id, collected_coins).into(), +// deadline, +// }; +// +// assert_matches!(pool.write().transactions.pop(), Some(tx) => { +// let tx = crate::mock::Extrinsic::decode(&mut &*tx).unwrap(); +// assert_eq!(tx.call, crate::mock::RuntimeCall::Creditcoin(call)); +// }); +// }); +// } +// +// #[test] +// fn request_collect_coins_v2_should_error_when_not_signed() { +// ExtBuilder::default().build_and_execute(|| { +// System::::set_block_number(1); +// +// let external_addr = ExternalAddress::default(); +// let tx_id = ExternalTxId::default(); +// let contract = BurnDetails::GATE(external_addr, tx_id); +// +// assert_noop!( +// Creditcoin::::request_collect_coins_v2(RuntimeOrigin::none(), contract), +// BadOrigin +// ); +// }); +// } +// +// #[test] +// fn request_collect_coins_v2_should_error_when_faucet_not_set() { +// ExtBuilder::default().build_and_execute(|| { +// System::::set_block_number(1); +// +// let who = AccountId::new([0; 32]); +// let external_addr = ExternalAddress::default(); +// let tx_id = ExternalTxId::default(); +// let contract = BurnDetails::GATE(external_addr, tx_id); +// +// assert_noop!( +// Creditcoin::::request_collect_coins_v2(RuntimeOrigin::signed(who), contract), +// crate::Error::::BurnGATEFaucetNotSet +// ); +// }); +// } +// } diff --git a/pallets/creditcoin/src/ocw/tasks/verify_transfer.rs b/pallets/creditcoin/src/ocw/tasks/verify_transfer.rs index 0b3c23c62f..c33a3739de 100644 --- a/pallets/creditcoin/src/ocw/tasks/verify_transfer.rs +++ b/pallets/creditcoin/src/ocw/tasks/verify_transfer.rs @@ -1,9 +1,14 @@ use ethabi::{Function, Param, ParamType, StateMutability, Token}; use ethereum_types::U64; -use frame_support::ensure; +use frame_support::{ensure, RuntimeDebug}; use frame_system::pallet_prelude::BlockNumberFor; use sp_core::U256; use sp_runtime::traits::UniqueSaturatedFrom; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::H160; +use hex_literal::hex; + #[cfg(not(feature = "std"))] use sp_std::prelude::*; @@ -17,6 +22,47 @@ use crate::{ TransferKind, UnverifiedTransfer, }; +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct DeployedContract { + pub address: sp_core::H160, + pub chain: Blockchain, +} + +impl DeployedContract { + const DEFAULT_CHAIN: Blockchain = Blockchain::Ethereum; +} + +impl Default for DeployedContract { + fn default() -> Self { + let contract_chain: Blockchain = DeployedContract::DEFAULT_CHAIN; + let contract_address: H160 = + sp_core::H160(hex!("a3EE21C306A700E682AbCdfe9BaA6A08F3820419")); + Self { address: contract_address, chain: contract_chain } + } +} + +impl DeployedContract { + ///exchange has been deprecated, use burn instead + fn burn_vested_cc_abi() -> Function { + #[allow(deprecated)] + Function { + name: "burn".into(), + inputs: vec![Param { + name: "value".into(), + kind: ParamType::Uint(256), + internal_type: None, + }], + outputs: vec![Param { + name: "success".into(), + kind: ParamType::Bool, + internal_type: None, + }], + constant: Some(false), + state_mutability: StateMutability::NonPayable, + } + } +} + pub(crate) fn ethless_transfer_function_abi() -> Function { #[allow(deprecated)] Function { diff --git a/pallets/creditcoin/src/types.rs b/pallets/creditcoin/src/types.rs index 1019347f6f..87433448ea 100644 --- a/pallets/creditcoin/src/types.rs +++ b/pallets/creditcoin/src/types.rs @@ -1,18 +1,18 @@ mod cleanup; -mod collect_coins; +//mod collect_coins; pub mod loan_terms; mod transfer; pub use cleanup::{StorageCleanupState, StorageItemCleanupState}; -pub use collect_coins::{ - CollectedCoins as CollectedCoinsStruct, CollectedCoinsId, UnverifiedCollectedCoins, -}; +// pub use collect_coins::{ +// CollectedCoins as CollectedCoinsStruct, CollectedCoinsId, UnverifiedCollectedCoins, +// }; pub use loan_terms::*; pub use transfer::*; -pub use collect_coins::{BurnDetails, ContractType}; +//pub use collect_coins::{BurnDetails, ContractType}; -use crate::ocw::tasks::collect_coins::DeployedContract; +//use crate::ocw::tasks::collect_coins::DeployedContract; use crate::ocw::VerificationFailureCause; use crate::ocw::VerificationResult; use extend::ext; @@ -483,7 +483,7 @@ impl<'de> serde::Deserialize<'de> for LegacySighash { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum Task { VerifyTransfer(UnverifiedTransfer), - CollectCoins(UnverifiedCollectedCoins), + //CollectCoins(UnverifiedCollectedCoins), } #[cfg(feature = "runtime-benchmarks")] @@ -511,18 +511,18 @@ impl From From - for Task -{ - fn from(coins: UnverifiedCollectedCoins) -> Self { - Task::CollectCoins(coins) - } -} +// impl From +// for Task +// { +// fn from(coins: UnverifiedCollectedCoins) -> Self { +// Task::CollectCoins(coins) +// } +// } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum TaskId { VerifyTransfer(TransferId), - CollectCoins(CollectedCoinsId), + //CollectCoins(CollectedCoinsId), } impl From> for TaskId { @@ -531,21 +531,21 @@ impl From> for TaskId { } } -impl From> for TaskId { - fn from(id: CollectedCoinsId) -> Self { - TaskId::CollectCoins(id) - } -} +// impl From> for TaskId { +// fn from(id: CollectedCoinsId) -> Self { +// TaskId::CollectCoins(id) +// } +// } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub enum TaskOutput { +pub enum TaskOutput { VerifyTransfer(TransferId, Transfer), - CollectCoins(CollectedCoinsId, CollectedCoinsStruct), + //CollectCoins(CollectedCoinsId, CollectedCoinsStruct), } -impl +impl From<(TransferId, Transfer)> - for TaskOutput + for TaskOutput { fn from( (id, transfer): (TransferId, Transfer), @@ -554,14 +554,14 @@ impl } } -impl - From<(CollectedCoinsId, CollectedCoinsStruct)> - for TaskOutput -{ - fn from((id, coins): (CollectedCoinsId, CollectedCoinsStruct)) -> Self { - Self::CollectCoins(id, coins) - } -} +// impl +// From<(CollectedCoinsId, CollectedCoinsStruct)> +// for TaskOutput +// { +// fn from((id, coins): (CollectedCoinsId, CollectedCoinsStruct)) -> Self { +// Self::CollectCoins(id, coins) +// } +// } #[cfg(test)] pub(crate) mod test { diff --git a/pallets/creditcoin/src/types/collect_coins.rs b/pallets/creditcoin/src/types/collect_coins.rs index 9339c73b62..14eb3c3a43 100644 --- a/pallets/creditcoin/src/types/collect_coins.rs +++ b/pallets/creditcoin/src/types/collect_coins.rs @@ -1,144 +1,144 @@ -use crate::types::{ - AddressId, Blockchain, DeployedContract, ExternalAddress, ExternalTxId, SystemConfig, -}; -use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct CollectedCoins { - pub to: AddressId, - pub amount: Balance, - pub tx_id: ExternalTxId, - pub contract_type: ContractType, -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct UnverifiedCollectedCoins { - pub to: ExternalAddress, - pub tx_id: ExternalTxId, - pub contract: DeployedContract, - pub contract_type: ContractType, -} - -impl UnverifiedCollectedCoins { - pub fn into_output(self, amount: T::Balance) -> CollectedCoins - where - T: Config, - { - let Self { to, tx_id, contract: DeployedContract { chain, .. }, contract_type } = self; - let to = crate::AddressId::new::(&chain, to.as_slice()); - CollectedCoins { amount, to, tx_id, contract_type } - } -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct CollectedCoinsId(Hash); - -impl CollectedCoinsId { - pub fn inner_hash(blockchain: &Blockchain, blockchain_tx_id: &[u8]) -> H - where - Hasher: Hash, - { - let key = concatenate!(blockchain.as_bytes(), blockchain_tx_id); - ::hash(&key) - } - - pub fn new( - contract_chain: &Blockchain, - blockchain_tx_id: &[u8], - ) -> CollectedCoinsId - where - ::Hashing: Hash, - { - let hash = Self::inner_hash::(contract_chain, blockchain_tx_id); - CollectedCoinsId(hash) - } - - pub fn into_inner(self) -> H { - self.0 - } -} - -impl From for CollectedCoinsId { - fn from(hash: H) -> Self { - Self(hash) - } -} - -use crate::ocw::errors::SchedulerError; -use crate::ocw::tasks::OffchainVerification; -use crate::ocw::VerificationFailureCause; -use crate::ocw::VerificationResult; -use crate::types::concatenate; -use crate::Config; -use crate::TaskOutput; -use pallet_offchain_task_scheduler::tasks::error::TaskError; -use pallet_offchain_task_scheduler::tasks::TaskV2; -use sp_runtime::traits::Hash; - -impl OffchainVerification for UnverifiedCollectedCoins { - type Output = T::Balance; - - fn verify(&self) -> VerificationResult { - crate::Pallet::::verify_collect_coins_ocw(self) - } -} - -impl TaskV2 for UnverifiedCollectedCoins -where - UnverifiedCollectedCoins: OffchainVerification, - >::Output: Into, -{ - type Call = crate::pallet::Call; - type EvaluationError = VerificationFailureCause; - type SchedulerError = SchedulerError; - fn to_id(&self) -> T::Hash { - CollectedCoinsId::inner_hash::(&self.contract.chain, self.tx_id.as_slice()) - } - - fn persistence_call( - &self, - deadline: T::BlockNumber, - id: &T::Hash, - ) -> Result> { - use crate::ocw::OffchainError::*; - match self.verify() { - Ok(amount) => { - let coins = self.clone().into_output::(amount.into()); - let id = CollectedCoinsId::from(*id); - Ok(Self::Call::persist_task_output { - deadline, - task_output: TaskOutput::from((id, coins)), - }) - }, - Err(InvalidTask(cause)) if cause.is_fatal() => { - log::warn!("Failed to verify pending task {:?} : {:?}", self, cause); - let id = CollectedCoinsId::from(*id); - Ok(Self::Call::fail_task { deadline, task_id: id.into(), cause }) - }, - Err(InvalidTask(e)) => Err(TaskError::Evaluation(e)), - Err(NoRpcUrl(e)) => Err(TaskError::Scheduler(e.into())), - Err(RpcError(e)) => Err(TaskError::Scheduler(e.into())), - } - } - - fn is_persisted(id: &T::Hash) -> bool { - let id = CollectedCoinsId::from(*id); - crate::pallet::CollectedCoins::::contains_key(id) - } -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[allow(clippy::upper_case_acronyms)] -pub enum BurnDetails { - GCRE(ExternalAddress, ExternalTxId), - GATE(ExternalAddress, ExternalTxId), -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[allow(clippy::upper_case_acronyms)] -pub enum ContractType { - GCRE, - GATE, -} +// use crate::types::{ +// AddressId, Blockchain, /*DeployedContract,*/ ExternalAddress, ExternalTxId, SystemConfig, +// }; +// use frame_support::RuntimeDebug; +// use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +// use scale_info::TypeInfo; +// +// #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +// pub struct CollectedCoins { +// pub to: AddressId, +// pub amount: Balance, +// pub tx_id: ExternalTxId, +// pub contract_type: ContractType, +// } +// +// #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +// pub struct UnverifiedCollectedCoins { +// pub to: ExternalAddress, +// pub tx_id: ExternalTxId, +// pub contract: DeployedContract, +// pub contract_type: ContractType, +// } +// +// impl UnverifiedCollectedCoins { +// pub fn into_output(self, amount: T::Balance) -> CollectedCoins +// where +// T: Config, +// { +// let Self { to, tx_id, contract: DeployedContract { chain, .. }, contract_type } = self; +// let to = crate::AddressId::new::(&chain, to.as_slice()); +// CollectedCoins { amount, to, tx_id, contract_type } +// } +// } +// +// #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +// pub struct CollectedCoinsId(Hash); +// +// impl CollectedCoinsId { +// pub fn inner_hash(blockchain: &Blockchain, blockchain_tx_id: &[u8]) -> H +// where +// Hasher: Hash, +// { +// let key = concatenate!(blockchain.as_bytes(), blockchain_tx_id); +// ::hash(&key) +// } +// +// pub fn new( +// contract_chain: &Blockchain, +// blockchain_tx_id: &[u8], +// ) -> CollectedCoinsId +// where +// ::Hashing: Hash, +// { +// let hash = Self::inner_hash::(contract_chain, blockchain_tx_id); +// CollectedCoinsId(hash) +// } +// +// pub fn into_inner(self) -> H { +// self.0 +// } +// } +// +// impl From for CollectedCoinsId { +// fn from(hash: H) -> Self { +// Self(hash) +// } +// } +// +// use crate::ocw::errors::SchedulerError; +// use crate::ocw::tasks::OffchainVerification; +// use crate::ocw::VerificationFailureCause; +// use crate::ocw::VerificationResult; +// use crate::types::concatenate; +// use crate::Config; +// use crate::TaskOutput; +// use pallet_offchain_task_scheduler::tasks::error::TaskError; +// use pallet_offchain_task_scheduler::tasks::TaskV2; +// use sp_runtime::traits::Hash; +// +// impl OffchainVerification for UnverifiedCollectedCoins { +// type Output = T::Balance; +// +// fn verify(&self) -> VerificationResult { +// crate::Pallet::::verify_collect_coins_ocw(self) +// } +// } +// +// impl TaskV2 for UnverifiedCollectedCoins +// where +// UnverifiedCollectedCoins: OffchainVerification, +// >::Output: Into, +// { +// type Call = crate::pallet::Call; +// type EvaluationError = VerificationFailureCause; +// type SchedulerError = SchedulerError; +// fn to_id(&self) -> T::Hash { +// CollectedCoinsId::inner_hash::(&self.contract.chain, self.tx_id.as_slice()) +// } +// +// fn persistence_call( +// &self, +// deadline: T::BlockNumber, +// id: &T::Hash, +// ) -> Result> { +// use crate::ocw::OffchainError::*; +// match self.verify() { +// Ok(amount) => { +// let coins = self.clone().into_output::(amount.into()); +// let id = CollectedCoinsId::from(*id); +// Ok(Self::Call::persist_task_output { +// deadline, +// task_output: TaskOutput::from((id, coins)), +// }) +// }, +// Err(InvalidTask(cause)) if cause.is_fatal() => { +// log::warn!("Failed to verify pending task {:?} : {:?}", self, cause); +// let id = CollectedCoinsId::from(*id); +// Ok(Self::Call::fail_task { deadline, task_id: id.into(), cause }) +// }, +// Err(InvalidTask(e)) => Err(TaskError::Evaluation(e)), +// Err(NoRpcUrl(e)) => Err(TaskError::Scheduler(e.into())), +// Err(RpcError(e)) => Err(TaskError::Scheduler(e.into())), +// } +// } +// +// fn is_persisted(id: &T::Hash) -> bool { +// let id = CollectedCoinsId::from(*id); +// crate::pallet::CollectedCoins::::contains_key(id) +// } +// } +// +// #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +// #[allow(clippy::upper_case_acronyms)] +// pub enum BurnDetails { +// GCRE(ExternalAddress, ExternalTxId), +// GATE(ExternalAddress, ExternalTxId), +// } +// +// #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +// #[allow(clippy::upper_case_acronyms)] +// pub enum ContractType { +// GCRE, +// GATE, +// }