commit 0467435320f42f1e7245554e194f3f2cd73d92c1
parent e21dbc6255dad5c30de11a9622bb4c02af68a4b0
Author: Antoine A <>
Date: Wed, 15 Dec 2021 17:01:28 +0100
Move taler api to another crate and other improvements
Diffstat:
21 files changed, 153 insertions(+), 106 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -191,13 +191,13 @@ dependencies = [
"owo-colors",
"postgres",
"rand",
- "rust-ini",
"serde",
+ "taler-api",
+ "taler-config",
"taler-log",
"thiserror",
"uri-pack",
"url",
- "wire-gateway",
]
[[package]]
@@ -251,12 +251,6 @@ dependencies = [
]
[[package]]
-name = "configparser"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06821ea598337a8412cf47c5b71c3bc694a7f0aed188ac28b836fab164a2c202"
-
-[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1473,6 +1467,26 @@ dependencies = [
]
[[package]]
+name = "taler-api"
+version = "0.1.0"
+dependencies = [
+ "base32",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "thiserror",
+ "url",
+]
+
+[[package]]
+name = "taler-config"
+version = "0.1.0"
+dependencies = [
+ "rust-ini",
+ "url",
+]
+
+[[package]]
name = "taler-log"
version = "0.1.0"
dependencies = [
@@ -1853,9 +1867,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
name = "wire-gateway"
version = "0.1.0"
dependencies = [
- "base32",
"bitcoin",
- "configparser",
"deadpool-postgres",
"hyper",
"listenfd",
@@ -1865,9 +1877,10 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"serde_with",
+ "taler-api",
+ "taler-config",
"taler-log",
"thiserror",
"tokio",
"tokio-postgres",
- "url",
]
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,2 +1,9 @@
[workspace]
-members = ["wire-gateway", "btc-wire", "uri-pack", "taler-log"]
+members = [
+ "wire-gateway",
+ "btc-wire",
+ "uri-pack",
+ "taler-log",
+ "taler-api",
+ "taler-config",
+]
diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml
@@ -28,13 +28,11 @@ postgres = "0.19.2"
uri-pack = { path = "../uri-pack" }
# Url format
url = { version = "2.2.2", features = ["serde"] }
-# Wire gateway api
-wire-gateway = { path = "../wire-gateway" }
# Ansi color
owo-colors = "3.1.1"
-# Ini files
-rust-ini = "0.17.0"
-# Taler logging
+# Taler libs
+taler-api = { path = "../taler-api" }
+taler-config = { path = "../taler-config" }
taler-log = { path = "../taler-log" }
[dev-dependencies]
diff --git a/btc-wire/src/bin/test.rs b/btc-wire/src/bin/test.rs
@@ -13,7 +13,7 @@ use bitcoincore_rpc::{
use btc_wire::{
rpc_patch::RpcErrorCode,
rpc_utils::{
- common_rpc, dirty_guess_network, network_dir_path, wallet_rpc, Network, CLIENT, WIRE,
+ common_rpc, default_data_dir, dirty_guess_network, wallet_rpc, Network, CLIENT, WIRE,
},
test::rand_key,
BounceErr, ClientExtended,
@@ -25,9 +25,9 @@ const RESERVE: &str = "reserve";
/// Instrumentation test
pub fn main() {
let test_amount = Amount::from_sat(1500);
-
+ let data_dir = default_data_dir();
// Network check
- let network = dirty_guess_network();
+ let network = dirty_guess_network(&data_dir);
match network {
Network::MainNet => {
panic!("Do not run tests on the mainnet, you are going to loose money")
@@ -39,13 +39,13 @@ pub fn main() {
// Wallet check
{
let existing_wallets: HashSet<String> =
- std::fs::read_dir(network_dir_path(network).join("wallets"))
+ std::fs::read_dir(data_dir.join(network.dir()).join("wallets"))
.unwrap()
.filter_map(|it| it.ok())
.map(|it| it.file_name().to_string_lossy().to_string())
.collect();
- let rpc = common_rpc(network).expect("Failed to open common client");
+ let rpc = common_rpc(&data_dir, network).expect("Failed to open common client");
if !existing_wallets.contains(CLIENT)
|| !existing_wallets.contains(WIRE)
|| !existing_wallets.contains(RESERVE)
@@ -65,9 +65,9 @@ pub fn main() {
}
// Client initialization
- let client_rpc = wallet_rpc(network, CLIENT);
- let wire_rpc = wallet_rpc(network, WIRE);
- let reserve_rpc = wallet_rpc(network, RESERVE);
+ let client_rpc = wallet_rpc(&data_dir, network, CLIENT);
+ let wire_rpc = wallet_rpc(&data_dir, network, WIRE);
+ let reserve_rpc = wallet_rpc(&data_dir, network, RESERVE);
let client_addr = client_rpc.get_new_address(None, None).unwrap();
let wire_addr = wire_rpc.get_new_address(None, None).unwrap();
let reserve_addr = reserve_rpc.get_new_address(None, None).unwrap();
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
@@ -14,13 +14,14 @@ use postgres::{fallible_iterator::FallibleIterator, Client, NoTls, Transaction};
use rand::{rngs::OsRng, RngCore};
use std::{
collections::HashMap,
- path::{Path, PathBuf},
+ path::PathBuf,
str::FromStr,
time::{Duration, SystemTime},
};
+use taler_api::api_common::{crockford_base32_encode, Amount};
+use taler_config::Config;
use taler_log::log::{error, info, warn};
use url::Url;
-use wire_gateway::api_common::{crockford_base32_encode, Amount};
use crate::fail_point::fail_point;
@@ -84,22 +85,20 @@ fn taler_amount_to_btc_amount(amount: &Amount) -> Result<BtcAmount, String> {
}
fn encode_info(wtid: &[u8; 32], url: &Url) -> Vec<u8> {
- let mut buffer = Vec::new();
- buffer.extend_from_slice(wtid);
+ let mut buffer = wtid.to_vec();
+ buffer.push(if url.scheme() == "http" { 1 } else { 0 });
let parts = format!("{}{}", url.domain().unwrap_or(""), url.path());
let packed = uri_pack::pack_uri(&parts).unwrap();
- buffer.push((url.scheme() == "http:") as u8);
buffer.extend_from_slice(&packed);
return buffer;
}
fn decode_info(bytes: &[u8]) -> ([u8; 32], Url) {
- let mut packed = uri_pack::unpack_uri(&bytes[33..]).unwrap();
- packed.insert_str(0, "://");
- if bytes[32] != 0 {
- packed.insert(0, 's');
- }
- packed.insert_str(0, "http");
+ let packed = format!(
+ "http{}://{}",
+ if bytes[32] == 0 { "s" } else { "" },
+ uri_pack::unpack_uri(&bytes[33..]).unwrap(),
+ );
let url = Url::parse(&packed).unwrap();
return (bytes[..32].try_into().unwrap(), url);
}
@@ -113,13 +112,14 @@ mod test {
#[test]
fn decode_encode_info() {
- let key = rand_key();
let urls = [
"https://git.taler.net/",
"https://git.taler.net/depolymerization.git/",
+ "http://git.taler.net/",
+ "http://git.taler.net/depolymerization.git/",
];
-
for url in urls {
+ let key = rand_key();
let url = Url::parse(url).unwrap();
let encode = encode_info(&key, &url);
let decode = decode_info(&encode);
@@ -416,31 +416,6 @@ fn watcher(rpc: RPC, mut db: AutoReloadDb, config: &Config) {
}
}
-#[derive(Debug, Clone)]
-struct Config {
- base_url: Url,
- db_url: String,
- port: u16,
- payto: Url,
- address: String,
- confirmation: u8,
-}
-
-impl Config {
- pub fn from_path(path: impl AsRef<Path>) -> Self {
- let conf = ini::Ini::load_from_file(path).unwrap();
- let conf = conf.section(Some("main")).unwrap();
- Self {
- base_url: Url::parse(&conf.get("BASE_URL").unwrap()).unwrap(),
- db_url: conf.get("DB_URL").unwrap().to_string(),
- port: conf.get("PORT").unwrap().parse().unwrap(),
- payto: Url::parse(&conf.get("PAYTO").unwrap()).unwrap(),
- address: conf.get("ADDRESS").unwrap().to_string(),
- confirmation: conf.get("CONFIRMATION").unwrap().parse().unwrap(),
- }
- }
-}
-
fn main() {
taler_log::init();
@@ -453,7 +428,7 @@ fn main() {
.next()
.map(|str| PathBuf::from_str(&str).unwrap())
.unwrap_or(default_data_dir());
- let config = Config::from_path("test.conf");
+ let config = taler_config::Config::from_path("test.conf");
let network = dirty_guess_network(&data_dir);
let rpc = common_rpc(&data_dir, network).unwrap();
rpc.load_wallet(&WIRE).ok();
diff --git a/script/setup.sh b/script/setup.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+## Test utils
+
# Load test.conf as bash variables
function load_config() {
source <(grep = test.conf | sed 's/ *= */=/' | sed 's/=\(.*\)/="\1"/g1')
diff --git a/script/test_btc_stress.sh b/script/test_btc_stress.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# Test btc_wire behavior
+## Test btc_wire behavior when ran and stressed concurrently
set -eu
@@ -65,12 +65,9 @@ for n in `$SEQ`; do
-a BTC:0.0000$n > /dev/null
done
next_btc # Mine transactions
-sleep 10
-next_btc # Trigger watcher twice, never sure
-sleep 10
-next_btc # Trigger watcher twice, never sure
-sleep 10
+sleep 5
next_btc # Trigger watcher twice, never sure
+sleep 3
echo " OK"
echo -n "Requesting exchange outgoing transaction list:"
diff --git a/script/test_btc_wire.sh b/script/test_btc_wire.sh
@@ -1,7 +1,8 @@
#!/bin/bash
-set -eu
+## Test btc_wire correctly receive and sens transactions on the blockchain
+set -eu
# Cleanup to run whenever we exit
function cleanup() {
diff --git a/script/test_gateway.sh b/script/test_gateway.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+## Test wire_gateway conformance to documentation and its security
+
set -eu
# Create temp file
diff --git a/script/test_recover_db.sh b/script/test_recover_db.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+## Check the capacity of wire_gateway and btc_wire to recover from database loss
+
set -eu
# Cleanup to run whenever we exit
@@ -62,6 +64,7 @@ taler-exchange-wire-gateway-client \
-b $BANK_ENDPOINT \
-C payto://bitcoin/$CLIENT \
-a BTC:0.00002 > /dev/null
+mine_btc
next_btc
check_balance 9.99992218 1.00006001
echo " OK"
diff --git a/taler-api/Cargo.toml b/taler-api/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "taler-api"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+# Serialization framework
+serde = { version = "1.0.130", features = ["derive"] }
+# Serialization helper
+serde_with = "1.11.0"
+# JSON serialization
+serde_json = "1.0.72"
+# Url format
+url = { version = "2.2.2", features = ["serde"] }
+# Crockford’s base32
+base32 = "0.4.0"
+# Error macros
+thiserror = "1.0.30"
+\ No newline at end of file
diff --git a/wire-gateway/src/api_common.rs b/taler-api/src/api_common.rs
diff --git a/wire-gateway/src/api_wire.rs b/taler-api/src/api_wire.rs
diff --git a/wire-gateway/src/error_codes.rs b/taler-api/src/error_codes.rs
diff --git a/taler-api/src/lib.rs b/taler-api/src/lib.rs
@@ -0,0 +1,5 @@
+pub use url;
+
+pub mod api_common;
+pub mod api_wire;
+pub mod error_codes;
diff --git a/taler-config/Cargo.toml b/taler-config/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "taler-config"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+# Url format
+url = { version = "2.2.2", features = ["serde"] }
+# Ini files
+rust-ini = "0.17.0"
+\ No newline at end of file
diff --git a/taler-config/src/lib.rs b/taler-config/src/lib.rs
@@ -0,0 +1,27 @@
+use std::path::Path;
+use url::Url;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Config {
+ pub base_url: Url,
+ pub db_url: String,
+ pub port: u16,
+ pub payto: Url,
+ pub address: String,
+ pub confirmation: u8,
+}
+
+impl Config {
+ pub fn from_path(path: impl AsRef<Path>) -> Self {
+ let conf = ini::Ini::load_from_file(path).unwrap();
+ let conf = conf.section(Some("main")).unwrap();
+ Self {
+ base_url: Url::parse(&conf.get("BASE_URL").unwrap()).unwrap(),
+ db_url: conf.get("DB_URL").unwrap().to_string(),
+ port: conf.get("PORT").unwrap().parse().unwrap(),
+ payto: Url::parse(&conf.get("PAYTO").unwrap()).unwrap(),
+ address: conf.get("ADDRESS").unwrap().to_string(),
+ confirmation: conf.get("CONFIRMATION").unwrap().parse().unwrap(),
+ }
+ }
+}
diff --git a/wire-gateway/Cargo.toml b/wire-gateway/Cargo.toml
@@ -20,25 +20,21 @@ serde_with = "1.11.0"
serde_json = "1.0.72"
# Url query serialization
serde_urlencoded = "0.7.0"
-# Crockford’s base32
-base32 = "0.4.0"
# Error macros
thiserror = "1.0.30"
# Deflate compression
miniz_oxide = "0.5.1"
# Rng
rand = { version = "0.8.4", features = ["getrandom"] }
-# Url format
-url = { version = "2.2.2", features = ["serde"] }
# Async postgres client
tokio-postgres = { version = "0.7.5" }
deadpool-postgres = "0.10.1"
-# Logging
-taler-log = { path = "../taler-log" }
# Socket activation
listenfd = "0.3.5"
-# Ini files
-configparser = "3.0.0"
+# Taler libs
+taler-api = { path = "../taler-api" }
+taler-config = { path = "../taler-config" }
+taler-log = { path = "../taler-log" }
# TODO Put this behind a feature
# Bitcoin data structure
diff --git a/wire-gateway/src/error.rs b/wire-gateway/src/error.rs
@@ -1,5 +1,5 @@
use hyper::{header, Body, Response, StatusCode};
-use wire_gateway::{api_common::ErrorDetail, error_codes::ErrorCode};
+use taler_api::{api_common::ErrorDetail, error_codes::ErrorCode};
/// Generic http error
#[derive(Debug)]
diff --git a/wire-gateway/src/lib.rs b/wire-gateway/src/lib.rs
@@ -1,3 +0,0 @@
-pub mod api_common;
-pub mod api_wire;
-pub mod error_codes;
-\ No newline at end of file
diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs
@@ -1,5 +1,4 @@
-use configparser::ini::Ini;
-use deadpool_postgres::{Config, Pool, Runtime};
+use deadpool_postgres::{Pool, Runtime};
use error::{CatchResult, ServerError};
use hyper::{
http::request::Parts,
@@ -9,47 +8,39 @@ use hyper::{
use json::{encode_body, parse_body};
use listenfd::ListenFd;
use std::{convert::Infallible, str::FromStr, time::Instant};
-use taler_log::log::{error, info, log, Level};
-use tokio_postgres::{config::Host, NoTls};
-use url::Url;
-use wire_gateway::{
+use taler_api::{
api_common::{Amount, SafeUint64, ShortHashCode, Timestamp},
api_wire::{
HistoryParams, IncomingBankTransaction, IncomingHistory, OutgoingBankTransaction,
OutgoingHistory, TransferRequest, TransferResponse,
},
error_codes::ErrorCode,
+ url::Url,
};
+use taler_log::log::{error, info, log, Level};
+use tokio_postgres::{config::Host, NoTls};
mod error;
mod json;
struct ServerState {
pool: Pool,
- pay_to: String,
+ config: taler_config::Config,
}
#[tokio::main]
async fn main() {
taler_log::init();
- let mut conf = Ini::new();
- conf.read(std::fs::read_to_string("test.conf").unwrap())
- .unwrap();
- let db_url = conf.get("main", "DB_URL").expect("Missing BD_URL");
- let port = conf
- .getuint("main", "PORT")
- .expect("Missing PORT")
- .unwrap_or(8080);
- let pay_to = conf.get("main", "PAYTO").expect("Missing PAYTO");
+ let conf = taler_config::Config::from_path("test.conf");
#[cfg(feature = "test")]
taler_log::log::warn!("Running with test admin endpoint unsuitable for production");
// Parse postgres url
- let config = tokio_postgres::Config::from_str(&db_url).unwrap();
+ let config = tokio_postgres::Config::from_str(&conf.db_url).unwrap();
// TODO find a way to clean this ugly mess
- let mut cfg = Config::new();
+ let mut cfg = deadpool_postgres::Config::new();
cfg.user = config.get_user().map(|it| it.to_string());
cfg.password = config
.get_password()
@@ -72,7 +63,7 @@ async fn main() {
cfg.connect_timeout = config.get_connect_timeout().cloned();
let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
- let state = ServerState { pool, pay_to };
+ let state = ServerState { pool, config: conf };
let state: &'static ServerState = Box::leak(Box::new(state));
let make_service = make_service_fn(move |_| async move {
Ok::<_, Infallible>(service_fn(move |req| async move {
@@ -111,7 +102,7 @@ async fn main() {
);
Server::from_tcp(listener).unwrap().serve(make_service)
} else {
- let addr = ([0, 0, 0, 0], port as u16).into();
+ let addr = ([0, 0, 0, 0], state.config.port).into();
info!("Server listening on http://{}", &addr);
Server::bind(&addr).serve(make_service)
};
@@ -238,7 +229,7 @@ async fn router(
let timestamp = Timestamp::now();
let row = db.query_one("INSERT INTO tx_out (_date, amount, wtid, debit_acc, credit_acc, exchange_url, status, request_uid) VALUES (now(), $1, $2, $3, $4, $5, $6, $7) RETURNING id", &[
- &request.amount.to_string(), &request.wtid.as_ref(), &state.pay_to, &request.credit_account.to_string(), &request.exchange_base_url.to_string(), &0i16, &request.request_uid.as_ref()
+ &request.amount.to_string(), &request.wtid.as_ref(), &state.config.payto.to_string(), &request.credit_account.to_string(), &request.exchange_base_url.to_string(), &0i16, &request.request_uid.as_ref()
]).await?;
encode_body(
parts,
@@ -348,7 +339,7 @@ async fn router(
"/admin/add-incoming" => {
// We do not check input as this is a test admin endpoint
assert_method(&parts, Method::POST).unwrap();
- let request: wire_gateway::api_wire::AddIncomingRequest =
+ let request: taler_api::api_wire::AddIncomingRequest =
parse_body(&parts, body).await.unwrap();
let timestamp = Timestamp::now();
let db = state.pool.get().await.catch_code(