Skip to content

Commit

Permalink
Scx1332/verifiying transactions (#61)
Browse files Browse the repository at this point in the history
Added code for verifying transactions
  • Loading branch information
scx1332 authored Oct 9, 2023
1 parent b60e273 commit 81c36d0
Show file tree
Hide file tree
Showing 15 changed files with 227 additions and 106 deletions.
2 changes: 2 additions & 0 deletions crates/erc20_payment_lib/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct AdditionalOptions {
pub skip_multi_contract_check: bool,
pub contract_use_direct_method: bool,
pub contract_use_unpacked_method: bool,
pub use_transfer_for_single_payment: bool,
}

impl Default for AdditionalOptions {
Expand All @@ -27,6 +28,7 @@ impl Default for AdditionalOptions {
skip_multi_contract_check: false,
contract_use_direct_method: false,
contract_use_unpacked_method: false,
use_transfer_for_single_payment: true,
}
}
}
Expand Down
94 changes: 92 additions & 2 deletions crates/erc20_payment_lib/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use crate::db::ops::{
cleanup_token_transfer_tx, delete_tx, get_last_unsent_tx, insert_token_transfer,
};
use crate::signer::Signer;
use crate::transaction::create_token_transfer;
use crate::transaction::{create_token_transfer, find_receipt_extended};
use crate::{err_custom_create, err_from};
use std::collections::BTreeMap;
use std::ops::DerefMut;
use std::path::Path;
use std::str::FromStr;

use crate::error::{ErrorBag, PaymentError};

Expand All @@ -27,7 +28,9 @@ use serde::Serialize;
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use web3::types::{Address, U256};
use web3::transports::Http;
use web3::types::{Address, H256, U256};
use web3::Web3;

#[derive(Debug, Clone, Serialize)]
pub struct SharedInfoTx {
Expand Down Expand Up @@ -373,6 +376,7 @@ impl PaymentRuntime {
config.engine.process_sleep,
config.engine.automatic_recover,
)?;
payment_setup.use_transfer_for_single_payment = options.use_transfer_for_single_payment;
payment_setup.extra_options_for_testing = extra_testing;
payment_setup.contract_use_direct_method = options.contract_use_direct_method;
payment_setup.contract_use_unpacked_method = options.contract_use_unpacked_method;
Expand Down Expand Up @@ -411,6 +415,17 @@ impl PaymentRuntime {
})
}

pub async fn get_web3_provider(&self, chain_name: &str) -> Result<Web3<Http>, PaymentError> {
let chain_cfg = self.config.chain.get(chain_name).ok_or(err_custom_create!(
"Chain {} not found in config file",
chain_name
))?;

let web3 = self.setup.get_provider(chain_cfg.chain_id)?;

Ok(web3.clone())
}

pub async fn get_token_balance(
&self,
chain_name: String,
Expand Down Expand Up @@ -528,11 +543,86 @@ impl PaymentRuntime {
self.get_chain(chain_id).map(|chain| chain.network.as_str())
}

pub async fn verify_transaction(
&self,
chain_id: i64,
tx_hash: H256,
sender: Address,
receiver: Address,
amount: U256,
) -> Result<VerifyTransactionResult, PaymentError> {
let network_name = self.network_name(chain_id).ok_or(err_custom_create!(
"Chain {} not found in config file",
chain_id
))?;
let prov = self.get_web3_provider(network_name).await?;
verify_transaction(&prov, chain_id, tx_hash, sender, receiver, amount).await
}

pub fn chains(&self) -> Vec<i64> {
self.setup.chain_setup.keys().copied().collect()
}
}

pub struct VerifyTransactionResult {
pub verified: bool,
pub reason: Option<String>,
}

