From a73b0872ef21d1b7b198093d619060032fee81bb Mon Sep 17 00:00:00 2001 From: scx1332 Date: Wed, 13 Mar 2024 13:09:49 +0100 Subject: [PATCH] working on finishing deposits --- .github/workflows/ci.yml | 80 ------------------- crates/erc20_payment_lib/config-payments.toml | 4 +- crates/erc20_payment_lib/src/runtime.rs | 74 +++++++++++++---- .../erc20_payment_lib/src/sender/batching.rs | 48 +++++++++-- crates/erc20_payment_lib/src/transaction.rs | 2 +- .../src/db/model/token_transfer_dao.rs | 2 +- .../src/db/ops/token_transfer_ops.rs | 60 ++++++++++---- src/actions/deposit/close.rs | 2 + src/main.rs | 2 +- 9 files changed, 154 insertions(+), 120 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69c2e2f1..70b18254 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -187,86 +187,6 @@ jobs: - name: Run tests (docker_03_problems) run: cargo test --test docker_03_problems --profile=release-fast -- --test-threads=10 - test_faucet: - name: Test Goerli faucet - timeout-minutes: 20 - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Cache dependencies - uses: Swatinem/rust-cache@v2 - with: - shared-key: "dev-build-cache" - - - name: Build - run: cargo build - - - name: Run tests (faucet) - run: cargo run -- generate-key -n 1 > .env - - - name: Check if balance is 0 - run: | - [ $(cargo run -- balance -c goerli | jq -r '.[] | .gasDecimal') == "0" ] - [ $(cargo run -- balance -c goerli | jq -r '.[] | .tokenDecimal') == "0" ] - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Get ETH from faucet - run: cargo run -- get-dev-eth -c goerli - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Check ETH balance after getting funds from faucet (should be 0.01) - run: | - sleep 60 # give time for the blockchain to propagate info about the transaction - [ $(cargo run -- balance -c goerli | jq -r '.[] | .gasDecimal') == "0.01" ] - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Mint tokens - run: | - cargo run -- mint-test-tokens -c goerli - cargo run -- run - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Check token balance - run: | - [ $(cargo run -- balance -c goerli | jq -r '.[] | .tokenDecimal') == "1000" ] - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Transfer 166.6 GLM tokens - run: | - cargo run -- transfer -c goerli --recipient 0x5b984629E2Cc7570cBa7dD745b83c3dD23Ba6d0f --token glm --amount 166.6 - cargo run -- run - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Transfer all GLM tokens - run: | - cargo run -- transfer -c goerli --recipient 0x5b984629E2Cc7570cBa7dD745b83c3dD23Ba6d0f --token glm --all - cargo run -- run - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Check token balance zero - run: | - [ $(cargo run -- balance -c goerli | jq -r '.[] | .tokenDecimal') == "0" ] - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - - - name: Transfer all left ETH tokens - run: | - cargo run -- transfer -c goerli --recipient 0x5b984629E2Cc7570cBa7dD745b83c3dD23Ba6d0f --token eth --all - cargo run -- run - env: - GOERLI_GETH_ADDR: ${{ secrets.GOERLI_RPC_ADDRESS }} - test_faucet_holesky: name: Test Holesky faucet timeout-minutes: 20 diff --git a/crates/erc20_payment_lib/config-payments.toml b/crates/erc20_payment_lib/config-payments.toml index 8cf8c34f..a71e4851 100644 --- a/crates/erc20_payment_lib/config-payments.toml +++ b/crates/erc20_payment_lib/config-payments.toml @@ -122,13 +122,13 @@ https://rpc.ankr.com/eth_goerli, priority = 0 max-timeout-ms = 5000 verify-interval-secs = 60 -allowed-head-behind-secs = 60 +allowed-head-behind-secs = 12000 [[chain.goerli.rpc-endpoints]] priority = 0 max-timeout-ms = 5000 verify-interval-secs = 60 -allowed-head-behind-secs = 120 +allowed-head-behind-secs = 12000 dns-source = "goerli.rpc-node.dev.golem.network." diff --git a/crates/erc20_payment_lib/src/runtime.rs b/crates/erc20_payment_lib/src/runtime.rs index 9d733b1b..88636aa4 100644 --- a/crates/erc20_payment_lib/src/runtime.rs +++ b/crates/erc20_payment_lib/src/runtime.rs @@ -1,15 +1,11 @@ use crate::signer::{Signer, SignerAccount}; use crate::transaction::{ - create_close_deposit, create_faucet_mint, create_make_deposit, create_terminate_deposit, + create_faucet_mint, create_make_deposit, create_terminate_deposit, create_token_transfer, find_receipt_extended, FindReceiptParseResult, }; use crate::{err_custom_create, err_from}; use erc20_payment_lib_common::create_sqlite_connection; -use erc20_payment_lib_common::ops::{ - cleanup_allowance_tx, cleanup_token_transfer_tx, delete_tx, get_last_unsent_tx, - get_transaction_chain, get_transactions, get_unpaid_token_transfers, - insert_token_transfer_with_deposit_check, insert_tx, -}; +use erc20_payment_lib_common::ops::{cleanup_allowance_tx, cleanup_token_transfer_tx, delete_tx, get_last_unsent_tx, get_token_transfers_by_deposit_id, get_transaction_chain, get_transactions, get_unpaid_token_transfers, insert_token_transfer, insert_token_transfer_with_deposit_check, insert_tx, update_token_transfer}; use std::collections::BTreeMap; use std::ops::DerefMut; use std::path::PathBuf; @@ -45,6 +41,7 @@ use std::time::Duration; use tokio::sync::{broadcast, mpsc, Mutex, Notify}; use tokio::task::{JoinError, JoinHandle}; use web3::types::{Address, H256, U256}; +use erc20_payment_lib_common::model::TokenTransferDbObj; #[derive(Debug, Clone, Serialize)] pub struct SharedState { @@ -1124,6 +1121,7 @@ pub struct CloseDepositOptionsInt { pub lock_contract_address: Address, pub skip_deposit_check: bool, pub deposit_id: U256, + pub token_address: Address, } pub struct TerminateDepositOptionsInt { @@ -1139,13 +1137,6 @@ pub async fn close_deposit( from: Address, opt: CloseDepositOptionsInt, ) -> Result<(), PaymentError> { - let free_deposit_tx_id = create_close_deposit( - from, - opt.lock_contract_address, - chain_id, - None, - opt.deposit_id, - )?; //let mut block_info: Option = None; if !opt.skip_deposit_check { @@ -1166,12 +1157,65 @@ pub async fn close_deposit( } let mut db_transaction = conn.begin().await.map_err(err_from!())?; - let make_deposit_tx = insert_tx(&mut *db_transaction, &free_deposit_tx_id) + + + let current_token_transfers = get_token_transfers_by_deposit_id(&mut *db_transaction, chain_id as i64, &format!("{:#x}", opt.deposit_id)) .await .map_err(err_from!())?; + + for tt in ¤t_token_transfers { + if tt.deposit_finish > 0 { + return Err(err_custom_create!("Deposit {} already being closed or closed", opt.deposit_id)); + } + } + let mut candidate_for_mark_close: Option<&TokenTransferDbObj> = None; + for tt in ¤t_token_transfers { + if tt.tx_id.is_none() { + if let Some(old_tx) = candidate_for_mark_close { + if old_tx.id < tt.id { + candidate_for_mark_close = Some(tt); + } else { + candidate_for_mark_close = Some(old_tx); + } + } else { + candidate_for_mark_close = Some(tt); + } + } + } + + if let Some(tt) = candidate_for_mark_close { + let mut tt = tt.clone(); + tt.deposit_finish = 1; + update_token_transfer(&mut *db_transaction, &tt).await.map_err(err_from!())?; + } else { + //empty transfer is just a marker that we need deposit to be closed + let new_tt = TokenTransferDbObj { + id: 0, + payment_id: Some(format!("close_deposit_{:#x}", opt.deposit_id)), + from_addr: format!("{:#x}", from), + receiver_addr: format!("{:#x}", Address::zero()), + chain_id: chain_id as i64, + token_addr: Some(format!("{:#x}", opt.token_address)), + token_amount: "0".to_string(), + deposit_id: Some(format!("{:#x}", opt.deposit_id)), + deposit_finish: 1, + create_date: chrono::Utc::now(), + tx_id: None, + paid_date: None, + fee_paid: None, + error: None, + }; + insert_token_transfer(&mut *db_transaction, &new_tt).await.map_err(err_from!())?; + } + + + //let mut db_transaction = conn.begin().await.map_err(err_from!())?; + //let make_deposit_tx = insert_tx(&mut *db_transaction, &free_deposit_tx_id) + // .await + // .map_err(err_from!())?; db_transaction.commit().await.map_err(err_from!())?; - log::info!("Close deposit added to queue: {}", make_deposit_tx.id); + //log::info!("Close deposit added to queue: {}", make_deposit_tx.id); Ok(()) } diff --git a/crates/erc20_payment_lib/src/sender/batching.rs b/crates/erc20_payment_lib/src/sender/batching.rs index 75740d10..f65737a6 100644 --- a/crates/erc20_payment_lib/src/sender/batching.rs +++ b/crates/erc20_payment_lib/src/sender/batching.rs @@ -5,11 +5,7 @@ use erc20_payment_lib_common::ops::*; use crate::error::{AllowanceRequest, ErrorBag, PaymentError}; -use crate::transaction::{ - create_erc20_deposit_transfer, create_erc20_transfer, create_erc20_transfer_multi, - create_erc20_transfer_multi_deposit, create_eth_transfer, MultiTransferArgs, - MultiTransferDepositArgs, -}; +use crate::transaction::{create_close_deposit, create_erc20_deposit_transfer, create_erc20_transfer, create_erc20_transfer_multi, create_erc20_transfer_multi_deposit, create_eth_transfer, MultiTransferArgs, MultiTransferDepositArgs}; use crate::setup::PaymentSetup; use crate::{err_create, err_custom_create, err_from}; @@ -51,7 +47,7 @@ pub struct TokenTransferMultiOrder { pub async fn gather_transactions_pre( account: &SignerAccount, conn: &SqlitePool, - _payment_setup: &PaymentSetup, + payment_setup: &PaymentSetup, ) -> Result { let mut transfer_map = TokenTransferMap::new(); @@ -81,6 +77,45 @@ pub async fn gather_transactions_pre( continue; } } + if let Some(deposit_id) = f.deposit_id.as_ref() { + if f.deposit_finish > 0 { + log::info!("Creating close deposit transaction for deposit id: {}", deposit_id); + + let deposit_id_u256 = U256::from_str(deposit_id).map_err( + |err| err_custom_create!("Invalid deposit id: {}", deposit_id), + )?; + + let lock_contract_address = payment_setup.chain_setup.get(&f.chain_id).ok_or(err_custom_create!( + "No setup found for chain id: {}", + f.chain_id + ))?.lock_contract_address.ok_or(err_custom_create!( + "Lock contract address not set for chain id: {}", + f.chain_id + ))?; + + let from_addr = Address::from_str(&f.from_addr).map_err(err_from!())?; + + + let close_deposit_tx_id = create_close_deposit( + from_addr, + lock_contract_address, + f.chain_id as u64, + None, + deposit_id_u256, + )?; + + let mut transaction = conn.begin().await.map_err(err_from!())?; + + let new_tx = insert_tx(&mut *transaction, &close_deposit_tx_id).await.map_err(err_from!())?; + + let mut tt = f.clone(); + tt.tx_id = Some(new_tx.id); + update_token_transfer(&mut *transaction, &tt).await.map_err(err_from!())?; + + transaction.commit().await.map_err(err_from!())?; + continue; + } + } match Address::from_str(&f.receiver_addr) { Ok(rec_address) => { if rec_address == Address::zero() { @@ -356,6 +391,7 @@ pub async fn gather_transactions_batch( ))?; let deposit_id = U256::from_str(deposit_id) .map_err(|err| err_custom_create!("Invalid deposit id: {}", err))?; + create_erc20_deposit_transfer( Address::from_str(&token_transfer.from_addr).map_err(err_from!())?, Address::from_str(&token_transfer.receiver_addr).map_err(err_from!())?, diff --git a/crates/erc20_payment_lib/src/transaction.rs b/crates/erc20_payment_lib/src/transaction.rs index f2179da6..e8b284eb 100644 --- a/crates/erc20_payment_lib/src/transaction.rs +++ b/crates/erc20_payment_lib/src/transaction.rs @@ -130,7 +130,7 @@ pub fn create_token_transfer( token_addr: token_addr.map(|addr| format!("{addr:#x}")), token_amount: token_amount.to_string(), deposit_id, - deposit_final: 0, + deposit_finish: 0, create_date: Utc::now(), tx_id: None, paid_date: None, diff --git a/crates/erc20_payment_lib_common/src/db/model/token_transfer_dao.rs b/crates/erc20_payment_lib_common/src/db/model/token_transfer_dao.rs index 1f794a81..5d00ffd8 100644 --- a/crates/erc20_payment_lib_common/src/db/model/token_transfer_dao.rs +++ b/crates/erc20_payment_lib_common/src/db/model/token_transfer_dao.rs @@ -13,7 +13,7 @@ pub struct TokenTransferDbObj { pub token_amount: String, /// If set payment done from internal deposit pub deposit_id: Option, - pub deposit_final: i64, + pub deposit_finish: i64, /// The time when the record is inserted into the database /// It is override when inserting new entry to db pub create_date: DateTime, diff --git a/crates/erc20_payment_lib_common/src/db/ops/token_transfer_ops.rs b/crates/erc20_payment_lib_common/src/db/ops/token_transfer_ops.rs index 85eafdc4..9b35a5dd 100644 --- a/crates/erc20_payment_lib_common/src/db/ops/token_transfer_ops.rs +++ b/crates/erc20_payment_lib_common/src/db/ops/token_transfer_ops.rs @@ -12,6 +12,24 @@ use std::ops::AddAssign; use std::str::FromStr; use web3::types::{Address, U256}; +pub async fn check_if_deposit_closed<'c, E>( + executor: E, + chain_id: i64, + deposit_id: &str, +) -> Result +where + E: Executor<'c, Database = Sqlite>, +{ + let finished_count = sqlx::query_as::<_, (i64,)>( + "SELECT COUNT(*) FROM token_transfer WHERE chain_id=$1 AND deposit_id=$2 AND deposit_finish=1", + ) + .bind(chain_id) + .bind(deposit_id) + .fetch_one(executor) + .await?; + Ok(finished_count.0 > 0) +} + pub async fn insert_token_transfer<'c, E>( executor: E, token_transfer: &TokenTransferDbObj, @@ -21,7 +39,7 @@ where { sqlx::query_as::<_, TokenTransferDbObj>( r"INSERT INTO token_transfer -(payment_id, from_addr, receiver_addr, chain_id, token_addr, token_amount, deposit_id, deposit_final, create_date, tx_id, paid_date, fee_paid, error) +(payment_id, from_addr, receiver_addr, chain_id, token_addr, token_amount, deposit_id, deposit_finish, create_date, tx_id, paid_date, fee_paid, error) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, strftime('%Y-%m-%dT%H:%M:%f', 'now'), $9, $10, $11, $12) RETURNING *; ", ) @@ -32,7 +50,7 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, strftime('%Y-%m-%dT%H:%M:%f', 'now'), $9 .bind(&token_transfer.token_addr) .bind(&token_transfer.token_amount) .bind(&token_transfer.deposit_id) - .bind(token_transfer.deposit_final) + .bind(token_transfer.deposit_finish) .bind(token_transfer.tx_id) .bind(token_transfer.paid_date) .bind(&token_transfer.fee_paid) @@ -47,21 +65,17 @@ pub async fn insert_token_transfer_with_deposit_check( ) -> Result { if let Some(deposit_id) = token_transfer.deposit_id.as_ref() { let mut transaction = conn.begin().await.map_err(err_from!())?; - let is_finished = sqlx::query_as::<_, (i64,)>( - "SELECT COUNT(*) FROM token_transfer WHERE deposit_id=$1 AND deposit_final=1", - ) - .bind(deposit_id) - .fetch_one(&mut *transaction) - .await - .map_err(err_from!())?; - if is_finished.0 > 0 { + let is_finished = check_if_deposit_closed(&mut *transaction, token_transfer.chain_id, deposit_id) + .await + .map_err(err_from!())?; + if is_finished { return Err(err_custom_create!( "Cannot add token_transfer to already finished deposit" )); } let res = sqlx::query_as::<_, TokenTransferDbObj>( r"INSERT INTO token_transfer -(payment_id, from_addr, receiver_addr, chain_id, token_addr, token_amount, deposit_id, deposit_final, create_date, tx_id, paid_date, fee_paid, error) +(payment_id, from_addr, receiver_addr, chain_id, token_addr, token_amount, deposit_id, deposit_finish, create_date, tx_id, paid_date, fee_paid, error) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, strftime('%Y-%m-%dT%H:%M:%f', 'now'), $9, $10, $11, $12) RETURNING *; ", ) @@ -72,7 +86,7 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, strftime('%Y-%m-%dT%H:%M:%f', 'now'), $9 .bind(&token_transfer.token_addr) .bind(&token_transfer.token_amount) .bind(&token_transfer.deposit_id) - .bind(token_transfer.deposit_final) + .bind(token_transfer.deposit_finish) .bind(token_transfer.tx_id) .bind(token_transfer.paid_date) .bind(&token_transfer.fee_paid) @@ -144,7 +158,7 @@ chain_id = $5, token_addr = $6, token_amount = $7, deposit_id = $8, -deposit_final = $9, +deposit_finish = $9, tx_id = $10, paid_date = $11, fee_paid = $12, @@ -160,7 +174,7 @@ WHERE id = $1 .bind(&token_transfer.token_addr) .bind(&token_transfer.token_amount) .bind(&token_transfer.deposit_id) - .bind(token_transfer.deposit_final) + .bind(token_transfer.deposit_finish) .bind(token_transfer.tx_id) .bind(token_transfer.paid_date) .bind(&token_transfer.fee_paid) @@ -200,6 +214,24 @@ pub async fn get_token_transfers_by_chain_id( Ok(rows) } +pub async fn get_token_transfers_by_deposit_id<'c, E>( + conn: E, + chain_id: i64, + deposit_id: &str, +) -> Result, sqlx::Error> + where + E: Executor<'c, Database = Sqlite>, +{ + let rows = sqlx::query_as::<_, TokenTransferDbObj>( + r"SELECT * FROM token_transfer WHERE chain_id = $1 AND deposit_id = $2 ORDER by id DESC", + ) + .bind(chain_id) + .bind(deposit_id) + .fetch_all(conn) + .await?; + Ok(rows) +} + pub async fn get_pending_token_transfers( conn: &SqlitePool, account: Address, diff --git a/src/actions/deposit/close.rs b/src/actions/deposit/close.rs index fcc6c180..2a4bfef1 100644 --- a/src/actions/deposit/close.rs +++ b/src/actions/deposit/close.rs @@ -71,6 +71,8 @@ pub async fn close_deposit_local( .expect("No lock contract found"), deposit_id, skip_deposit_check: close_deposit_options.skip_check, + token_address: chain_cfg + .token.address, }, ) .await?; diff --git a/src/main.rs b/src/main.rs index de15ca0a..5987e977 100644 --- a/src/main.rs +++ b/src/main.rs @@ -502,7 +502,7 @@ async fn main_internal() -> Result<(), PaymentError> { token_addr: token, token_amount: amount_str, deposit_id: single_transfer_options.deposit_id, - deposit_final: 0, + deposit_finish: 0, create_date: Default::default(), tx_id: None, paid_date: None,