From b4b1992873c0e8c43f5e600eedc6b6fb0d7a2f43 Mon Sep 17 00:00:00 2001 From: Adam Wierzbicki Date: Wed, 27 Jan 2021 12:45:02 +0100 Subject: [PATCH] ERC-20 payment driver: added network field to transaction For the sender to calculate nonces properly, it is necessary to include network in transaction model as well. Additionally: * Added some log messages. * Remove `unwrap()` that cause driver to panic on tx submit error. * Extended timeout in invoice_flow and debit_note_flow (300s wasn't always enough for mainnet transactions). Signed-off-by: Adam Wierzbicki --- Cargo.lock | 17 +++++++- core/payment-driver/gnt/Cargo.toml | 2 + .../2021-01-18-154920_network_id/down.sql | 26 +++++++++++- .../2021-01-18-154920_network_id/up.sql | 2 + .../payment-driver/gnt/src/dao/transaction.rs | 20 +++++++-- core/payment-driver/gnt/src/gnt/sender.rs | 42 ++++++++++++------- core/payment-driver/gnt/src/lib.rs | 3 ++ core/payment-driver/gnt/src/models.rs | 1 + core/payment-driver/gnt/src/networks.rs | 2 +- core/payment-driver/gnt/src/schema.rs | 1 + core/payment-driver/gnt/src/utils.rs | 6 ++- core/payment/examples/debit_note_flow.rs | 4 +- core/payment/examples/invoice_flow.rs | 2 +- 13 files changed, 101 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3d2997fa7..503930af77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "parking_lot 0.11.1", "pin-project 0.4.27", "smallvec 1.6.1", - "tokio", + "tokio 0.2.24", "tokio-util 0.3.1", ] @@ -3930,6 +3930,17 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn 1.0.60", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -7314,6 +7325,8 @@ dependencies = [ "log", "maplit", "num-bigint 0.2.6", + "num-derive 0.3.3", + "num-traits", "r2d2", "rlp", "secp256k1 0.19.0", @@ -7397,7 +7410,7 @@ dependencies = [ "libsqlite3-sys", "log", "metrics", - "num-derive", + "num-derive 0.2.5", "num-traits", "r2d2", "rand 0.7.3", diff --git a/core/payment-driver/gnt/Cargo.toml b/core/payment-driver/gnt/Cargo.toml index ff3edaf27a..af2cdfb59d 100644 --- a/core/payment-driver/gnt/Cargo.toml +++ b/core/payment-driver/gnt/Cargo.toml @@ -30,6 +30,8 @@ hex = "0.4" lazy_static = "1.4" log = "0.4.8" num-bigint = "0.2" +num-derive = "0.3" +num-traits = "0.2" maplit = "1.0" r2d2 = "0.8" rlp = "0.4" diff --git a/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/down.sql b/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/down.sql index 9f8f17b168..8c3a06b35a 100644 --- a/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/down.sql +++ b/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/down.sql @@ -1,4 +1,4 @@ --- HACK: All this code below is just to drop column network from table gnt_driver_payment +-- HACK: All this code below is just to drop column network from tables gnt_driver_payment and gnt_driver_transaction PRAGMA foreign_keys=off; @@ -26,4 +26,28 @@ DROP TABLE gnt_driver_payment; ALTER TABLE gnt_driver_payment_tmp RENAME TO gnt_driver_payment; +CREATE TABLE gnt_driver_transaction_tmp +( + tx_id VARCHAR(128) NOT NULL PRIMARY KEY, + sender VARCHAR(40) NOT NULL, + -- U256 in big endian hex + nonce VARCHAR(64) NOT NULL, + timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + status INTEGER NOT NULL, + tx_type INTEGER NOT NULL, + encoded VARCHAR (8000) NOT NULL, + signature VARCHAR (130) NOT NULL, + tx_hash VARCHAR(64), + FOREIGN KEY(status) REFERENCES gnt_driver_transaction_status (status_id), + FOREIGN KEY(tx_type) REFERENCES gnt_driver_transaction_type (type_id) +); + +INSERT INTO gnt_driver_transaction_tmp(tx_id, sender, nonce, timestamp, status, tx_type, encoded, signature, tx_hash) +SELECT tx_id, sender, nonce, timestamp, status, tx_type, encoded, signature, tx_hash FROM gnt_driver_transaction; + + +DROP TABLE gnt_driver_transaction; + +ALTER TABLE gnt_driver_transaction_tmp RENAME TO gnt_driver_transaction; + PRAGMA foreign_keys=on; diff --git a/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/up.sql b/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/up.sql index b7e010dbbb..7ec4ecab38 100644 --- a/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/up.sql +++ b/core/payment-driver/gnt/migrations/2021-01-18-154920_network_id/up.sql @@ -1 +1,3 @@ ALTER TABLE gnt_driver_payment ADD COLUMN network INTEGER NOT NULL DEFAULT 4; -- 4 is rinkeby's network ID + +ALTER TABLE gnt_driver_transaction ADD COLUMN network INTEGER NOT NULL DEFAULT 4; -- 4 is rinkeby's network ID diff --git a/core/payment-driver/gnt/src/dao/transaction.rs b/core/payment-driver/gnt/src/dao/transaction.rs index c8200408a9..9c5876d515 100644 --- a/core/payment-driver/gnt/src/dao/transaction.rs +++ b/core/payment-driver/gnt/src/dao/transaction.rs @@ -4,6 +4,7 @@ use crate::models::{TransactionEntity, TransactionStatus}; use crate::schema::gnt_driver_transaction::dsl; use crate::dao::DbResult; +use crate::networks::Network; use ya_persistence::executor::{do_with_transaction, AsDao, PoolType}; #[allow(unused)] @@ -44,10 +45,15 @@ impl<'c> TransactionDao<'c> { .await } - pub async fn get_used_nonces(&self, address: String) -> DbResult> { + pub async fn get_used_nonces( + &self, + address: String, + network: Network, + ) -> DbResult> { do_with_transaction(self.pool, move |conn| { let nonces: Vec = dsl::gnt_driver_transaction .filter(dsl::sender.eq(address)) + .filter(dsl::network.eq(network)) .select(dsl::nonce) .load(conn)?; Ok(nonces) @@ -55,14 +61,20 @@ impl<'c> TransactionDao<'c> { .await } - pub async fn get_unconfirmed_txs(&self) -> DbResult> { - self.get_by_status(TransactionStatus::Sent.into()).await + pub async fn get_unconfirmed_txs(&self, network: Network) -> DbResult> { + self.get_by_status(TransactionStatus::Sent.into(), network) + .await } - pub async fn get_by_status(&self, status: i32) -> DbResult> { + pub async fn get_by_status( + &self, + status: i32, + network: Network, + ) -> DbResult> { do_with_transaction(self.pool, move |conn| { let txs: Vec = dsl::gnt_driver_transaction .filter(dsl::status.eq(status)) + .filter(dsl::network.eq(network)) .load(conn)?; Ok(txs) }) diff --git a/core/payment-driver/gnt/src/gnt/sender.rs b/core/payment-driver/gnt/src/gnt/sender.rs index cd346fe954..bf218bf51d 100644 --- a/core/payment-driver/gnt/src/gnt/sender.rs +++ b/core/payment-driver/gnt/src/gnt/sender.rs @@ -197,23 +197,27 @@ impl TransactionSender { let db = self.db.clone(); let client = self.ethereum_client.clone(); let address_str = crate::utils::addr_to_str(&address); + let network = self.network; future::try_join( async move { - Ok::<_, GNTDriverError>( - db.as_dao::() - .get_used_nonces(address_str) - .await? - .into_iter() - .map(u256_from_big_endian_hex) - .max(), - ) + let db_nonce = db + .as_dao::() + .get_used_nonces(address_str, network) + .await? + .into_iter() + .map(u256_from_big_endian_hex) + .max(); + log::trace!("DB nonce: {:?}", db_nonce); + Ok::<_, GNTDriverError>(db_nonce) }, async move { - client + let client_nonce = client .get_next_nonce(address) .map_err(GNTDriverError::from) - .await + .await; + log::trace!("Client nonce: {:?}", client_nonce); + client_nonce }, ) .and_then(|r| { @@ -395,6 +399,9 @@ impl Handler for TransactionSender { }) .collect::>(); + db_transactions + .iter() + .for_each(|tx| log::trace!("Creating db transaction: {:?}", tx)); let db = self.db.clone(); let fut = { let db = db.clone(); @@ -434,7 +441,8 @@ impl Handler for TransactionSender { confirmations: required_confirmations, }); } - Err(_e) => { + Err(e) => { + log::error!("Error sending transaction: {:?}", e); db.as_dao::() .update_tx_status(tx_id, TransactionStatus::Failed.into()) .await @@ -762,10 +770,11 @@ impl TransactionSender { let db = self.db.clone(); let me = ctx.address(); let required_confirmations = self.required_confirmations; + let network = self.network; let job = async move { let txs = db .as_dao::() - .get_unconfirmed_txs() + .get_unconfirmed_txs(network) .await .unwrap(); for tx in txs { @@ -904,7 +913,7 @@ async fn process_payment( }, ) .await?; - log::error!("NGNT transfer failed: {}", e); + log::error!("GLM transfer failed: {}", e); return Err(e); } } @@ -936,7 +945,12 @@ async fn transfer_gnt( GNT_TRANSFER_GAS.into(), ); let r = batch.send_to(tx_sender, sign_tx).await?; - Ok(r.into_iter().next().unwrap()) + match r.into_iter().next() { + Some(tx) => Ok(tx), + None => Err(GNTDriverError::LibraryError( + "GLM transfer failed".to_string(), + )), + } } async fn notify_tx_confirmed(db: DbExecutor, tx_id: String) -> GNTDriverResult<()> { diff --git a/core/payment-driver/gnt/src/lib.rs b/core/payment-driver/gnt/src/lib.rs index e3cad6cbe5..4d31da6744 100644 --- a/core/payment-driver/gnt/src/lib.rs +++ b/core/payment-driver/gnt/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate diesel; +#[macro_use] +extern crate num_derive; + pub mod migrations { #[derive(diesel_migrations::EmbedMigrations)] struct _Dummy; diff --git a/core/payment-driver/gnt/src/models.rs b/core/payment-driver/gnt/src/models.rs index 3d7701d588..90fd1c2536 100644 --- a/core/payment-driver/gnt/src/models.rs +++ b/core/payment-driver/gnt/src/models.rs @@ -69,6 +69,7 @@ pub struct TransactionEntity { pub encoded: String, pub signature: String, pub tx_hash: Option, + pub network: Network, } #[derive(Queryable, Clone, Debug, Identifiable, Insertable, PartialEq)] diff --git a/core/payment-driver/gnt/src/networks.rs b/core/payment-driver/gnt/src/networks.rs index 934c9dbdad..307d88e7f9 100644 --- a/core/payment-driver/gnt/src/networks.rs +++ b/core/payment-driver/gnt/src/networks.rs @@ -10,7 +10,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use std::str::FromStr; use ya_core_model::payment::local as pay_srv; -#[derive(AsExpression, FromSqlRow, PartialEq, Debug, Clone, Copy)] +#[derive(AsExpression, FromSqlRow, PartialEq, Debug, Clone, Copy, FromPrimitive)] #[sql_type = "Integer"] pub enum Network { Mainnet = 1, diff --git a/core/payment-driver/gnt/src/schema.rs b/core/payment-driver/gnt/src/schema.rs index fb8de63b23..11029b45db 100644 --- a/core/payment-driver/gnt/src/schema.rs +++ b/core/payment-driver/gnt/src/schema.rs @@ -30,6 +30,7 @@ table! { encoded -> Text, signature -> Text, tx_hash -> Nullable, + network -> Integer, } } diff --git a/core/payment-driver/gnt/src/utils.rs b/core/payment-driver/gnt/src/utils.rs index 599f098478..04ab0b844e 100644 --- a/core/payment-driver/gnt/src/utils.rs +++ b/core/payment-driver/gnt/src/utils.rs @@ -1,13 +1,14 @@ use crate::error::GNTDriverError; -use crate::GNTDriverResult; - use crate::models::{TransactionEntity, TransactionStatus, TxType}; +use crate::networks::Network; +use crate::GNTDriverResult; use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; use ethereum_tx_sign::RawTransaction; use ethereum_types::{Address, H160, H256, U256}; use futures3::{Future, FutureExt}; use num_bigint::ToBigInt; +use num_traits::FromPrimitive; use sha3::{Digest, Sha3_512}; use std::pin::Pin; use std::str::FromStr; @@ -93,6 +94,7 @@ pub fn raw_tx_to_entity( tx_type: tx_type.into(), signature: hex::encode(signature), tx_hash: None, + network: Network::from_u64(chain_id).unwrap(), } } diff --git a/core/payment/examples/debit_note_flow.rs b/core/payment/examples/debit_note_flow.rs index 8b9a28b143..68df8c0881 100644 --- a/core/payment/examples/debit_note_flow.rs +++ b/core/payment/examples/debit_note_flow.rs @@ -111,7 +111,7 @@ async fn main() -> anyhow::Result<()> { log::info!("Debit note accepted."); log::info!("Waiting for payment..."); - let timeout = Some(Duration::from_secs(300)); // Should be enough for GLM transfer + let timeout = Some(Duration::from_secs(1000)); // Should be enough for GLM transfer let mut payments = provider .get_payments(Some(&now), timeout, None, None) .await?; @@ -156,7 +156,7 @@ async fn main() -> anyhow::Result<()> { log::info!("Debit note accepted."); log::info!("Waiting for payment..."); - let timeout = Some(Duration::from_secs(300)); // Should be enough for GLM transfer + let timeout = Some(Duration::from_secs(1000)); // Should be enough for GLM transfer let mut payments = provider .get_payments(Some(&now), timeout, None, args.app_session_id.clone()) .await?; diff --git a/core/payment/examples/invoice_flow.rs b/core/payment/examples/invoice_flow.rs index 5a71efa772..7d559092be 100644 --- a/core/payment/examples/invoice_flow.rs +++ b/core/payment/examples/invoice_flow.rs @@ -120,7 +120,7 @@ async fn main() -> anyhow::Result<()> { log::debug!("events 2: {:?}", &invoice_events_accepted); log::info!("Waiting for payment..."); - let timeout = Some(Duration::from_secs(300)); // Should be enough for GLM transfer + let timeout = Some(Duration::from_secs(1000)); // Should be enough for GLM transfer let mut payments = provider .get_payments(Some(&now), timeout, None, args.app_session_id.clone()) .await?;