diff options
author | Antoine A <> | 2022-02-24 17:19:11 +0100 |
---|---|---|
committer | Antoine A <> | 2022-02-24 17:19:11 +0100 |
commit | f83ecda42ee694ade4e5f4e16ce019d92b3754f4 (patch) | |
tree | 3832beeb539f2671ba6239390f4bc0bb3087a689 /eth-wire | |
parent | ec1d6043820b5a5d37e46fe2d26f228528663d60 (diff) | |
download | depolymerization-f83ecda42ee694ade4e5f4e16ce019d92b3754f4.tar.gz depolymerization-f83ecda42ee694ade4e5f4e16ce019d92b3754f4.tar.bz2 depolymerization-f83ecda42ee694ade4e5f4e16ce019d92b3754f4.zip |
Add eth instrumentation test and fix eth bounce
Diffstat (limited to 'eth-wire')
-rw-r--r-- | eth-wire/src/bin/eth-wire-utils.rs | 6 | ||||
-rw-r--r-- | eth-wire/src/lib.rs | 114 | ||||
-rw-r--r-- | eth-wire/src/loops/worker.rs | 73 | ||||
-rw-r--r-- | eth-wire/src/main.rs | 88 | ||||
-rw-r--r-- | eth-wire/src/rpc.rs | 29 | ||||
-rw-r--r-- | eth-wire/src/taler_util.rs | 4 |
6 files changed, 178 insertions, 136 deletions
diff --git a/eth-wire/src/bin/eth-wire-utils.rs b/eth-wire/src/bin/eth-wire-utils.rs index 5032036..16a3588 100644 --- a/eth-wire/src/bin/eth-wire-utils.rs +++ b/eth-wire/src/bin/eth-wire-utils.rs @@ -108,7 +108,7 @@ fn main() { .as_ref() .and_then(|it| it.data_dir.as_deref()) .or(args.datadir.as_deref()); - let mut rpc = Rpc::new(data_dir.unwrap().join("geth.ipc")).unwrap(); + let mut rpc = Rpc::new(data_dir.unwrap_or(&PathBuf::from("/tmp/")).join("geth.ipc")).unwrap(); let passwd = password(); match args.cmd { Cmd::Deposit(TransactionCmd { @@ -143,7 +143,6 @@ fn main() { to, value, nonce: None, - gas: None, gas_price: None, data: Hex(vec![]), }) @@ -188,7 +187,7 @@ fn main() { } Cmd::Balance { addr } => { let addr = H160::from_str(&addr).unwrap(); - let balance = rpc.balance(&addr).unwrap(); + let balance = rpc.get_balance(&addr).unwrap(); println!("{}", (balance / 10_000_000_000u64).as_u64()); } Cmd::Connect { datadir } => { @@ -215,7 +214,6 @@ fn main() { from, to: tx.to.unwrap(), value: U256::zero(), - gas: None, gas_price: Some(U256::from(1u8)), // Bigger gas price to replace fee data: Hex(vec![]), nonce: Some(tx.nonce), diff --git a/eth-wire/src/lib.rs b/eth-wire/src/lib.rs index b386006..923bdaf 100644 --- a/eth-wire/src/lib.rs +++ b/eth-wire/src/lib.rs @@ -14,17 +14,18 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use common::url::Url; -use ethereum_types::{Address, H256, U256, U64}; +use std::{str::FromStr, sync::atomic::AtomicU16}; + +use common::{api_common::Amount, config::EthConfig, url::Url}; +use ethereum_types::{Address, H160, H256, U256, U64}; use metadata::{InMetadata, OutMetadata}; -use rpc::hex::Hex; +use rpc::{hex::Hex, Transaction}; +use taler_util::{eth_payto_addr, taler_to_eth}; pub mod metadata; pub mod rpc; pub mod taler_util; -const GWEI: u64 = 1_000_000_000; - impl rpc::Rpc { pub fn deposit( &mut self, @@ -39,7 +40,6 @@ impl rpc::Rpc { to, value, nonce: None, - gas: None, gas_price: None, data: Hex(metadata.encode()), }) @@ -59,7 +59,6 @@ impl rpc::Rpc { to, value, nonce: None, - gas: None, gas_price: None, data: Hex(metadata.encode()), }) @@ -76,7 +75,6 @@ impl rpc::Rpc { to: tx.from.expect("Cannot bounce coinbase transaction"), value: bounce_value, nonce: None, - gas: None, gas_price: None, data: Hex(metadata.encode()), }; @@ -85,13 +83,80 @@ impl rpc::Rpc { // Deduce fee price from bounced value request.value = request .value - .saturating_sub(fill.tx.gas * GWEI * fill.tx.gas_price); + .saturating_sub(fill.tx.gas * fill.tx.gas_price.or(fill.tx.max_fee_per_gas).unwrap()); Ok(if request.value.is_zero() { None } else { Some(self.send_transaction(&request)?) }) } + + /// List new and removed transaction since the last sync state, returning a new sync state + pub fn list_since_sync_state( + &mut self, + address: &Address, + state: SyncState, + min_confirmation: u16, + ) -> rpc::Result<(Vec<(Transaction, u16)>, Vec<(Transaction, u16)>, SyncState)> { + let match_tx = |txs: Vec<Transaction>, conf: u16| -> Vec<(Transaction, u16)> { + txs.into_iter() + .filter_map(|tx| { + (tx.from == Some(*address) || tx.to == Some(*address)).then(|| (tx, conf)) + }) + .collect() + }; + + let mut txs = Vec::new(); + let mut removed = Vec::new(); + + // Add pending transaction + txs.extend(match_tx(self.pending_transactions()?, 0)); + + let latest = self.latest_block()?; + + let mut confirmation = 1; + let mut chain_cursor = latest.clone(); + + // Move until tip height + while chain_cursor.number.unwrap() != state.tip_height { + txs.extend(match_tx(chain_cursor.transactions, confirmation)); + chain_cursor = self.block(&chain_cursor.parent_hash)?.unwrap(); + confirmation += 1; + } + + // Check if fork + if chain_cursor.hash.unwrap() != state.tip_hash { + let mut fork_cursor = self.block(&state.tip_hash)?.unwrap(); + // Move until found common parent + while fork_cursor.hash != chain_cursor.hash { + txs.extend(match_tx(chain_cursor.transactions, confirmation)); + removed.extend(match_tx(fork_cursor.transactions, confirmation)); + chain_cursor = self.block(&chain_cursor.parent_hash)?.unwrap(); + fork_cursor = self.block(&fork_cursor.parent_hash)?.unwrap(); + confirmation += 1; + } + } + + // Move until last conf + while chain_cursor.number.unwrap() > state.conf_height { + txs.extend(match_tx(chain_cursor.transactions, confirmation)); + chain_cursor = self.block(&chain_cursor.parent_hash)?.unwrap(); + confirmation += 1; + } + + Ok(( + txs, + removed, + SyncState { + tip_hash: latest.hash.unwrap(), + tip_height: latest.number.unwrap(), + conf_height: latest + .number + .unwrap() + .saturating_sub(U64::from(min_confirmation)), + }, + )) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -119,6 +184,37 @@ impl SyncState { } } +const DEFAULT_CONFIRMATION: u16 = 24; +const DEFAULT_BOUNCE_FEE: &'static str = "ETH:0.00001"; +pub struct WireState { + pub confirmation: AtomicU16, + pub max_confirmations: u16, + pub address: H160, + pub config: EthConfig, + pub bounce_fee: U256, +} + +impl WireState { + pub fn from_taler_config(config: EthConfig) -> Self { + let init_confirmation = config.confirmation.unwrap_or(DEFAULT_CONFIRMATION); + Self { + confirmation: AtomicU16::new(init_confirmation), + max_confirmations: init_confirmation * 2, + address: eth_payto_addr(&config.payto).unwrap(), + bounce_fee: config_bounce_fee(&config), + config, + } + } +} + +fn config_bounce_fee(config: &EthConfig) -> U256 { + let config = config.bounce_fee.as_deref().unwrap_or(DEFAULT_BOUNCE_FEE); + Amount::from_str(&config) + .ok() + .and_then(|a| taler_to_eth(&a).ok()) + .expect("config value BOUNCE_FEE is no a valid ethereum amount") +} + #[cfg(test)] mod test { use common::{rand::random, rand_slice}; diff --git a/eth-wire/src/loops/worker.rs b/eth-wire/src/loops/worker.rs index c6864c2..4f8b659 100644 --- a/eth-wire/src/loops/worker.rs +++ b/eth-wire/src/loops/worker.rs @@ -29,7 +29,7 @@ use eth_wire::{ taler_util::{eth_payto_url, eth_to_taler}, SyncState, }; -use ethereum_types::{Address, H256, U256, U64}; +use ethereum_types::{Address, H256, U256}; use crate::{ fail_point::fail_point, @@ -119,73 +119,6 @@ pub fn worker(mut rpc: AutoRpcWallet, mut db: AutoReconnectDb, state: &WireState } } -/// List new and removed transaction since the last sync state, returning a new sync state -fn list_since_block_state( - rpc: &mut Rpc, - address: &Address, - state: SyncState, - min_confirmation: u16, -) -> LoopResult<(Vec<(Transaction, u16)>, Vec<(Transaction, u16)>, SyncState)> { - let match_tx = |txs: Vec<Transaction>, conf: u16| -> Vec<(Transaction, u16)> { - txs.into_iter() - .filter_map(|tx| { - (tx.from == Some(*address) || tx.to == Some(*address)).then(|| (tx, conf)) - }) - .collect() - }; - - let mut txs = Vec::new(); - let mut removed = Vec::new(); - - // Add pending transaction - txs.extend(match_tx(rpc.pending_transactions()?, 0)); - - let latest = rpc.latest_block()?; - - let mut confirmation = 1; - let mut chain_cursor = latest.clone(); - - // Move until tip height - while chain_cursor.number.unwrap() != state.tip_height { - txs.extend(match_tx(chain_cursor.transactions, confirmation)); - chain_cursor = rpc.block(&chain_cursor.parent_hash)?.unwrap(); - confirmation += 1; - } - - // Check if fork - if chain_cursor.hash.unwrap() != state.tip_hash { - let mut fork_cursor = rpc.block(&state.tip_hash)?.unwrap(); - // Move until found common parent - while fork_cursor.hash != chain_cursor.hash { - txs.extend(match_tx(chain_cursor.transactions, confirmation)); - removed.extend(match_tx(fork_cursor.transactions, confirmation)); - chain_cursor = rpc.block(&chain_cursor.parent_hash)?.unwrap(); - fork_cursor = rpc.block(&fork_cursor.parent_hash)?.unwrap(); - confirmation += 1; - } - } - - // Move until last conf - while chain_cursor.number.unwrap() > state.conf_height { - txs.extend(match_tx(chain_cursor.transactions, confirmation)); - chain_cursor = rpc.block(&chain_cursor.parent_hash)?.unwrap(); - confirmation += 1; - } - - Ok(( - txs, - removed, - SyncState { - tip_hash: latest.hash.unwrap(), - tip_height: latest.number.unwrap(), - conf_height: latest - .number - .unwrap() - .saturating_sub(U64::from(min_confirmation)), - }, - )) -} - /// Parse new transactions, return true if the database is up to date with the latest mined block fn sync_chain( rpc: &mut Rpc, @@ -199,7 +132,7 @@ fn sync_chain( let min_confirmations = state.confirmation.load(Ordering::SeqCst); let (txs, removed, next_state) = - list_since_block_state(rpc, &state.address, block, min_confirmations)?; + rpc.list_since_sync_state(&state.address, block, min_confirmations)?; // Check if a confirmed incoming transaction have been removed by a blockchain reorganisation let new_status = sync_chain_removed(&txs, &removed, db, &state.address, min_confirmations)?; @@ -520,7 +453,7 @@ fn bounce(db: &mut Client, rpc: &mut Rpc, fee: U256) -> LoopResult<bool> { "UPDATE bounce SET status=$1 WHERE id=$2", &[&(BounceStatus::Ignored as i16), &id], )?; - info!("|| (ignore) {} ", &bounced); + info!("|| (ignore) {} ", hex::encode(bounced)); } } } diff --git a/eth-wire/src/main.rs b/eth-wire/src/main.rs index 2b97c94..694c8db 100644 --- a/eth-wire/src/main.rs +++ b/eth-wire/src/main.rs @@ -14,47 +14,26 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::{path::PathBuf, str::FromStr, sync::atomic::AtomicU16}; +use std::path::PathBuf; use clap::StructOpt; use common::{ - api_common::Amount, - config::{load_eth_config, Config, CoreConfig, EthConfig}, + config::{load_eth_config, Config, CoreConfig}, named_spawn, password, postgres::{Client, NoTls}, reconnect::auto_reconnect_db, }; use eth_wire::{ rpc::{auto_rpc_common, auto_rpc_wallet, Rpc}, - taler_util::{eth_payto_addr, taler_to_eth}, - SyncState, + SyncState, WireState, }; -use ethereum_types::{H160, U256}; +use ethereum_types::H160; use loops::{analysis::analysis, watcher::watcher, worker::worker}; mod fail_point; mod loops; mod sql; -const DEFAULT_CONFIRMATION: u16 = 24; -const DEFAULT_BOUNCE_FEE: &'static str = "ETH:0.00001"; - -pub struct WireState { - confirmation: AtomicU16, - max_confirmations: u16, - address: H160, - config: EthConfig, - bounce_fee: U256, -} - -fn config_bounce_fee(config: &EthConfig) -> U256 { - let config = config.bounce_fee.as_deref().unwrap_or(DEFAULT_BOUNCE_FEE); - Amount::from_str(&config) - .ok() - .and_then(|a| taler_to_eth(&a).ok()) - .expect("config value BOUNCE_FEE is no a valid ethereum amount") -} - /// Taler wire for geth #[derive(clap::Parser, Debug)] struct Args { @@ -89,8 +68,13 @@ fn init(config: Option<PathBuf>, init: Init) { // Connect to database let mut db = Client::connect(&config.db_url, NoTls).expect("Failed to connect to database"); // Connect to ethereum node - let mut rpc = Rpc::new(config.data_dir.unwrap().join("geth.ipc")) - .expect("Failed to connect to ethereum RPC server"); + let mut rpc = Rpc::new( + config + .data_dir + .unwrap_or(PathBuf::from("/tmp/")) + .join("geth.ipc"), + ) + .expect("Failed to connect to ethereum RPC server"); match init { Init::Initdb => { @@ -111,12 +95,11 @@ fn init(config: Option<PathBuf>, init: Init) { tip_height: block.number.unwrap(), conf_height: block.number.unwrap(), }; - db - .execute( - "INSERT INTO state (name, value) VALUES ('sync', $1) ON CONFLICT (name) DO NOTHING", - &[&state.to_bytes().as_ref()], - ) - .expect("Failed to update database state"); + db.execute( + "INSERT INTO state (name, value) VALUES ('sync', $1) ON CONFLICT (name) DO NOTHING", + &[&state.to_bytes().as_ref()], + ) + .expect("Failed to update database state"); println!("Database initialised"); } Init::Initwallet => { @@ -172,18 +155,33 @@ fn init(config: Option<PathBuf>, init: Init) { fn run(config: Option<PathBuf>) { let config = load_eth_config(config.as_deref()); - let init_confirmation = config.confirmation.unwrap_or(DEFAULT_CONFIRMATION); - let state: &'static WireState = Box::leak(Box::new(WireState { - confirmation: AtomicU16::new(init_confirmation), - max_confirmations: init_confirmation * 2, - address: eth_payto_addr(&config.payto).unwrap(), - bounce_fee: config_bounce_fee(&config), - config, - })); - - let rpc_worker = auto_rpc_wallet(state.config.core.data_dir.clone().unwrap(), state.address); - let rpc_analysis = auto_rpc_common(state.config.core.data_dir.clone().unwrap()); - let rpc_watcher = auto_rpc_common(state.config.core.data_dir.clone().unwrap()); + let state: &'static _ = Box::leak(Box::new(WireState::from_taler_config(config))); + + let rpc_worker = auto_rpc_wallet( + state + .config + .core + .data_dir + .clone() + .unwrap_or(PathBuf::from("/tmp/")), + state.address, + ); + let rpc_analysis = auto_rpc_common( + state + .config + .core + .data_dir + .clone() + .unwrap_or(PathBuf::from("/tmp/")), + ); + let rpc_watcher = auto_rpc_common( + state + .config + .core + .data_dir + .clone() + .unwrap_or(PathBuf::from("/tmp/")), + ); let db_watcher = auto_reconnect_db(state.config.core.db_url.clone()); let db_analysis = auto_reconnect_db(state.config.core.db_url.clone()); diff --git a/eth-wire/src/rpc.rs b/eth-wire/src/rpc.rs index 1ee31e4..4dadd8b 100644 --- a/eth-wire/src/rpc.rs +++ b/eth-wire/src/rpc.rs @@ -224,6 +224,13 @@ impl Rpc { } } + pub fn get_transaction_receipt(&mut self, hash: &H256) -> Result<Option<TransactionReceipt>> { + match self.call("eth_getTransactionReceipt", &[hash]) { + Err(Error::Null) => Ok(None), + r => r, + } + } + pub fn fill_transaction(&mut self, params: &TransactionRequest) -> Result<Filled> { self.call("eth_fillTransaction", &[params]) } @@ -271,7 +278,7 @@ impl Rpc { self.call("eth_getBlockByNumber", &("earliest", &true)) } - pub fn balance(&mut self, addr: &Address) -> Result<U256> { + pub fn get_balance(&mut self, addr: &Address) -> Result<U256> { self.call("eth_getBalance", &(addr, "latest")) } @@ -364,6 +371,17 @@ pub struct Transaction { pub input: Hex, } +/// Description of a Transaction, pending or in the chain. +#[derive(Debug, Clone, serde::Deserialize)] +pub struct TransactionReceipt { + /// Gas used by this transaction alone. + #[serde(rename = "gasUsed")] + pub gas_used: U256, + /// Effective gas price + #[serde(rename = "effectiveGasPrice")] + pub effective_gas_price: Option<U256>, +} + /// Fill result #[derive(Debug, serde::Deserialize)] pub struct Filled { @@ -377,7 +395,10 @@ pub struct FilledGas { pub gas: U256, /// Gas price #[serde(rename = "gasPrice")] - pub gas_price: U256, + pub gas_price: Option<U256>, + /// Max fee per gas + #[serde(rename = "maxFeePerGas")] + pub max_fee_per_gas: Option<U256>, } /// Send Transaction Parameters @@ -389,11 +410,7 @@ pub struct TransactionRequest { pub to: Address, /// Transferred value pub value: U256, - /// Supplied gas (None for sensible default) - #[serde(skip_serializing_if = "Option::is_none")] - pub gas: Option<U256>, /// Gas price (None for sensible default) - #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "gasPrice")] pub gas_price: Option<U256>, /// Transaction data diff --git a/eth-wire/src/taler_util.rs b/eth-wire/src/taler_util.rs index 7f9fef1..b5db5df 100644 --- a/eth-wire/src/taler_util.rs +++ b/eth-wire/src/taler_util.rs @@ -18,8 +18,8 @@ use std::str::FromStr; use ethereum_types::{Address, U256}; use common::{api_common::Amount, url::Url}; -const WEI: u64 = 1_000_000_000_000_000_000; -const TRUNC: u64 = 10_000_000_000; +pub const WEI: u64 = 1_000_000_000_000_000_000; +pub const TRUNC: u64 = 10_000_000_000; /// Generate a payto uri from an eth address pub fn eth_payto_url(addr: &Address) -> Url { |