gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-depolymerization] branch master updated (3e080b2 -> 88556bd)


From: gnunet
Subject: [taler-depolymerization] branch master updated (3e080b2 -> 88556bd)
Date: Tue, 15 Feb 2022 18:33:53 +0100

This is an automated email from the git hooks/post-receive script.

antoine pushed a change to branch master
in repository depolymerization.

    from 3e080b2  Use taler amount format for bounce fee in config
     new 187f435  btc-wire: metadata doc draft
     new 88556bd  Replace argh CLI parser with clap, merge wire binary with its 
bootstrap and improve config error msg

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Cargo.lock                         | 151 +++++++++++++++++++---------
 README.md                          |  17 +++-
 btc-wire/Cargo.toml                |   6 +-
 btc-wire/README.md                 |  31 ++++--
 btc-wire/src/bin/btc-wire-cli.rs   | 113 ---------------------
 btc-wire/src/bin/btc-wire-utils.rs | 194 +++++++++++++++--------------------
 btc-wire/src/bin/segwit-demo.rs    |  56 +++++++++++
 btc-wire/src/main.rs               | 122 ++++++++++++++++++++--
 btc-wire/src/segwit.rs             |   2 +-
 common/src/config.rs               |  71 ++++++++-----
 common/src/lib.rs                  |   1 +
 common/src/log.rs                  |   2 +
 eth-wire/Cargo.toml                |   4 +-
 eth-wire/README.md                 |  38 +++++++
 eth-wire/src/bin/eth-wire-cli.rs   | 102 -------------------
 eth-wire/src/bin/eth-wire-utils.rs | 200 ++++++++++++++-----------------------
 eth-wire/src/main.rs               | 112 ++++++++++++++++++++-
 makefile                           |  19 ++--
 script/prepare.sh                  |   4 +-
 test/common.sh                     |  28 +++---
 test/conf/taler_btc_lifetime.conf  |   1 -
 uri-pack/README.md                 |   2 +-
 wire-gateway/src/main.rs           |  10 +-
 23 files changed, 710 insertions(+), 576 deletions(-)
 delete mode 100644 btc-wire/src/bin/btc-wire-cli.rs
 create mode 100644 btc-wire/src/bin/segwit-demo.rs
 create mode 100644 eth-wire/README.md
 delete mode 100644 eth-wire/src/bin/eth-wire-cli.rs

diff --git a/Cargo.lock b/Cargo.lock
index fdb75e4..2c82bf6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,35 +23,6 @@ dependencies = [
  "memchr",
 ]
 