// This is for now very limited check. It needs lot more work to be complete
pub async fn verify_transaction(
web3: &web3::Web3<web3::transports::Http>,
chain_id: i64,
tx_hash: H256,
sender: Address,
receiver: Address,
amount: U256,
) -> Result<VerifyTransactionResult, PaymentError> {
let (chain_tx_dao, transfers) = find_receipt_extended(web3, tx_hash, chain_id).await?;
if chain_tx_dao.chain_status == 1 {
//one transaction can contain multiple transfers. Search for ours.
for transfer in transfers {
log::info!(
"Verifying {tx_hash:#x}: Found transfers on chain: {:?}",
transfer
);
if Address::from_str(&transfer.receiver_addr).map_err(err_from!())? == receiver
&& Address::from_str(&transfer.from_addr).map_err(err_from!())? == sender
{
return if U256::from_dec_str(&transfer.token_amount).map_err(err_from!())? >= amount
{
log::info!("Transaction found and verified: {}", tx_hash);
Ok(VerifyTransactionResult {
verified: true,
reason: None,
})
} else {
log::warn!(
"Transaction found but amount insufficient: {}: {}/{}",
tx_hash,
transfer.token_amount,
amount
);
Ok(VerifyTransactionResult {
verified: false,
reason: Some("Transaction found but amount insufficient".to_string()),
})
};
}
}
log::warn!("Transaction found but not matching: {}", tx_hash);
Ok(VerifyTransactionResult {
verified: false,
reason: Some("Transaction found but not matching".to_string()),
})
} else {
Ok(VerifyTransactionResult {
verified: false,
reason: Some("Transaction not found".to_string()),
})
}
}

