commit 88556bdbfa2140e8d11367cf87601df0c70f0604
parent 187f435336d86935a92b4d55a5a17cd5e24b9aab
Author: Antoine A <>
Date: Tue, 15 Feb 2022 16:05:04 +0100
Replace argh CLI parser with clap, merge wire binary with its bootstrap and improve config error msg
Diffstat:
18 files changed, 625 insertions(+), 572 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -24,35 +24,6 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -150,10 +121,10 @@ dependencies = [
name = "btc-wire"
version = "0.1.0"
dependencies = [
- "argh",
"base64",
"bech32",
"bitcoin",
+ "clap 3.0.14",
"common",
"criterion",
"hex",
@@ -223,11 +194,41 @@ 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"
dependencies = [
@@ -262,7 +263,7 @@ checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
dependencies = [
"atty",
"cast",
- "clap",
+ "clap 2.34.0",
"criterion-plot",
"csv",
"itertools",
@@ -479,7 +480,7 @@ dependencies = [
name = "eth-wire"
version = "0.1.0"
dependencies = [
- "argh",
+ "clap 3.0.14",
"common",
"ethereum-types",
"hex",
@@ -704,13 +705,16 @@ dependencies = [
]
[[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"
@@ -833,6 +837,16 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -879,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"
@@ -1033,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]]
@@ -1206,6 +1229,30 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1575,6 +1622,15 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1584,6 +1640,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1785,12 +1847,6 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git 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
@@ -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
diff --git 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,9 +34,9 @@ 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`
diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs
@@ -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
@@ -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/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/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, ¤cy, 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/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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,7 +32,7 @@ test_eth: install
test/eth/hell.sh
test/eth/analysis.sh
-test: install test_gateway test_eth test_btc
+test: test_gateway test_eth test_btc
segwit_demo:
cargo run --release --bin segwit-demo
\ No newline at end of file
diff --git 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/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
@@ -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");