-[[package]]
-name = "argh"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "dbb41d85d92dfab96cb95ab023c265c5e4261bb956c0fb49ca06d90c570f1958"
-dependencies = [
- "argh_derive",
- "argh_shared",
-]
-
-[[package]]
-name = "argh_derive"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "be69f70ef5497dd6ab331a50bd95c6ac6b8f7f17a7967838332743fbd58dc3b5"
-dependencies = [
- "argh_shared",
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "argh_shared"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e6f8c380fa28aa1b36107cd97f0196474bb7241bb95a453c5c01a15ac74b2eac"
-
 [[package]]
 name = "async-trait"
 version = "0.1.52"
@@ -150,12 +121,13 @@ dependencies = [
 name = "btc-wire"
 version = "0.1.0"
 dependencies = [
- "argh",
  "base64",
  "bech32",
  "bitcoin",
+ "clap 3.0.14",
  "common",
  "criterion",
+ "hex",
  "rust-ini",
  "serde",
  "serde_json",
@@ -222,10 +194,40 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
 dependencies = [
  "bitflags",
- "textwrap",
+ "textwrap 0.11.0",
  "unicode-width",
 ]
 
+[[package]]
+name = "clap"
+version = "3.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "indexmap",
+ "lazy_static",
+ "os_str_bytes",
+ "strsim",
+ "termcolor",
+ "textwrap 0.14.2",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "common"
 version = "0.1.0"
@@ -261,7 +263,7 @@ checksum = 
"1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
 dependencies = [
  "atty",
  "cast",
- "clap",
+ "clap 2.34.0",
  "criterion-plot",
  "csv",
  "itertools",
@@ -478,7 +480,7 @@ dependencies = [
 name = "eth-wire"
 version = "0.1.0"
 dependencies = [
- "argh",
+ "clap 3.0.14",
  "common",
  "ethereum-types",
  "hex",
@@ -702,14 +704,17 @@ dependencies = [
  "ahash",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
 [[package]]
 name = "heck"
-version = "0.3.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
 
 [[package]]
 name = "hermit-abi"
@@ -831,6 +836,16 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "indexmap"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.11.2",
+]
+
 [[package]]
 name = "instant"
 version = "0.1.12"
@@ -878,9 +893,9 @@ checksum = 
"e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.117"
+version = "0.2.118"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
+checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
 
 [[package]]
 name = "listenfd"
@@ -1032,7 +1047,16 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485"
 dependencies = [
  "dlv-list",
- "hashbrown",
+ "hashbrown 0.9.1",
+]
+
+[[package]]
+name = "os_str_bytes"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+dependencies = [
+ "memchr",
 ]
 
 [[package]]
@@ -1204,6 +1228,30 @@ dependencies = [
  "uint",
 ]
 
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.36"
@@ -1573,6 +1621,15 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "textwrap"
 version = "0.11.0"
@@ -1582,6 +1639,12 @@ dependencies = [
  "unicode-width",
 ]
 
+[[package]]
+name = "textwrap"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+
 [[package]]
 name = "thiserror"
 version = "1.0.30"
@@ -1783,12 +1846,6 @@ dependencies = [
  "tinyvec",
 ]
 
-[[package]]
-name = "unicode-segmentation"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
-
 [[package]]
 name = "unicode-width"
 version = "0.1.9"
diff --git a/README.md b/README.md
index c3b676d..9640ec1 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,26 @@
 - **docs**: Documentation files
 - **test**: Test scripts
 
+## Install
+
+```
+make install
+```
+
+Read implementation specific documentation to run depolymerizer:
+
+- [BTC](btc-wire/README.md)
+- [ETH](eth-wire/README.md)
+
+
 ## Configuration
 
 The configuration is based on 
[taler.conf](https://docs.taler.net/manpages/taler.conf.5.html).
 
+This is the minimal required config for initialization.
+
 ``` ini
-# taler.conf - common config
+# taler.conf - required config (fill all ___)
 [taler]
 CURRENCY = ___ 
 
@@ -22,7 +36,6 @@ BASE_URL = https://___
 
 [depolymerizer-___]
 DB_URL   = postgres://___
-PAYTO    = payto://___
 ```
 
 ### Process lifetime
diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml
index 9d3a9aa..243d045 100644
--- a/btc-wire/Cargo.toml
+++ b/btc-wire/Cargo.toml
@@ -15,8 +15,8 @@ bitcoin = { version = "0.27.1", features = [
     "std",
     "use-serde",
 ], default-features = false }
-# Cli args 
-argh = "0.1.7"
+# Cli args parser
+clap = { version = "3.0.14", features = ["derive"] }
 # Bech32 encoding and decoding
 bech32 = "0.8.1"
 # Serialization library
@@ -32,6 +32,8 @@ base64 = "0.13.0"
 common = { path = "../common" }
 # Ini parser
 rust-ini = "0.17.0"
+# Hexadecimal encoding
+hex = "0.4.3"
 
 [dev-dependencies]
 # statistics-driven micro-benchmarks
diff --git a/btc-wire/README.md b/btc-wire/README.md
index 74a2738..f8823e0 100644
--- a/btc-wire/README.md
+++ b/btc-wire/README.md
@@ -4,7 +4,7 @@ btc-wire is taler wire implementation for 
[bitcoincore](https://bitcoincore.org/
 
 ## Bitcoincore compatibility
 
-Bitcoind version >= 22.0 is required
+Bitcoind version 22.* is required
 
 ## Install 
 
@@ -17,11 +17,11 @@ Bitcoind version >= 22.0 is required
 The configuration is based on 
[taler.conf](https://docs.taler.net/manpages/taler.conf.5.html)
 
 ``` ini
-# taler.conf - btc-wire config
+# taler.conf - btc-wire default config
 [depolymerizer-bitcoin]
 DATA_DIR     =
 CONFIRMATION = 6
-BOUNCE_FEE   = 1000
+BOUNCE_FEE   = BTC:0.00001
 ```
 
 ### bitcoin.conf
@@ -34,12 +34,31 @@ the RPC server, `txindex=1` and `maxtxfee` are mandatory.
 1. Write configuration files
 2. Start bitcoind
 3. Start PostgreSQL
-4. Initialize database `btc-wire-cli initdb`
-5. Initialize wallet `btc-wire-cli initwallet`
-6. Update taler.conf with wallet info
+4. Initialize database `btc-wire initdb`
+5. Initialize wallet `btc-wire initwallet`
+6. Update configuration files with wallet info
 7. Run wire-gateway `wire-gateway`
 8. Run btc-wire `btc-wire`
 
+## Deposit metadata format
+
+Starting from a bitcoin payto URI you will have to generate fake segwit
+addresses to encode the reserve public key as metadata into a common bitcoin
+transaction.
+
+A single segwit address can contain 20B of chosen data and the reserve pub key
+is 32B. We use two fake adresses consisting of the two key half prepended
+with the same random pattern, at the exception of the first bit with must be 0
+for the first half and 1 for the second one. You must then send a single
+transaction with the three addresses as recipients.
+
+As a few lines of code can carry more meaning that many words you can find a
+[simple rust example](src/bin/segwit-demo.rs) in this project and run it with
+`make segwit_demo`.
+
+Segwit addresses are encoded using a bitcoin specific format: [bech32](
+[bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki))
+
 ## Implementation details
 
 ### Stuck transaction
diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs
deleted file mode 100644
index 9438230..0000000
--- a/btc-wire/src/bin/btc-wire-cli.rs
+++ /dev/null
@@ -1,113 +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 btc_wire::{
-    config::{BitcoinConfig, WIRE_WALLET_NAME},
-    rpc::{Error, ErrorCode, Rpc},
-    rpc_utils::default_data_dir,
-};
-use common::{
-    config::{Config, CoreConfig},
-    log::init,
-    postgres::{Client, NoTls},
-};
-
-fn main() {
-    init();
-    let args: Vec<_> = std::env::args().collect();
-    // Parse taler config
-    let config = CoreConfig::load_taler_config(Some(&args[2]));
-    assert_eq!(config.currency, "BTC");
-    // Connect to database
-    let mut db = Client::connect(&config.db_url, NoTls).expect("Failed to 
connect to database");
-
-    match args[1].as_str() {
-        "initdb" => {
-            // Load schema
-            db.batch_execute(include_str!("../../../db/btc.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" => {
-            // Parse bitcoin config
-            let btc_conf = 
BitcoinConfig::load(config.data_dir.unwrap_or_else(default_data_dir))
-                .expect("Failed to load bitcoin configuration");
-            // Connect to bitcoin node
-            let mut rpc = Rpc::common(&btc_conf).expect("Failed to connect to 
bitcoin RPC server");
-
-            // Skip previous blocks
-            let info = rpc
-                .get_blockchain_info()
-                .expect("Failed to get blockchain info");
-            let nb_row = db
-                .execute(
-                    "INSERT INTO state (name, value) VALUES ('last_hash', $1) 
ON CONFLICT (name) DO NOTHING",
-                    &[&info.best_block_hash.as_ref()],
-                )
-                .expect("Failed to update database state");
-            if nb_row > 0 {
-                println!("Skip previous block until now");
-            }
-
-            // Create wallet
-            let created = match rpc.create_wallet(WIRE_WALLET_NAME) {
-                Err(Error::RPC { code, .. }) if code == 
ErrorCode::RpcWalletError => false,
-                Err(e) => panic!("{}", e),
-                Ok(_) => true,
-            };
-
-            rpc.load_wallet(WIRE_WALLET_NAME).ok();
-
-            // Load previous address
-            let prev_addr = db
-                .query_opt("SELECT value FROM state WHERE name = 'addr'", &[])
-                .expect("Failed to query database state");
-            let addr = if let Some(row) = prev_addr {
-                String::from_utf8(row.get(0)).expect("Stored address is not a 
valid string")
-            } else {
-                // Or generate a new one
-                let new = Rpc::wallet(&btc_conf, WIRE_WALLET_NAME)
-                    .expect("Failed to connect to wallet bitcoin RPC server")
-                    .get_new_address()
-                    .expect("Failed to generate new address")
-                    .to_string();
-                db.execute(
-                    "INSERT INTO state (name, value) VALUES ('addr', $1)",
-                    &[&new.as_bytes()],
-                )
-                .expect("Failed to update database state");
-                new
-            };
-
-            if created {
-                println!("Created new wallet");
-            } else {
-                println!("Found already existing wallet")
-            }
-            println!("Address is {}", &addr);
-            println!("Add the following line into taler.conf:");
-            println!("[depolymerizer-bitcoin]");
-            println!("PAYTO = payto://bitcoin/{}", addr);
-        }
-        cmd => panic!("Unknown command {}", cmd),
-    }
-}
diff --git a/btc-wire/src/bin/btc-wire-utils.rs 
b/btc-wire/src/bin/btc-wire-utils.rs
index c69ae45..2354f65 100644
--- a/btc-wire/src/bin/btc-wire-utils.rs
+++ b/btc-wire/src/bin/btc-wire-utils.rs
@@ -21,140 +21,109 @@ use btc_wire::{
     rpc::{Category, Error, ErrorCode, Rpc},
     rpc_utils::default_data_dir,
 };
+use clap::StructOpt;
 use common::{
     config::{Config, CoreConfig},
     postgres::{Client, NoTls},
-    rand_slice, log::init,
+    rand_slice,
 };
 
-#[derive(argh::FromArgs)]
-/// Bitcoin wire test client
+/// btc-wire test utils
+#[derive(clap::Parser, Debug)]
+#[clap(name = "btc-wire-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)]
+#[derive(clap::Subcommand, Debug)]
 enum Cmd {
-    Transfer(TransferCmd),
-    NextBlock(NextBlockCmd),
-    Abandon(AbandonCmd),
-    ResetDB(ResetCmd),
+    /// Wait or mine the next block
+    Transfer {
+        #[clap(short, long, default_value_t = String::from("client"))]
+        /// sender wallet
+        from: String,
+        #[clap(short, long, default_value_t = String::from("wire"))]
+        /// receiver wallet
+        to: String,
+        /// amount to send in btc
+        amount: f64,
+    },
+    /// Wait or mine the next block
+    Nblock {
+        #[clap(default_value_t = String::from("wire"))]
+        /// receiver wallet
+        to: String,
+    },
+    /// Abandon all unconfirmed transaction
+    Abandon {
+        #[clap(default_value_t = String::from("wire"))]
+        /// sender wallet
+        from: String,
+    },
+    /// Clear database
+    Resetdb,
 }
 
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "transfer")]
-/// Wait or mine the next block
-struct TransferCmd {
-    #[argh(option, short = 'f', default = "String::from(\"client\")")]
-    /// sender wallet
-    from: String,
-
-    #[argh(option, short = 't', default = "String::from(\"wire\")")]
-    /// receiver wallet
-    to: String,
-
-    #[argh(positional)]
-    /// amount to send in btc
-    amount: f64,
-}
-
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "nblock")]
-/// Wait or mine the next block
-struct NextBlockCmd {
-    #[argh(positional, default = "String::from(\"wire\")")]
-    /// receiver wallet
-    to: String,
-}
-
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "abandon")]
-/// Abandon all unconfirmed transaction
-struct AbandonCmd {
-    #[argh(positional, default = "String::from(\"wire\")")]
-    /// sender wallet
-    from: String,
-}
-
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "resetdb")]
-/// Clear database
-struct ResetCmd {
-    #[argh(positional)]
-    /// taler config
-    config: String,
-}
-
-struct App {
-    config: BitcoinConfig,
-    rpc: Rpc,
-}
-
-impl App {
-    pub fn start(data_dir: Option<PathBuf>) -> Self {
-        let data_dir = data_dir.unwrap_or_else(default_data_dir);
-        let config = BitcoinConfig::load(data_dir).unwrap();
-        let client = Rpc::common(&config).unwrap();
-
-        Self {
-            config,
-            rpc: client,
-        }
-    }
-
-    pub fn auto_wallet(&mut self, name: &str) -> (Rpc, Address) {
-        // Auto load
-        if let Err(err) = self.rpc.load_wallet(name) {
-            match err {
-                Error::RPC { code, .. } if code == 
ErrorCode::RpcWalletAlreadyLoaded => {}
-                e => Err(e).unwrap(),
-            }
-        }
-        let mut wallet = Rpc::wallet(&self.config, name).unwrap();
-        let addr = wallet
-            .get_new_address()
-            .unwrap_or_else(|_| panic!("Failed to get wallet address {}", 
name));
-        (wallet, addr)
-    }
-
-    pub fn next_block(&mut self, wallet: &str) {
-        match self.config.network {
-            Network::Regtest => {
-                // Manually mine a block
-                let (_, addr) = self.auto_wallet(wallet);
-                self.rpc.generate(1, &addr).unwrap();
-            }
-            _ => {
-                // Wait for next network block
-                self.rpc.wait_for_new_block(0).ok();
-            }
+pub fn auto_wallet(rpc: &mut Rpc, config: &BitcoinConfig, name: &str) -> (Rpc, 
Address) {
+    // Auto load
+    if let Err(err) = rpc.load_wallet(name) {
+        match err {
+            Error::RPC { code, .. } if code == 
ErrorCode::RpcWalletAlreadyLoaded => {}
+            e => Err(e).unwrap(),
         }
     }
+    let mut wallet = Rpc::wallet(config, name).unwrap();
+    let addr = wallet
+        .get_new_address()
+        .unwrap_or_else(|_| panic!("Failed to get wallet address {}", name));
+    (wallet, addr)
 }
 
 fn main() {
-    init();
-    let args: Args = argh::from_env();
-    let mut app = App::start(args.datadir);
+    common::log::init();
+    let args = Args::parse();
+    let config = args
+        .config
+        .map(|path| CoreConfig::load_taler_config(Some(&path), Some("BTC")));
+    let data_dir: PathBuf = config
+        .as_ref()
+        .and_then(|it| it.data_dir.clone())
+        .or(args.datadir)
+        .unwrap_or_else(default_data_dir);
+    let btc_config = BitcoinConfig::load(data_dir).unwrap();
+    let mut rpc = Rpc::common(&btc_config).unwrap();
+
     match args.cmd {
-        Cmd::Transfer(TransferCmd { from, to, amount }) => {
-            let (mut client, _) = app.auto_wallet(&from);
-            let (_, to) = app.auto_wallet(&to);
+        Cmd::Transfer { from, to, amount } => {
+            let (mut client, _) = auto_wallet(&mut rpc, &btc_config, &from);
+            let (_, to) = auto_wallet(&mut rpc, &btc_config, &to);
             let tx = client
                 .send_segwit_key(&to, &Amount::from_btc(amount).unwrap(), 
&rand_slice())
                 .unwrap();
             println!("{}", tx);
         }
-        Cmd::NextBlock(NextBlockCmd { to }) => {
-            app.next_block(&to);
+        Cmd::Nblock { to } => {
+            match btc_config.network {
+                Network::Regtest => {
+                    // Manually mine a block
+                    let (_, addr) = auto_wallet(&mut rpc, &btc_config, &to);
+                    rpc.generate(1, &addr).unwrap();
+                }
+                _ => {
+                    // Wait for next network block
+                    rpc.wait_for_new_block(0).ok();
+                }
+            }
         }
-        Cmd::Abandon(AbandonCmd { from }) => {
-            let (mut wire, _) = app.auto_wallet(&from);
+        Cmd::Abandon { from } => {
+            let (mut wire, _) = auto_wallet(&mut rpc, &btc_config, &from);
             let list = wire.list_since_block(None, 1, false).unwrap();
             for tx in list.transactions {
                 if tx.category == Category::Send && tx.confirmations == 0 {
@@ -162,10 +131,9 @@ fn main() {
                 }
             }
         }
-        Cmd::ResetDB(ResetCmd { config }) => {
-            let config = CoreConfig::load_taler_config(Some(&config));
-            let hash: BlockHash = app.rpc.get_block_hash(0).unwrap();
-            let mut db = Client::connect(&config.db_url, NoTls).unwrap();
+        Cmd::Resetdb => {
+            let hash: BlockHash = rpc.get_block_hash(0).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();
diff --git a/btc-wire/src/bin/segwit-demo.rs b/btc-wire/src/bin/segwit-demo.rs
new file mode 100644
index 0000000..607b6e2
--- /dev/null
+++ b/btc-wire/src/bin/segwit-demo.rs
@@ -0,0 +1,56 @@
+use bitcoin::Amount;
+use btc_wire::{rpc_utils, segwit::encode_segwit_addr};
+use common::{
+    base32::{self, Alphabet},
+    rand_slice,
+};
+
+pub fn main() {
+    let address = "bc1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj";
+    let amount = Amount::from_sat(10000000);
+    let reserve_pub = "0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00";
+    let btc = amount.as_btc();
+
+    println!("Ⅰ - Parse payto uri");
+    println!("Got payto uri: 
payto://bitcoin/{address}?amount=BTC:{btc}&subject={reserve_pub}");
+    println!("Send {btc} BTC to {address} with reserve public key 
{reserve_pub}");
+
+    println!("\nⅡ - Generate fake segwit addresses");
+    let decoded: [u8; 32] = base32::decode(Alphabet::Crockford, reserve_pub)
+        .unwrap()
+        .try_into()
+        .unwrap();
+    println!("Decode reserve public key: 0x{}", hex::encode(&decoded[..]));
+    let magic_id: [u8; 4] = rand_slice();
+    println!("Generate magic id: 0x{}", hex::encode(&magic_id));
+    println!(
+        "Split reserve public key in two:\n0x{}\n0x{}",
+        hex::encode(&decoded[..16]),
+        hex::encode(&decoded[16..])
+    );
+    let mut first_half = [&magic_id, &decoded[..16]].concat();
+    let mut second_half = [&magic_id, &decoded[16..]].concat();
+    println!(
+        "Concatenate magic id with each reserve public key half:\n0x{}\n0x{}",
+        hex::encode(&first_half),
+        hex::encode(&second_half)
+    );
+    first_half[0] &= 0b0111_1111;
+    second_half[0] |= 0b1000_0000;
+    println!(
+        "Set first bit of the first half:\n0x{}\nUnset first bit of the second 
half:\n0x{}",
+        hex::encode(&first_half),
+        hex::encode(&second_half)
+    );
+    // bech32: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
+    let first = encode_segwit_addr("bc", first_half[..].try_into().unwrap());
+    let second = encode_segwit_addr("bc", second_half[..].try_into().unwrap());
+    println!(
+        "Encode each half using bech32 to generate a segwit address:\n{}\n{}",
+        first, second
+    );
+
+    println!("\nⅢ - Send to many");
+    let minimum = rpc_utils::segwit_min_amount().as_btc();
+    println!("Send a single bitcoin transaction with the three addresses as 
recipient as follow:\n{address} {btc}BTC\n{first} {minimum}BTC\n{second} 
{minimum}BTC");
+}
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
index 777e6b2..225e11b 100644
--- a/btc-wire/src/main.rs
+++ b/btc-wire/src/main.rs
@@ -16,17 +16,19 @@
 use bitcoin::{Amount as BtcAmount, Network};
 use btc_wire::{
     config::{BitcoinConfig, WIRE_WALLET_NAME},
-    rpc::{auto_reconnect_rpc, Rpc},
+    rpc::{self, auto_reconnect_rpc, ErrorCode, Rpc},
     rpc_utils::default_data_dir,
 };
+use clap::StructOpt;
 use common::{
     api_common::Amount,
-    config::{load_btc_config, BtcConfig},
+    config::{load_btc_config, BtcConfig, Config, CoreConfig},
     log::log::info,
     named_spawn,
+    postgres::{Client, NoTls},
     reconnect::auto_reconnect_db,
 };
-use std::{str::FromStr, sync::atomic::AtomicU16};
+use std::{path::PathBuf, str::FromStr, sync::atomic::AtomicU16};
 use taler_util::taler_to_btc;
 
 use crate::loops::{analysis::analysis, watcher::watcher, worker::worker};
@@ -55,12 +57,120 @@ fn config_bounce_fee(config: &BtcConfig) -> BtcAmount {
         .expect("config value BOUNCE_FEE is no a valid bitcoin amount")
 }
 
+/// Taler wire for bitcoincore
+#[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 bitcoin wallet and initialize state
+    Initwallet,
+}
+
 fn main() {
     common::log::init();
+    let args = Args::parse();
+
+    match args.init {
+        Some(cmd) => init(args.config, cmd),
+        None => run(args.config),
+    }
+}
+
+fn init(config: Option<PathBuf>, init: Init) {
+    // Parse taler config
+    let config = CoreConfig::load_taler_config(config.as_deref(), Some("BTC"));
+    // Connect to database
+    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/btc.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 => {
+            // Parse bitcoin config
+            let btc_conf = 
BitcoinConfig::load(config.data_dir.unwrap_or_else(default_data_dir))
+                .expect("Failed to load bitcoin configuration");
+            // Connect to bitcoin node
+            let mut rpc = Rpc::common(&btc_conf).expect("Failed to connect to 
bitcoin RPC server");
+
+            // Skip previous blocks
+            let info = rpc
+                .get_blockchain_info()
+                .expect("Failed to get blockchain info");
+            let nb_row = db
+                .execute(
+                    "INSERT INTO state (name, value) VALUES ('last_hash', $1) 
ON CONFLICT (name) DO NOTHING",
+                    &[&info.best_block_hash.as_ref()],
+                )
+                .expect("Failed to update database state");
+            if nb_row > 0 {
+                println!("Skip previous block until now");
+            }
+
+            // Create wallet
+            let created = match rpc.create_wallet(WIRE_WALLET_NAME) {
+                Err(rpc::Error::RPC { code, .. }) if code == 
ErrorCode::RpcWalletError => false,
+                Err(e) => panic!("{}", e),
+                Ok(_) => true,
+            };
+
+            rpc.load_wallet(WIRE_WALLET_NAME).ok();
+
+            // Load previous address
+            let prev_addr = db
+                .query_opt("SELECT value FROM state WHERE name = 'addr'", &[])
+                .expect("Failed to query database state");
+            let addr = if let Some(row) = prev_addr {
+                String::from_utf8(row.get(0)).expect("Stored address is not a 
valid string")
+            } else {
+                // Or generate a new one
+                let new = Rpc::wallet(&btc_conf, WIRE_WALLET_NAME)
+                    .expect("Failed to connect to wallet bitcoin RPC server")
+                    .get_new_address()
+                    .expect("Failed to generate new address")
+                    .to_string();
+                db.execute(
+                    "INSERT INTO state (name, value) VALUES ('addr', $1)",
+                    &[&new.as_bytes()],
+                )
+                .expect("Failed to update database state");
+                new
+            };
+
+            if created {
+                println!("Created new wallet");
+            } else {
+                println!("Found already existing wallet")
+            }
+            println!("Address is {}", &addr);
+            println!("Add the following line into taler.conf:");
+            println!("[depolymerizer-bitcoin]");
+            println!("PAYTO = payto://bitcoin/{}", addr);
+        }
+    }
+}
 
-    let config = load_btc_config(Some(
-        &std::env::args().nth(1).expect("Missing conf path arg"),
-    ));
+fn run(config: Option<PathBuf>) {
+    let config = load_btc_config(config.as_deref());
     let data_dir = config
         .core
         .data_dir
diff --git a/btc-wire/src/segwit.rs b/btc-wire/src/segwit.rs
index 414ef75..3555ffd 100644
--- a/btc-wire/src/segwit.rs
+++ b/btc-wire/src/segwit.rs
@@ -17,7 +17,7 @@ use bech32::{u5, FromBase32, ToBase32, Variant};
 use common::rand::{rngs::OsRng, RngCore};
 
 /// Encode metadata into a segwit address
-fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String {
+pub fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String {
     // We use the version 0 with bech32 encoding
     let mut buf = vec![u5::try_from_u8(0).unwrap()];
     buf.extend_from_slice(&metada.to_base32());
diff --git a/common/src/config.rs b/common/src/config.rs
index b4e57f2..8adb590 100644
--- a/common/src/config.rs
+++ b/common/src/config.rs
@@ -14,18 +14,26 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 use ini::{Ini, Properties};
-use std::{path::PathBuf, process::Command, str::FromStr};
+use std::{
+    fmt::Display,
+    path::{Path, PathBuf},
+    process::{exit, Command},
+    str::FromStr,
+};
 use url::Url;
 
 pub trait Config: Sized {
     /// Load using taler-config
-    fn load_taler_config(file: Option<&str>) -> Self {
+    fn load_taler_config(file: Option<&Path>, currency: Option<&'static str>) 
-> Self {
         let mut cmd = Command::new("taler-config");
         cmd.arg("-d");
         if let Some(path) = file {
-            cmd.args(["-c", path]);
+            cmd.arg("-c");
+            cmd.arg(path);
         }
-        let output = cmd.output().expect("Failed to execute taler-config");
+        let output = cmd
+            .output()
+            .unwrap_or_else(|_| err("Failed to execute taler-config"));
         if !output.status.success() {
             panic!(
                 "taler-config failure:\n{}",
@@ -35,15 +43,23 @@ pub trait Config: Sized {
         let conf = 
ini::Ini::load_from_str(&String::from_utf8_lossy(&output.stdout))
             .expect("Failed to parse config");
         let taler = section(&conf, "taler");
-        let currency = require(taler, "CURRENCY", string);
-        let section_name = match currency.as_str() {
+        let curr = require(taler, "CURRENCY", string);
+        if let Some(currency) = currency {
+            if currency != curr {
+                err(format_args!(
+                    "expected config CURRENCY = {} got {}",
+                    curr, currency
+                ))
+            }
+        }
+        let section_name = match curr.as_str() {
             "BTC" => "depolymerizer-bitcoin",
             "ETH" => "depolymerizer-ethereum",
-            currency => unimplemented!("Unsupported currency {}", currency),
+            currency => err(format_args!("Unsupported currency {}", currency)),
         };
 
         let dep = section(&conf, section_name);
-        return Self::load_from_ini(&conf, &currency, dep);
+        return Self::load_from_ini(&conf, &curr, dep);
     }
     /// Load from loaded ini file
     fn load_from_ini(ini: &Ini, currency: &str, dep: &Properties) -> Self;
@@ -105,7 +121,7 @@ pub struct WireConfig {
 impl Config for WireConfig {
     fn load_from_ini(ini: &Ini, currency: &str, dep: &Properties) -> Self {
         let ex = section(ini, "exchange");
-        let config = Self {
+        Self {
             base_url: require(ex, "BASE_URL", url),
             payto: require(dep, "PAYTO", url),
             confirmation: nb(dep, "CONFIRMATION"),
@@ -117,33 +133,27 @@ impl Config for WireConfig {
                 .and_then(|nb| (nb != 0).then(|| Some(nb)))
                 .unwrap_or(None),
             core: CoreConfig::load_from_ini(ini, currency, dep),
-        };
-        assert_eq!(config.core.currency, currency);
-        return config;
+        }
     }
 }
 
 pub type BtcConfig = WireConfig;
 
-pub fn load_btc_config(path: Option<&str>) -> BtcConfig {
-    let config = WireConfig::load_taler_config(path);
-    assert_eq!(config.core.currency, "BTC");
-    return config;
+pub fn load_btc_config(path: Option<&Path>) -> BtcConfig {
+    WireConfig::load_taler_config(path, Some("BTC"))
 }
 
 pub type EthConfig = WireConfig;
 
-pub fn load_eth_config(path: Option<&str>) -> EthConfig {
-    let config = WireConfig::load_taler_config(path);
-    assert_eq!(config.core.currency, "ETH");
-    return config;
+pub fn load_eth_config(path: Option<&Path>) -> EthConfig {
+    WireConfig::load_taler_config(path, Some("ETH"))
 }
 
-/* ----- Helper functions ----- */
+/* ----- Helper parsing functions ----- */
 
 fn section<'a>(ini: &'a Ini, name: &str) -> &'a Properties {
     ini.section(Some(name))
-        .unwrap_or_else(|| panic!("missing config section {}", name))
+        .unwrap_or_else(|| err(format_args!("missing config section {}", 
name)))
 }
 
 fn require<T>(
@@ -152,7 +162,7 @@ fn require<T>(
     lambda: fn(properties: &Properties, name: &str) -> Option<T>,
 ) -> T {
     let result = lambda(properties, name);
-    result.unwrap_or_else(|| panic!("missing config {}", name))
+    result.unwrap_or_else(|| err(format_args!("missing config {}", name)))
 }
 
 fn string(properties: &Properties, name: &str) -> Option<String> {
@@ -161,19 +171,28 @@ fn string(properties: &Properties, name: &str) -> 
Option<String> {
 
 fn path(properties: &Properties, name: &str) -> Option<PathBuf> {
     properties.get(name).map(|s| {
-        PathBuf::from_str(s).unwrap_or_else(|_| panic!("config value {} is not 
a valid path", name))
+        PathBuf::from_str(s)
+            .unwrap_or_else(|_| err(format_args!("config value {} is not a 
valid path", name)))
     })
 }
 
 fn nb<T: FromStr>(properties: &Properties, name: &str) -> Option<T> {
     properties.get(name).map(|s| {
         s.parse()
-            .unwrap_or_else(|_| panic!("config value {} is not a number", 
name))
+            .unwrap_or_else(|_| err(format_args!("config value {} is not a 
number", name)))
     })
 }
 
 fn url(properties: &Properties, name: &str) -> Option<Url> {
     properties.get(name).map(|s| {
-        Url::parse(s).unwrap_or_else(|_| panic!("config value {} is not a 
valid url", name))
+        Url::parse(s)
+            .unwrap_or_else(|_| err(format_args!("config value {} is not a 
valid url", name)))
     })
 }
+
+/* ----- Helper report functions ----- */
+
+fn err(msg: impl Display) -> ! {
+    eprintln!("error: {}", msg);
+    exit(1);
+}
diff --git a/common/src/lib.rs b/common/src/lib.rs
index 07d52b7..da7a3f3 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -20,6 +20,7 @@ use rand::{rngs::OsRng, RngCore};
 pub use postgres;
 pub use rand;
 pub use url;
+pub use base32;
 
 pub mod api_common;
 pub mod api_wire;
diff --git a/common/src/log.rs b/common/src/log.rs
index 4c67779..ccbfeb1 100644
--- a/common/src/log.rs
+++ b/common/src/log.rs
@@ -44,3 +44,5 @@ pub fn init() {
         .start()
         .unwrap();
 }
+
+
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),
diff --git a/makefile b/makefile
index 61c3ff1..aed52be 100644
--- a/makefile
+++ b/makefile
@@ -1,12 +1,16 @@
 install:
-       cargo install --path btc-wire --bin btc-wire-cli --bin btc-wire-utils 
--bin btc-wire
-       cargo install --path eth-wire --bin eth-wire-cli --bin eth-wire-utils 
--bin eth-wire
+       cargo install --path btc-wire --bin btc-wire
+       cargo install --path eth-wire --bin eth-wire
        cargo install --path wire-gateway
 
-test_gateway: install
+install_test: install
+       cargo install --path btc-wire --bin btc-wire-utils
+       cargo install --path eth-wire --bin eth-wire-utils
+
+test_gateway: install_test
        test/gateway/api.sh
 
-test_btc: install
+test_btc: install_test
        test/btc/wire.sh
        test/btc/lifetime.sh
        test/btc/reconnect.sh
@@ -19,7 +23,7 @@ test_btc: install
        test/btc/maxfee.sh
        test/btc/config.sh
 
-test_eth: install
+test_eth: install_test
        test/eth/wire.sh
        test/eth/lifetime.sh
        test/eth/reconnect.sh
@@ -28,4 +32,7 @@ test_eth: install
        test/eth/hell.sh
        test/eth/analysis.sh
 
-test: install test_gateway test_eth test_btc
\ No newline at end of file
+test: test_gateway test_eth test_btc
+
+segwit_demo: 
+       cargo run --release --bin segwit-demo
\ No newline at end of file
diff --git a/script/prepare.sh b/script/prepare.sh
index c2ac225..232210a 100644
--- a/script/prepare.sh
+++ b/script/prepare.sh
@@ -33,7 +33,7 @@ tar xvzf btc.tar.gz
 mv -v bitcoin-22.0/* ~/bitcoin
 
 
-echo "Ⅳ - Install Go Ethereum (Geth) v1.10.15 "
+echo "Ⅲ - Install Go Ethereum (Geth) v1.10.15 "
 cd $DIR
 curl -L 
https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.10.15-8be800ff.tar.gz
 -o geth.tar.gz
 rm -rfv ~/geth
@@ -41,7 +41,7 @@ mkdir -pv ~/geth
 tar xvzf geth.tar.gz
 mv -v geth-alltools-linux-amd64-1.10.15-8be800ff/* ~/geth
 
-echo "Ⅲ - Config"
+echo "Ⅳ - Config"
 
 echo "Add ~/postgresql/bin to your path"
 echo "Add ~/bitcoin/bin to your path"
diff --git a/test/common.sh b/test/common.sh
index 1e0dfbf..a146196 100644
--- a/test/common.sh
+++ b/test/common.sh
@@ -47,12 +47,12 @@ function load_config() {
     source <(grep = $CONF | sed 's/ *= */=/' | sed 's/=\(.*\)/="\1"/g1')
     BANK_ENDPOINT=http://127.0.0.1:$PORT/
     if [ "$CURRENCY" == "BTC" ]; then
-        WIRE_CLI="btc-wire-cli"
-        WIRE_UTILS="btc-wire-utils -d $WIRE_DIR"
+        WIRE_CLI="btc-wire -c $CONF"
+        WIRE_UTILS="btc-wire-utils -c $CONF"
         WIRE_UTILS2="btc-wire-utils -d $WIRE_DIR2"
     else
-        WIRE_CLI="eth-wire-cli"
-        WIRE_UTILS="eth-wire-utils -d $WIRE_DIR"
+        WIRE_CLI="eth-wire -c $CONF"
+        WIRE_UTILS="eth-wire-utils -c $CONF"
         WIRE_UTILS2="eth-wire-utils -d $WIRE_DIR2"
     fi
 }
@@ -88,13 +88,13 @@ function setup_db() {
     echo "port=5454" >> $DB_DIR/postgresql.conf
     pg_ctl start -D $DB_DIR >> log/postgres.log
     echo "CREATE ROLE postgres LOGIN SUPERUSER PASSWORD 'password'" | psql -p 
5454 postgres > /dev/null
-    $WIRE_CLI initdb $CONF > /dev/null
+    $WIRE_CLI initdb > /dev/null
 }
 
 # Erase database
 function reset_db() {
     sleep 5 # Wait for loop to stop
-    $WIRE_UTILS resetdb $CONF
+    $WIRE_UTILS resetdb
 }
 
 # Stop database
@@ -117,7 +117,7 @@ function init_btc() {
     # Wait for RPC server to be online
     $BTC_CLI -rpcwait getnetworkinfo > /dev/null
     # Create wire wallet
-    btc-wire-cli initwallet $CONF > /dev/null
+    $WIRE_CLI initwallet > /dev/null
     # Create other wallets
     for wallet in client reserve; do
         $BTC_CLI createwallet $wallet > /dev/null
@@ -210,15 +210,15 @@ function check_balance() {
 # Start btc-wire
 function btc_wire() {    
     cargo build --bin btc-wire --release &>> log/cargo.log
-    target/release/btc-wire $CONF &>> log/wire.log &
+    target/release/btc-wire -c $CONF &>> log/wire.log &
     WIRE_PID="$!"
 }
 
 # Start multiple btc-wire with random failures in parallel
 function stress_btc_wire() {
    cargo build --bin btc-wire --release --features fail &>> log/cargo.log
-   target/release/btc-wire $CONF &>> log/wire.log & 
-   target/release/btc-wire $CONF &>> log/wire1.log & 
+   target/release/btc-wire -c $CONF &>> log/wire.log & 
+   target/release/btc-wire -c $CONF &>> log/wire1.log & 
 }
 
 # ----- Ethereum node ----- #
@@ -261,7 +261,7 @@ function init_eth() {
     NODE_PID="$!"
     sleep 1
     # Create wire address
-    WIRE=`eth-wire-cli initwallet $CONF | grep -oP '(?<=is ).*'`
+    WIRE=`$WIRE_CLI initwallet | grep -oP '(?<=is ).*'`
     echo -e "PAYTO = payto://ethereum/$WIRE" >> $CONF
 }
 
@@ -325,15 +325,15 @@ function check_balance_eth() {
 # Start eth-wire
 function eth_wire() {
     cargo build --bin eth-wire --release &>> log/cargo.log
-    target/release/eth-wire $CONF &>> log/wire.log &
+    target/release/eth-wire -c $CONF &>> log/wire.log &
     WIRE_PID="$!"
 }
 
 # Start multiple eth-wire with random failures in parallel
 function stress_eth_wire() {
     cargo build --bin eth-wire --release --features fail &>> log/cargo.log
-    target/release/eth-wire $CONF &>> log/wire.log &
-    target/release/eth-wire $CONF &>> log/wire1.log &
+    target/release/eth-wire -c $CONF &>> log/wire.log &
+    target/release/eth-wire -c $CONF &>> log/wire1.log &
 }
 
 # Mine ethereum blocks
diff --git a/test/conf/taler_btc_lifetime.conf 
b/test/conf/taler_btc_lifetime.conf
index 77db531..f57c3b7 100644
--- a/test/conf/taler_btc_lifetime.conf
+++ b/test/conf/taler_btc_lifetime.conf
@@ -9,6 +9,5 @@ DB_URL        = 
postgres://localhost:5454/postgres?user=postgres&password=passwo
 PORT          = 8060
 PAYTO         = payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj
 CONFIRMATION  = 3
-BOUNCE_FEE    = 1000
 HTTP_LIFETIME = 10
 WIRE_LIFETIME = 10
\ No newline at end of file
diff --git a/uri-pack/README.md b/uri-pack/README.md
index 4c32424..1cc3978 100644
--- a/uri-pack/README.md
+++ b/uri-pack/README.md
@@ -10,7 +10,7 @@ uri are encoded with 5b, the encoded size is smaller than a 
simple ascii
 format.
 
 On the majestic_million database, 98.77% of the domain name where smaller, 
-going from an average of 14b to an average of 10b.
+going from an average encoded size of 14B in ASCII to 10B using our format
 
 ## Usage
 
diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs
index 1a1b02d..e6e4199 100644
--- a/wire-gateway/src/main.rs
+++ b/wire-gateway/src/main.rs
@@ -37,6 +37,7 @@ use json::{encode_body, parse_body};
 use listenfd::ListenFd;
 use std::{
     convert::Infallible,
+    path::PathBuf,
     str::FromStr,
     sync::atomic::{AtomicBool, AtomicU32, Ordering},
     time::{Duration, Instant},
@@ -88,9 +89,12 @@ impl ServerState {
 async fn main() {
     common::log::init();
 
-    let conf = GatewayConfig::load_taler_config(Some(
-        &std::env::args().nth(1).expect("Missing conf path arg"),
-    ));
+    let conf = GatewayConfig::load_taler_config(
+        Some(&PathBuf::from(
+            std::env::args_os().nth(1).expect("Missing conf path arg"),
+        )),
+        None,
+    );
 
     #[cfg(feature = "test")]
     common::log::log::warn!("Running with test admin endpoint unsuitable for 
production");

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]