pub async fn remove_last_unsent_transactions(
conn: SqlitePool,
) -> Result<Option<i64>, PaymentError> {
Expand Down
72 changes: 35 additions & 37 deletions crates/erc20_payment_lib/src/sender/batching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,43 +189,41 @@ pub async fn gather_transactions_batch_multi(
erc20_amounts.push(sum);
}

let web3tx = match erc20_to.len() {
0 => {
return Ok(0);
}
1 => {
log::info!(
"Inserting transaction stub for ERC20 transfer to: {:?}",
erc20_to[0]
);

create_erc20_transfer(
Address::from_str(&token_transfer.from_addr).map_err(err_from!())?,
Address::from_str(token_addr).map_err(err_from!())?,
erc20_to[0],
erc20_amounts[0],
token_transfer.chain_id as u64,
None,
max_fee_per_gas,
priority_fee,
)?
}
_ => {
log::info!("Inserting transaction stub for ERC20 multi transfer contract: {:?} for {} distinct transfers", chain_setup.multi_contract_address.unwrap(), erc20_to.len());

create_erc20_transfer_multi(
Address::from_str(&token_transfer.from_addr).map_err(err_from!())?,
chain_setup.multi_contract_address.unwrap(),
erc20_to,
erc20_amounts,
token_transfer.chain_id as u64,
None,
max_fee_per_gas,
priority_fee,
use_direct_method,
use_unpacked_method,
)?
}
let use_transfer_for_single_payment = false;

let web3tx = if erc20_to.is_empty() {
return Ok(0);
} else if use_transfer_for_single_payment && erc20_to.len() == 1 {
log::info!(
"Inserting transaction stub for ERC20 transfer to: {:?}",
erc20_to[0]
);

create_erc20_transfer(
Address::from_str(&token_transfer.from_addr).map_err(err_from!())?,
Address::from_str(token_addr).map_err(err_from!())?,
erc20_to[0],
erc20_amounts[0],
token_transfer.chain_id as u64,
None,
max_fee_per_gas,
priority_fee,
)?
} else {
log::info!("Inserting transaction stub for ERC20 multi transfer contract: {:?} for {} distinct transfers", chain_setup.multi_contract_address.unwrap(), erc20_to.len());

create_erc20_transfer_multi(
Address::from_str(&token_transfer.from_addr).map_err(err_from!())?,
chain_setup.multi_contract_address.unwrap(),
erc20_to,
erc20_amounts,
token_transfer.chain_id as u64,
None,
max_fee_per_gas,
priority_fee,
use_direct_method,
use_unpacked_method,
)?
};
let mut db_transaction = conn.begin().await.map_err(err_from!())?;
let web3_tx_dao = insert_tx(&mut *db_transaction, &web3tx)
Expand Down
2 changes: 2 additions & 0 deletions crates/erc20_payment_lib/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub struct PaymentSetup {
pub automatic_recover: bool,
pub contract_use_direct_method: bool,
pub contract_use_unpacked_method: bool,
pub use_transfer_for_single_payment: bool,
pub extra_options_for_testing: Option<ExtraOptionsForTesting>,
}

Expand Down Expand Up @@ -92,6 +93,7 @@ impl PaymentSetup {
contract_use_direct_method: false,
contract_use_unpacked_method: false,
extra_options_for_testing: None,
use_transfer_for_single_payment: true,
};
for chain_config in &config.chain {
let mut providers = Vec::new();
Expand Down
5 changes: 1 addition & 4 deletions crates/erc20_payment_lib_test/src/durabily2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,7 @@ pub async fn test_durability2(generate_count: u64, gen_interval_secs: f64, trans
Some(conn_.clone()),
Some(AdditionalOptions {
keep_running: false,
generate_tx_only: false,
skip_multi_contract_check: false,
contract_use_direct_method: false,
contract_use_unpacked_method: false,
..Default::default()
}),
Some(sender),
None,
Expand Down
6 changes: 2 additions & 4 deletions crates/erc20_payment_lib_test/src/multi_erc20_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,8 @@ pub async fn test_durability(generate_count: u64, gen_interval_secs: f64, transf
Some(conn_.clone()),
Some(AdditionalOptions {
keep_running: false,
generate_tx_only: false,
skip_multi_contract_check: false,
contract_use_direct_method: false,
contract_use_unpacked_method: false,
use_transfer_for_single_payment: false,
..Default::default()
}),
Some(sender),
None,
Expand Down
3 changes: 1 addition & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ async fn main_internal() -> Result<(), PaymentError> {
keep_running: run_options.keep_running,
generate_tx_only: run_options.generate_tx_only,
skip_multi_contract_check: run_options.skip_multi_contract_check,
contract_use_direct_method: false,
contract_use_unpacked_method: false,
..Default::default()
};

let sp = PaymentRuntime::new(
Expand Down
38 changes: 27 additions & 11 deletions tests/docker_01_basic/single_erc20_transfer.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use erc20_payment_lib::config::AdditionalOptions;
use erc20_payment_lib::db::model::TxDao;
use erc20_payment_lib::db::ops::insert_token_transfer;
use erc20_payment_lib::misc::load_private_keys;
use erc20_payment_lib::runtime::DriverEventContent::*;
use erc20_payment_lib::runtime::{DriverEvent, PaymentRuntime};
use erc20_payment_lib::runtime::{verify_transaction, DriverEvent, PaymentRuntime};
use erc20_payment_lib::signer::PrivateKeySigner;
use erc20_payment_lib::transaction::create_token_transfer;
use erc20_payment_lib::utils::u256_to_rust_dec;
use erc20_payment_lib_test::*;
use rust_decimal::prelude::ToPrimitive;
use std::str::FromStr;
use std::time::Duration;
use web3::types::{Address, U256};
use web3::types::{Address, H256, U256};
use web3_test_proxy_client::list_transactions_human;

#[tokio::test(flavor = "multi_thread")]
Expand All @@ -23,6 +24,7 @@ async fn test_erc20_transfer() -> Result<(), anyhow::Error> {

let proxy_url_base = format!("http://127.0.0.1:{}", geth_container.web3_proxy_port);
let proxy_key = "erc20_transfer";
let mut tx_dao_return: Option<TxDao> = None;

let (sender, mut receiver) = tokio::sync::mpsc::channel::<DriverEvent>(1);
let receiver_loop = tokio::spawn(async move {
Expand All @@ -42,7 +44,8 @@ async fn test_erc20_transfer() -> Result<(), anyhow::Error> {
approve_contract_message_count += 1;
fee_paid += U256::from_dec_str(&allowance_dao.fee_paid.expect("fee paid should be set")).expect("fee paid should be a valid U256");
}
TransactionConfirmed(_tx_dao) => {
TransactionConfirmed(tx_dao) => {
tx_dao_return = Some(tx_dao);
tx_confirmed_message_count += 1;
},
StatusChanged(_) => { }
Expand All @@ -56,9 +59,9 @@ async fn test_erc20_transfer() -> Result<(), anyhow::Error> {
assert_eq!(tx_confirmed_message_count, 2);
assert_eq!(transfer_finished_message_count, 1);
assert_eq!(approve_contract_message_count, 1);
fee_paid
(fee_paid, tx_dao_return)
});
{
let web3 = {
let config = create_default_config_setup(&proxy_url_base, proxy_key).await;

//load private key for account 0xbfb29b133aa51c4b45b49468f9a22958eafea6fa
Expand Down Expand Up @@ -88,20 +91,20 @@ async fn test_erc20_transfer() -> Result<(), anyhow::Error> {
Some(conn.clone()),
Some(AdditionalOptions {
keep_running: false,
generate_tx_only: false,
skip_multi_contract_check: false,
contract_use_direct_method: false,
contract_use_unpacked_method: false,
..Default::default()
}),
Some(sender),
None
).await?;
let web3 = sp.get_web3_provider("dev").await.unwrap();
sp.runtime_handle.await?;
}
web3
};

#[allow(clippy::bool_assert_comparison)]
{
// *** RESULT CHECK ***
let fee_paid_u256 = receiver_loop.await.unwrap();
let (fee_paid_u256, tx_dao) = receiver_loop.await.unwrap();
let fee_paid = u256_to_rust_dec(fee_paid_u256,None).unwrap();
log::info!("fee paid: {}", fee_paid);
assert!(fee_paid.to_f64().unwrap() > 0.00008 && fee_paid.to_f64().unwrap() < 0.00015);
Expand All @@ -118,6 +121,19 @@ async fn test_erc20_transfer() -> Result<(), anyhow::Error> {
log::info!("transaction list \n {}", transaction_human.join("\n"));
assert!(transaction_human.len() > 30);
assert!(transaction_human.len() < 70);

let tx_dao = tx_dao.unwrap();
let tx_hash = H256::from_str(&tx_dao.tx_hash.unwrap()).unwrap();
let fr_str = Address::from_str("0xbfb29b133aa51c4b45b49468f9a22958eafea6fa").unwrap();
let fr_str_wrong = Address::from_str("0xcfb29b133aa51c4b45b49468f9a22958eafea6fa").unwrap();
let to_str = Address::from_str("0xf2f86a61b769c91fc78f15059a5bd2c189b84be2").unwrap();
let to_str_wrong = Address::from_str("0x02f86a61b769c91fc78f15059a5bd2c189b84be2").unwrap();
assert_eq!(verify_transaction(&web3, 987789, tx_hash,fr_str,to_str,U256::from(2222000000000000222_u128)).await.unwrap().verified, true);
assert_eq!(verify_transaction(&web3, 987789, tx_hash,fr_str,to_str_wrong,U256::from(2222000000000000222_u128)).await.unwrap().verified, false);
assert_eq!(verify_transaction(&web3, 987789, tx_hash,fr_str_wrong,to_str,U256::from(2222000000000000222_u128)).await.unwrap().verified, false);
assert_eq!(verify_transaction(&web3, 987789, tx_hash,fr_str,to_str,U256::from(2222000000000000223_u128)).await.unwrap().verified, false);


}

Ok(())
Expand Down
5 changes: 1 addition & 4 deletions tests/docker_01_basic/single_gas_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ async fn test_gas_transfer() -> Result<(), anyhow::Error> {
Some(conn.clone()),
Some(AdditionalOptions {
keep_running: false,
generate_tx_only: false,
skip_multi_contract_check: false,
contract_use_direct_method: false,
contract_use_unpacked_method: false,
..Default::default()
}),
Some(sender),
None
Expand Down
Loading

0 comments on commit 81c36d0

Please sign in to comment.