diff options
author | Antoine A <> | 2022-02-15 16:05:04 +0100 |
---|---|---|
committer | Antoine A <> | 2022-02-15 16:58:41 +0100 |
commit | 88556bdbfa2140e8d11367cf87601df0c70f0604 (patch) | |
tree | 39ad5eed91fb8d074c5fa668840d533bfbe43616 /eth-wire | |
parent | 187f435336d86935a92b4d55a5a17cd5e24b9aab (diff) | |
download | depolymerization-88556bdbfa2140e8d11367cf87601df0c70f0604.tar.gz depolymerization-88556bdbfa2140e8d11367cf87601df0c70f0604.tar.bz2 depolymerization-88556bdbfa2140e8d11367cf87601df0c70f0604.zip |
Replace argh CLI parser with clap, merge wire binary with its bootstrap and improve config error msg
Diffstat (limited to 'eth-wire')
-rw-r--r-- | eth-wire/Cargo.toml | 4 | ||||
-rw-r--r-- | eth-wire/README.md | 38 | ||||
-rw-r--r-- | eth-wire/src/bin/eth-wire-cli.rs | 102 | ||||
-rw-r--r-- | eth-wire/src/bin/eth-wire-utils.rs | 200 | ||||
-rw-r--r-- | eth-wire/src/main.rs | 112 |
5 files changed, 223 insertions, 233 deletions
diff --git a/eth-wire/Cargo.toml b/eth-wire/Cargo.toml index 644c246..5c44f6e 100644 --- a/eth-wire/Cargo.toml +++ b/eth-wire/Cargo.toml @@ -10,8 +10,8 @@ rust-version = "1.56.1" fail = [] [dependencies] -# Cli args -argh = "0.1.7" +# Cli args +clap = { version = "3.0.14", features = ["derive"] } # Serialization library serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.78" diff --git a/eth-wire/README.md b/eth-wire/README.md new file mode 100644 index 0000000..d170735 --- /dev/null +++ b/eth-wire/README.md @@ -0,0 +1,38 @@ +# eth-wire + +eth-wire is taler wire implementation for [geth](https://geth.ethereum.org/) node + +## Geth compatibility + +Geth version 1.10.* is required + +## Install + +`cargo install --path eth-wire` + +## Configuration + +### taler.conf + +The configuration is based on [taler.conf](https://docs.taler.net/manpages/taler.conf.5.html) + +``` ini +# taler.conf - eth-wire default config +[depolymerizer-ethereum] +DATA_DIR = +CONFIRMATION = 24 +BOUNCE_FEE = ETH:0.00001 +``` + +## Getting Started + +1. Write configuration files +2. Start geth +3. Start PostgreSQL +4. Initialize database `eth-wire initdb` +5. Initialize wallet `eth-wire initwallet` +6. Update configuration files with wallet info +7. Run wire-gateway `wire-gateway` +8. Run eth-wire `eth-wire` + +## Implementation details diff --git a/eth-wire/src/bin/eth-wire-cli.rs b/eth-wire/src/bin/eth-wire-cli.rs deleted file mode 100644 index ce8aa93..0000000 --- a/eth-wire/src/bin/eth-wire-cli.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ - -use common::{ - config::{Config, CoreConfig}, - log::init, - postgres::{Client, NoTls}, -}; -use eth_wire::{rpc::Rpc, SyncState}; -use ethereum_types::H160; - -fn main() { - init(); - let args: Vec<_> = std::env::args().collect(); - let config = CoreConfig::load_taler_config(Some(&args[2])); - assert_eq!(config.currency, "ETH"); - let mut db = Client::connect(&config.db_url, NoTls).expect("Failed to connect to database"); - - // TODO user defined password - - match args[1].as_str() { - "initdb" => { - // Load schema - db.batch_execute(include_str!("../../../db/eth.sql")) - .expect("Failed to load database schema"); - // Init status to true - db - .execute( - "INSERT INTO state (name, value) VALUES ('status', $1) ON CONFLICT (name) DO NOTHING", - &[&[1u8].as_ref()], - ) - .expect("Failed to initialise database state"); - println!("Database initialised"); - } - "initwallet" => { - // Connect to ethereum node - let mut rpc = Rpc::new(config.data_dir.unwrap().join("geth.ipc")) - .expect("Failed to connect to ethereum RPC server"); - - // Skip previous blocks - let block = rpc.latest_block().expect("Failed to get current block"); - let state = SyncState { - tip_hash: block.hash.unwrap(), - tip_height: block.number.unwrap(), - conf_height: block.number.unwrap(), - }; - let nb_row = 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"); - if nb_row > 0 { - println!("Skipped {} previous block", state.conf_height); - } - - let prev_addr = db - .query_opt("SELECT value FROM state WHERE name = 'addr'", &[]) - .expect("Failed to query database state"); - let (addr, created) = if let Some(row) = prev_addr { - (H160::from_slice(row.get(0)), false) - } else { - // Or generate a new one - let new = rpc - .new_account("password") - .expect("Failed creating account"); - db.execute( - "INSERT INTO state (name, value) VALUES ('addr', $1)", - &[&new.as_bytes()], - ) - .expect("Failed to update database state"); - (new, true) - }; - - if created { - println!("Created new wallet"); - } else { - println!("Found already existing wallet") - }; - - let addr = hex::encode(addr.as_bytes()); - println!("Address is {}", &addr); - println!("Add the following line into taler.conf:"); - println!("[depolymerizer-ethereum]"); - println!("PAYTO = payto://ethereum/{}", addr); - } - cmd => panic!("Unknown command {}", cmd), - } -} diff --git a/eth-wire/src/bin/eth-wire-utils.rs b/eth-wire/src/bin/eth-wire-utils.rs index 86d03f3..1da44d3 100644 --- a/eth-wire/src/bin/eth-wire-utils.rs +++ b/eth-wire/src/bin/eth-wire-utils.rs @@ -13,8 +13,12 @@ You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::{path::PathBuf, str::FromStr}; +use std::{ + path::{Path, PathBuf}, + str::FromStr, +}; +use clap::StructOpt; use common::{ api_common::Amount, config::{Config, CoreConfig}, @@ -29,138 +33,87 @@ use eth_wire::{ }; use ethereum_types::{H160, U256}; -#[derive(argh::FromArgs)] -/// Euthereum wire test client +#[derive(clap::Parser, Debug)] +#[clap(name = "eth-wire-utils")] +/// eth-wire test utils struct Args { - #[argh(option, short = 'd')] - /// specify data directory + /// Override default configuration file path + #[clap(global = true, short, long)] + config: Option<PathBuf>, + /// Override default data directory path + #[clap(global = true, short, long)] datadir: Option<PathBuf>, - #[argh(subcommand)] + #[clap(subcommand)] cmd: Cmd, } -#[derive(argh::FromArgs)] -#[argh(subcommand)] -enum Cmd { - Send(SendCmd), - Deposit(DepositCmd), - Mine(MineCmd), - ResetDb(ResetCmd), - Balance(BalanceCmd), - Connect(ConnectCmd), - Disconnect(DisconnectCmd), - Abandon(AbandonCmd), -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "send")] -/// Send a transaction -struct SendCmd { - #[argh(positional)] - /// sender wallet - from: String, - - #[argh(positional)] - /// receiver wallet - to: String, - - #[argh(positional)] - /// sender wallet - fmt: String, - - #[argh(positional)] - /// amounts to send in eth - amounts: Vec<u32>, -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "deposit")] -/// Perform a deposit transaction -struct DepositCmd { - #[argh(positional)] +#[derive(clap::Parser, Debug)] +struct TransactionCmd { /// sender wallet from: String, - - #[argh(positional)] /// receiver wallet to: String, - - #[argh(positional)] /// sender wallet fmt: String, - - #[argh(positional)] /// amounts to send in eth amounts: Vec<u32>, } -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "mine")] -/// Wait or mine the next block -struct MineCmd { - #[argh(positional)] - /// receiver wallet - to: String, - #[argh(positional, default = "0")] - /// amount to mine in eth - amount: u64, -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "resetdb")] -/// Clear database -struct ResetCmd { - #[argh(positional)] - /// taler config - config: String, -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "balance")] -/// Get eth balance -struct BalanceCmd { - #[argh(positional)] - /// account address - addr: String, -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "connect")] -/// Add a peer -struct ConnectCmd { - #[argh(positional)] - /// peer datadir - datadir: PathBuf, -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "disconnect")] -/// Remove a peer -struct DisconnectCmd { - #[argh(positional)] - /// peer datadir - datadir: PathBuf, -} - -#[derive(argh::FromArgs)] -#[argh(subcommand, name = "abandon")] -/// Abandon all unconfirmed transaction -struct AbandonCmd { - #[argh(positional)] - /// sender address - from: String, +#[derive(clap::Subcommand, Debug)] +enum Cmd { + /// Send common ethereum transactions + Send(TransactionCmd), + /// Send taler deposit transactions + Deposit(TransactionCmd), + /// Mine pending transactions and more blocks + Mine { + /// receiver wallet + to: String, + #[clap(default_value_t = 0)] + /// amount to mine in eth + amount: u64, + }, + /// Clear database + Resetdb, + /// Get eth balance + Balance { + /// account address + addr: String, + }, + /// Add a peer + Connect { + /// peer datadir + datadir: PathBuf, + }, + /// Remove a peer + Disconnect { + /// peer datadir + datadir: PathBuf, + }, + /// Abandon all unconfirmed transaction + Abandon { + /// sender address + from: String, + }, } fn main() { init(); - let args: Args = argh::from_env(); - let mut rpc = Rpc::new(args.datadir.unwrap().join("geth.ipc")).unwrap(); + let args: Args = Args::parse(); + let config = args + .config + .map(|path| CoreConfig::load_taler_config(Some(&path), Some("ETH"))); + let data_dir: Option<&Path> = config + .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(); match args.cmd { - Cmd::Deposit(DepositCmd { + Cmd::Deposit(TransactionCmd { from, to, - amounts, fmt, + amounts, }) => { let from = H160::from_str(&from).unwrap(); let to = H160::from_str(&to).unwrap(); @@ -171,7 +124,7 @@ fn main() { rpc.deposit(from, to, value, rand_slice()).unwrap(); } } - Cmd::Send(SendCmd { + Cmd::Send(TransactionCmd { from, to, fmt, @@ -195,7 +148,7 @@ fn main() { .unwrap(); } } - Cmd::Mine(MineCmd { to, mut amount }) => { + Cmd::Mine { to, mut amount } => { let to = H160::from_str(&to).unwrap(); rpc.unlock_account(&to, "password").ok(); let mut notifier = rpc.subscribe_new_head().unwrap(); @@ -210,15 +163,9 @@ fn main() { } rpc.miner_stop().unwrap(); } - Cmd::Balance(BalanceCmd { addr }) => { - let addr = H160::from_str(&addr).unwrap(); - let balance = rpc.balance(&addr).unwrap(); - println!("{}", (balance / 10_000_000_000u64).as_u64()); - } - Cmd::ResetDb(ResetCmd { config }) => { - let config = CoreConfig::load_taler_config(Some(&config)); + Cmd::Resetdb => { let block = rpc.earliest_block().unwrap(); - let mut db = Client::connect(&config.db_url, NoTls).unwrap(); + let mut db = Client::connect(&config.unwrap().db_url, NoTls).unwrap(); let mut tx = db.transaction().unwrap(); // Clear transaction tables and reset state tx.execute("DELETE FROM tx_in", &[]).unwrap(); @@ -237,21 +184,26 @@ fn main() { .unwrap(); tx.commit().unwrap(); } - Cmd::Connect(ConnectCmd { datadir }) => { + Cmd::Balance { addr } => { + let addr = H160::from_str(&addr).unwrap(); + let balance = rpc.balance(&addr).unwrap(); + println!("{}", (balance / 10_000_000_000u64).as_u64()); + } + Cmd::Connect { datadir } => { let mut peer = Rpc::new(datadir.join("geth.ipc")).unwrap(); let mut enode = peer.node_info().unwrap().enode; // Replace ip with localhost because it is broken enode.set_host(Some("127.0.0.1")).unwrap(); assert!(rpc.add_peer(&enode).unwrap()); } - Cmd::Disconnect(DisconnectCmd { datadir }) => { + Cmd::Disconnect { datadir } => { let mut peer = Rpc::new(datadir.join("geth.ipc")).unwrap(); let mut enode = peer.node_info().unwrap().enode; // Replace ip with localhost because it is broken enode.set_host(Some("127.0.0.1")).unwrap(); assert!(rpc.remove_peer(&enode).unwrap()); } - Cmd::Abandon(AbandonCmd { from }) => { + Cmd::Abandon { from } => { let from = H160::from_str(&from).unwrap(); rpc.unlock_account(&from, "password").ok(); let pending = rpc.pending_transactions().unwrap(); @@ -262,7 +214,7 @@ fn main() { to: tx.to.unwrap(), value: U256::zero(), gas: None, - gas_price: Some(U256::from(1)), // Bigger gas price to replace fee + 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/main.rs b/eth-wire/src/main.rs index fc3e947..14da742 100644 --- a/eth-wire/src/main.rs +++ b/eth-wire/src/main.rs @@ -14,17 +14,20 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::{str::FromStr, sync::atomic::AtomicU16}; +use std::{path::PathBuf, str::FromStr, sync::atomic::AtomicU16}; +use clap::StructOpt; use common::{ api_common::Amount, - config::{load_eth_config, EthConfig}, + config::{load_eth_config, Config, CoreConfig, EthConfig}, named_spawn, + postgres::{Client, NoTls}, reconnect::auto_reconnect_db, }; use eth_wire::{ - rpc::auto_reconnect_rpc, + rpc::{auto_reconnect_rpc, Rpc}, taler_util::{eth_payto_addr, taler_to_eth}, + SyncState, }; use ethereum_types::{H160, U256}; use loops::{analysis::analysis, watcher::watcher, worker::worker}; @@ -52,12 +55,111 @@ fn config_bounce_fee(config: &EthConfig) -> U256 { .expect("config value BOUNCE_FEE is no a valid ethereum amount") } +/// Taler wire for geth +#[derive(clap::Parser, Debug)] +struct Args { + /// Override default configuration file path + #[clap(global = true, short, long)] + config: Option<PathBuf>, + + #[clap(subcommand)] + init: Option<Init>, +} + +#[derive(clap::Subcommand, Debug)] +enum Init { + /// Initialize database schema and state + Initdb, + /// Generate ethereum wallet and initialize state + Initwallet, +} + fn main() { common::log::init(); - let path = std::env::args().nth(1).unwrap(); - let config = load_eth_config(Some(&path)); + let args = Args::parse(); + + match args.init { + Some(cmd) => init(args.config, cmd), + None => run(args.config), + } +} + +fn init(config: Option<PathBuf>, init: Init) { + let config = CoreConfig::load_taler_config(config.as_deref(), Some("ETH")); + let mut db = Client::connect(&config.db_url, NoTls).expect("Failed to connect to database"); + + match init { + Init::Initdb => { + // Load schema + db.batch_execute(include_str!("../../db/eth.sql")) + .expect("Failed to load database schema"); + // Init status to true + db + .execute( + "INSERT INTO state (name, value) VALUES ('status', $1) ON CONFLICT (name) DO NOTHING", + &[&[1u8].as_ref()], + ) + .expect("Failed to initialise database state"); + println!("Database initialised"); + } + Init::Initwallet => { + // Connect to ethereum node + let mut rpc = Rpc::new(config.data_dir.unwrap().join("geth.ipc")) + .expect("Failed to connect to ethereum RPC server"); + + // Skip previous blocks + let block = rpc.latest_block().expect("Failed to get current block"); + let state = SyncState { + tip_hash: block.hash.unwrap(), + tip_height: block.number.unwrap(), + conf_height: block.number.unwrap(), + }; + let nb_row = 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"); + if nb_row > 0 { + println!("Skipped {} previous block", state.conf_height); + } + + let prev_addr = db + .query_opt("SELECT value FROM state WHERE name = 'addr'", &[]) + .expect("Failed to query database state"); + let (addr, created) = if let Some(row) = prev_addr { + (H160::from_slice(row.get(0)), false) + } else { + // Or generate a new one + let new = rpc + .new_account("password") + .expect("Failed creating account"); + db.execute( + "INSERT INTO state (name, value) VALUES ('addr', $1)", + &[&new.as_bytes()], + ) + .expect("Failed to update database state"); + (new, true) + }; + + if created { + println!("Created new wallet"); + } else { + println!("Found already existing wallet") + }; + + let addr = hex::encode(addr.as_bytes()); + println!("Address is {}", &addr); + println!("Add the following line into taler.conf:"); + println!("[depolymerizer-ethereum]"); + println!("PAYTO = payto://ethereum/{}", addr); + } + } +} +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), |