summaryrefslogtreecommitdiff
path: root/eth-wire
diff options
context:
space:
mode:
authorAntoine A <>2022-02-15 16:05:04 +0100
committerAntoine A <>2022-02-15 16:58:41 +0100
commit88556bdbfa2140e8d11367cf87601df0c70f0604 (patch)
tree39ad5eed91fb8d074c5fa668840d533bfbe43616 /eth-wire
parent187f435336d86935a92b4d55a5a17cd5e24b9aab (diff)
downloaddepolymerization-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.toml4
-rw-r--r--eth-wire/README.md38
-rw-r--r--eth-wire/src/bin/eth-wire-cli.rs102
-rw-r--r--eth-wire/src/bin/eth-wire-utils.rs200
-rw-r--r--eth-wire/src/main.rs112
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),