commit 48b3266bbffa290d96e3f7d7fb6d2ca48679d8a9
parent d76ac36c53783d5d6e358f71845944cd286a77aa
Author: Antoine A <>
Date: Wed, 2 Feb 2022 15:59:23 +0100
Preparation for eth-wire
Diffstat:
51 files changed, 1400 insertions(+), 1338 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -24,12 +24,6 @@ dependencies = [
]
[[package]]
-name = "anyhow"
-version = "1.0.53"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
-
-[[package]]
name = "argh"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -59,18 +53,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6f8c380fa28aa1b36107cd97f0196474bb7241bb95a453c5c01a15ac74b2eac"
[[package]]
-name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
-[[package]]
-name = "arrayvec"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
-
-[[package]]
name = "async-trait"
version = "0.1.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -144,28 +126,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
-name = "bitvec"
-version = "0.20.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "block-buffer"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
-dependencies = [
- "block-padding",
- "generic-array",
-]
-
-[[package]]
name = "block-buffer"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -175,12 +135,6 @@ dependencies = [
]
[[package]]
-name = "block-padding"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
-
-[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -217,12 +171,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
-name = "byte-slice-cast"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698"
-
-[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -279,12 +227,6 @@ dependencies = [
]
[[package]]
-name = "convert_case"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
-
-[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -479,34 +421,12 @@ dependencies = [
]
[[package]]
-name = "derive_more"
-version = "0.99.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
-dependencies = [
- "convert_case",
- "proc-macro2",
- "quote",
- "rustc_version",
- "syn",
-]
-
-[[package]]
-name = "digest"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
name = "digest"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
dependencies = [
- "block-buffer 0.10.0",
+ "block-buffer",
"crypto-common",
"generic-array",
"subtle",
@@ -538,33 +458,18 @@ dependencies = [
]
[[package]]
-name = "eth_wire"
+name = "eth-wire"
version = "0.1.0"
dependencies = [
"argh",
+ "ethereum-types",
+ "hex",
"serde",
"serde_json",
"serde_repr",
"taler-common",
"thiserror",
"uri-pack",
- "web3",
-]
-
-[[package]]
-name = "ethabi"
-version = "14.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01317735d563b3bad2d5f90d2e1799f414165408251abb762510f40e790e69a"
-dependencies = [
- "anyhow",
- "ethereum-types",
- "hex",
- "serde",
- "serde_json",
- "sha3",
- "thiserror",
- "uint",
]
[[package]]
@@ -575,20 +480,18 @@ checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8"
dependencies = [
"crunchy",
"fixed-hash",
- "impl-rlp",
"impl-serde",
"tiny-keccak",
]
[[package]]
name = "ethereum-types"
-version = "0.11.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f64b5df66a228d85e4b17e5d6c6aa43b0310898ffe8a85988c4c032357aaabfd"
+checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf"
dependencies = [
"ethbloom",
"fixed-hash",
- "impl-rlp",
"impl-serde",
"primitive-types",
"uint",
@@ -616,16 +519,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c"
dependencies = [
"byteorder",
- "rand",
"rustc-hex",
"static_assertions",
]
[[package]]
name = "flexi_logger"
-version = "0.22.2"
+version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b51b4517f4422bfa0515dafcc10b4cc4cd3953d69a19608fd74afb3b19e227c"
+checksum = "969940c39bc718475391e53a3a59b0157e64929c80cf83ad5dde5f770ecdc423"
dependencies = [
"chrono",
"glob",
@@ -653,12 +555,6 @@ dependencies = [
]
[[package]]
-name = "funty"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
-
-[[package]]
name = "futures"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -730,12 +626,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
[[package]]
-name = "futures-timer"
-version = "3.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
-
-[[package]]
name = "futures-util"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -825,7 +715,7 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2"
dependencies = [
- "digest 0.10.1",
+ "digest",
]
[[package]]
@@ -916,24 +806,6 @@ dependencies = [
]
[[package]]
-name = "impl-codec"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443"
-dependencies = [
- "parity-scale-codec",
-]
-
-[[package]]
-name = "impl-rlp"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808"
-dependencies = [
- "rlp",
-]
-
-[[package]]
name = "impl-serde"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -943,17 +815,6 @@ dependencies = [
]
[[package]]
-name = "impl-trait-for-tuples"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -993,27 +854,6 @@ dependencies = [
]
[[package]]
-name = "jsonrpc-core"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
-dependencies = [
- "futures",
- "futures-executor",
- "futures-util",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
-]
-
-[[package]]
-name = "keccak"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
-
-[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1066,7 +906,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae"
dependencies = [
- "digest 0.10.1",
+ "digest",
]
[[package]]
@@ -1169,12 +1009,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
-name = "opaque-debug"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
-
-[[package]]
name = "ordered-multimap"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1185,32 +1019,6 @@ dependencies = [
]
[[package]]
-name = "parity-scale-codec"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909"
-dependencies = [
- "arrayvec 0.7.2",
- "bitvec",
- "byte-slice-cast",
- "impl-trait-for-tuples",
- "parity-scale-codec-derive",
- "serde",
-]
-
-[[package]]
-name = "parity-scale-codec-derive"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27"
-dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1370,28 +1178,16 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "primitive-types"
-version = "0.9.1"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06345ee39fbccfb06ab45f3a1a5798d9dafa04cb8921a76d227040003a234b0e"
+checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373"
dependencies = [
"fixed-hash",
- "impl-codec",
- "impl-rlp",
"impl-serde",
"uint",
]
[[package]]
-name = "proc-macro-crate"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
-dependencies = [
- "thiserror",
- "toml",
-]
-
-[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1432,12 +1228,6 @@ dependencies = [
]
[[package]]
-name = "radium"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
-
-[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1535,16 +1325,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
-name = "rlp"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5"
-dependencies = [
- "bytes",
- "rustc-hex",
-]
-
-[[package]]
name = "rust-ini"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1716,19 +1496,7 @@ checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
dependencies = [
"cfg-if",
"cpufeatures",
- "digest 0.10.1",
-]
-
-[[package]]
-name = "sha3"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
-dependencies = [
- "block-buffer 0.9.0",
- "digest 0.9.0",
- "keccak",
- "opaque-debug",
+ "digest",
]
[[package]]
@@ -1817,12 +1585,6 @@ dependencies = [
]
[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1968,15 +1730,6 @@ dependencies = [
]
[[package]]
-name = "toml"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
-dependencies = [
- "serde",
-]
-
-[[package]]
name = "tower-service"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2190,29 +1943,6 @@ dependencies = [
]
[[package]]
-name = "web3"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd24abe6f2b68e0677f843059faea87bcbd4892e39f02886f366d8222c3c540d"
-dependencies = [
- "arrayvec 0.5.2",
- "derive_more",
- "ethabi",
- "ethereum-types",
- "futures",
- "futures-timer",
- "hex",
- "jsonrpc-core",
- "log",
- "parking_lot",
- "pin-project",
- "rlp",
- "serde",
- "serde_json",
- "tiny-keccak",
-]
-
-[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2262,9 +1992,3 @@ dependencies = [
"tokio",
"tokio-postgres",
]
-
-[[package]]
-name = "wyz"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
diff --git a/Cargo.toml b/Cargo.toml
@@ -2,7 +2,7 @@
members = [
"wire-gateway",
"btc-wire",
- "eth_wire",
+ "eth-wire",
"uri-pack",
"taler-common",
]
diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml
@@ -19,8 +19,8 @@ argh = "0.1.7"
# Bech32 encoding and decoding
bech32 = "0.8.1"
# Serialization library
-serde = { version = "1.0.133", features = ["derive"] }
-serde_json = "1.0.75"
+serde = { version = "1.0.136", features = ["derive"] }
+serde_json = "1.0.78"
serde_repr = "0.1.7"
# Error macros
thiserror = "1.0.30"
diff --git a/btc-wire/README.md b/btc-wire/README.md
@@ -18,8 +18,8 @@ The configuration is based on [taler.conf](https://docs.taler.net/manpages/taler
``` ini
# taler.conf - btc_wire config
-[depolymerizer-___]
-BTC_DATA_DIR =
+[depolymerizer-bitcoin]
+DATA_DIR =
CONFIRMATION = 6
BOUNCE_FEE = 1000
BUMP_DELAY =
diff --git a/btc-wire/src/bin/btc_test.rs b/btc-wire/src/bin/btc-test.rs
diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs
@@ -18,12 +18,16 @@ use btc_wire::{
rpc::{BtcRpc, Error, ErrorCode},
rpc_utils::default_data_dir,
};
-use taler_common::postgres::{NoTls, Client};
+use taler_common::{
+ config::{Config, CoreConfig},
+ postgres::{Client, NoTls},
+};
fn main() {
let args: Vec<_> = std::env::args().collect();
// Parse taler config
- let config = taler_common::config::InitConfig::load_from_file(&args[2]);
+ let config = CoreConfig::load_from_file(&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");
@@ -44,7 +48,7 @@ fn main() {
"initwallet" => {
// Parse bitcoin config
let btc_conf =
- BitcoinConfig::load(config.btc_data_dir.unwrap_or_else(default_data_dir))
+ BitcoinConfig::load(config.data_dir.unwrap_or_else(default_data_dir))
.expect("Failed to load bitcoin configuration");
// Connect to bitcoin node
let mut rpc =
diff --git a/btc-wire/src/bin/btc-wire-utils.rs b/btc-wire/src/bin/btc-wire-utils.rs
@@ -21,7 +21,11 @@ use btc_wire::{
rpc::{BtcRpc, Category, Error, ErrorCode},
rpc_utils::default_data_dir,
};
-use taler_common::{config::Config, rand_slice, postgres::{NoTls, Client}};
+use taler_common::{
+ config::{Config, CoreConfig},
+ postgres::{Client, NoTls},
+ rand_slice,
+};
#[derive(argh::FromArgs)]
/// Bitcoin wire test client
@@ -166,7 +170,7 @@ fn main() {
}
}
Cmd::ClearDB(ClearCmd { config }) => {
- let config = Config::load_from_file(&config);
+ let config = CoreConfig::load_from_file(&config);
let mut db = Client::connect(&config.db_url, NoTls).unwrap();
db.execute("DROP TABLE IF EXISTS state, tx_in, tx_out, bounce", &[])
.unwrap();
diff --git a/btc-wire/src/loops/analysis.rs b/btc-wire/src/loops/analysis.rs
@@ -17,7 +17,7 @@ use std::sync::atomic::Ordering;
use btc_wire::rpc::ChainTipsStatus;
use taler_common::{
- config::Config,
+ config::BtcConfig,
log::log::{error, warn},
postgres::fallible_iterator::FallibleIterator,
};
@@ -33,7 +33,7 @@ use super::LoopResult;
pub fn analysis(
mut rpc: AutoReconnectRPC,
mut db: AutoReconnectSql,
- config: &Config,
+ config: &BtcConfig,
state: &WireState,
) {
// The biggest fork ever seen
diff --git a/btc-wire/src/loops/worker.rs b/btc-wire/src/loops/worker.rs
@@ -29,7 +29,7 @@ use btc_wire::{
use postgres::{fallible_iterator::FallibleIterator, Client};
use taler_common::{
api_common::base32,
- config::Config,
+ config::BtcConfig,
log::log::{error, info, warn},
postgres,
sql::{sql_array, sql_url},
@@ -51,7 +51,7 @@ use super::{LoopError, LoopResult};
pub fn worker(
mut rpc: AutoReconnectRPC,
mut db: AutoReconnectSql,
- config: &Config,
+ config: &BtcConfig,
state: &WireState,
) {
let mut lifetime = config.wire_lifetime;
@@ -131,6 +131,7 @@ pub fn worker(
skip_notification = !matches!(
e,
LoopError::RPC(rpc::Error::RPC { .. } | rpc::Error::Bitcoin(_))
+ | LoopError::Concurrency
);
} else {
skip_notification = false;
@@ -240,7 +241,7 @@ fn last_hash(db: &mut Client) -> Result<Option<BlockHash>, postgres::Error> {
fn sync_chain(
rpc: &mut BtcRpc,
db: &mut Client,
- config: &Config,
+ config: &BtcConfig,
state: &WireState,
status: &mut bool,
) -> LoopResult<bool> {
@@ -398,7 +399,7 @@ fn sync_chain_outgoing(
confirmations: i32,
rpc: &mut BtcRpc,
db: &mut Client,
- config: &Config,
+ config: &BtcConfig,
) -> LoopResult<()> {
match rpc
.get_tx_op_return(id)
@@ -429,7 +430,7 @@ fn sync_chain_outgoing_send(
rpc: &mut BtcRpc,
db: &mut Client,
confirmations: i32,
- config: &Config,
+ config: &BtcConfig,
) -> LoopResult<()> {
let credit_addr = full.details[0].address.as_ref().unwrap();
let amount = btc_to_taler(&full.amount);
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
@@ -21,7 +21,10 @@ use btc_wire::{
};
use reconnect::{AutoReconnectRPC, AutoReconnectSql};
use std::{sync::atomic::AtomicU16, thread::JoinHandle};
-use taler_common::{config::Config, log::log::info};
+use taler_common::{
+ config::{load_btc_config, BtcConfig},
+ log::log::info,
+};
use crate::loops::{analysis::analysis, watcher::watcher, worker::worker};
@@ -29,9 +32,9 @@ mod fail_point;
mod info;
mod loops;
mod reconnect;
+mod sql;
mod status;
mod taler_util;
-mod sql;
pub struct WireState {
confirmation: AtomicU16,
@@ -40,15 +43,14 @@ pub struct WireState {
fn main() {
taler_common::log::init();
- let config = taler_common::config::Config::load_from_file(
- std::env::args_os().nth(1).expect("Missing conf path arg"),
- );
+ let config = load_btc_config(std::env::args_os().nth(1).expect("Missing conf path arg"));
let data_dir = config
- .btc_data_dir
+ .init
+ .data_dir
.as_ref()
.cloned()
.unwrap_or_else(default_data_dir);
- let config: &'static Config = Box::leak(Box::new(config));
+ let config: &'static BtcConfig = Box::leak(Box::new(config));
let btc_config = BitcoinConfig::load(&data_dir).unwrap();
#[cfg(feature = "fail")]
@@ -76,12 +78,10 @@ fn main() {
let rpc_analysis = AutoReconnectRPC::new(btc_config.clone(), WIRE_WALLET_NAME);
let rpc_worker = AutoReconnectRPC::new(btc_config, WIRE_WALLET_NAME);
- let db_watcher = AutoReconnectSql::new(&config.db_url);
- let db_analysis = AutoReconnectSql::new(&config.db_url);
- let db_worker = AutoReconnectSql::new(&config.db_url);
- named_spawn("watcher", move || {
- watcher(rpc_watcher, db_watcher)
- });
+ let db_watcher = AutoReconnectSql::new(&config.init.db_url);
+ let db_analysis = AutoReconnectSql::new(&config.init.db_url);
+ let db_worker = AutoReconnectSql::new(&config.init.db_url);
+ named_spawn("watcher", move || watcher(rpc_watcher, db_watcher));
named_spawn("analysis", move || {
analysis(rpc_analysis, db_analysis, config, state)
});
@@ -94,5 +94,8 @@ where
F: Send + 'static,
T: Send + 'static,
{
- std::thread::Builder::new().name(name.into()).spawn(f).unwrap()
+ std::thread::Builder::new()
+ .name(name.into())
+ .spawn(f)
+ .unwrap()
}
diff --git a/db/btc.sql b/db/btc.sql
@@ -23,9 +23,9 @@ CREATE TABLE tx_out (
debit_acc TEXT NOT NULL,
credit_acc TEXT NOT NULL,
exchange_url TEXT NOT NULL,
+ request_uid BYTEA UNIQUE,
status SMALLINT NOT NULL DEFAULT 0,
- txid BYTEA UNIQUE,
- request_uid BYTEA UNIQUE
+ txid BYTEA UNIQUE
);
-- Bounced transaction
diff --git a/db/eth.sql b/db/eth.sql
@@ -0,0 +1,38 @@
+-- Key value state
+CREATE TABLE state (
+ name TEXT PRIMARY KEY,
+ value BYTEA NOT NULL
+);
+
+-- Incoming transactions
+CREATE TABLE tx_in (
+ id SERIAL PRIMARY KEY,
+ _date TIMESTAMP NOT NULL DEFAULT now(),
+ amount TEXT NOT NULL,
+ reserve_pub BYTEA NOT NULL UNIQUE,
+ debit_acc TEXT NOT NULL,
+ credit_acc TEXT NOT NULL
+);
+
+-- Outgoing transactions
+CREATE TABLE tx_out (
+ id SERIAL PRIMARY KEY,
+ _date TIMESTAMP NOT NULL DEFAULT now(),
+ amount TEXT NOT NULL,
+ wtid BYTEA NOT NULL UNIQUE,
+ debit_acc TEXT NOT NULL,
+ credit_acc TEXT NOT NULL,
+ exchange_url TEXT NOT NULL,
+ request_uid BYTEA UNIQUE,
+ status SMALLINT NOT NULL DEFAULT 0,
+ txid BYTEA UNIQUE
+);
+
+-- Bounced transaction
+CREATE TABLE bounce (
+ id SERIAL PRIMARY KEY,
+ bounced BYTEA UNIQUE NOT NULL,
+ txid BYTEA UNIQUE,
+ _date TIMESTAMP NOT NULL DEFAULT now(),
+ status SMALLINT NOT NULL DEFAULT 0
+)
+\ No newline at end of file
diff --git a/eth-wire/Cargo.toml b/eth-wire/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "eth-wire"
+version = "0.1.0"
+edition = "2021"
+license = "AGPL-3.0-or-later"
+
+[dependencies]
+# Cli args
+argh = "0.1.7"
+# Serialization library
+serde = { version = "1.0.136", features = ["derive"] }
+serde_json = "1.0.78"
+serde_repr = "0.1.7"
+hex = "0.4.3"
+# Ethereum serializable types
+ethereum-types = { version = "0.12.1", default-features = false, features = [
+ "serialize",
+] }
+# Error macros
+thiserror = "1.0.30"
+# Optimized uri binary format
+uri-pack = { path = "../uri-pack" }
+# Taler libs
+taler-common = { path = "../taler-common" }
diff --git a/eth-wire/src/bin/eth-test.rs b/eth-wire/src/bin/eth-test.rs
@@ -0,0 +1,140 @@
+/*
+ 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 std::{panic::AssertUnwindSafe, str::FromStr};
+
+use eth_wire::{
+ metadata::{InMetadata, OutMetadata},
+ rpc::{self, Rpc},
+};
+use ethereum_types::{H256, U256};
+use taler_common::{config::load_eth_config, rand_slice, url::Url};
+
+pub fn main() {
+ let path = std::env::args().nth(1).unwrap();
+ let config = load_eth_config(path);
+ let mut rpc = Rpc::new(config.init.data_dir.unwrap().join("geth.ipc")).unwrap();
+
+ let accounts = rpc.list_accounts().unwrap();
+ for account in &accounts {
+ rpc.unlock_account(&account, "password").unwrap();
+ }
+ let wire = accounts[0];
+ let client = accounts[1];
+ let reserve = accounts[2];
+
+ let test_value = U256::from(15000u32);
+ let bounce_value = U256::from(10000u32);
+ let test_url = Url::from_str("http://test.com").unwrap();
+
+ let mut runner = TestRunner::new();
+
+ runner.test("Deposit", || {
+ let rng = rand_slice();
+ let hash = rpc.deposit(client, wire, test_value, rng).unwrap();
+ assert!(tx_pending(&mut rpc, &hash).unwrap());
+ mine_pending(&mut rpc).unwrap();
+ assert!(!tx_pending(&mut rpc, &hash).unwrap());
+ let tx = rpc.get_transaction(&hash).unwrap().unwrap();
+ let metadata = InMetadata::decode(&tx.input).unwrap();
+ assert!(matches!(metadata, InMetadata::Deposit { reserve_pub } if reserve_pub == rng));
+ assert_eq!(tx.value, test_value);
+ });
+
+ runner.test("Withdraw", || {
+ let rng = rand_slice();
+ let hash = rpc
+ .withdraw(wire, client, test_value, rng, test_url.clone())
+ .unwrap();
+ assert!(tx_pending(&mut rpc, &hash).unwrap());
+ mine_pending(&mut rpc).unwrap();
+ assert!(!tx_pending(&mut rpc, &hash).unwrap());
+ let tx = rpc.get_transaction(&hash).unwrap().unwrap();
+ let metadata = OutMetadata::decode(&tx.input).unwrap();
+ assert!(
+ matches!(metadata, OutMetadata::Withdraw { wtid, url } if wtid == rng && url == url)
+ );
+ assert_eq!(tx.value, test_value);
+ });
+
+ runner.test("Bounce", || {
+ let rng = rand_slice();
+ let deposit = rpc.deposit(client, wire, test_value, rng).unwrap();
+ let hash = rpc.bounce(deposit, bounce_value).unwrap();
+ assert!(tx_pending(&mut rpc, &hash).unwrap());
+ mine_pending(&mut rpc).unwrap();
+ assert!(!tx_pending(&mut rpc, &hash).unwrap());
+ let tx = rpc.get_transaction(&hash).unwrap().unwrap();
+ let metadata = OutMetadata::decode(&tx.input).unwrap();
+ assert!(matches!(metadata, OutMetadata::Bounce { bounced } if bounced == deposit));
+ assert_eq!(tx.value, test_value - bounce_value);
+ });
+
+ runner.conclude();
+}
+
+/// Check a specific transaction is pending
+fn tx_pending(rpc: &mut Rpc, id: &H256) -> rpc::Result<bool> {
+ Ok(rpc.pending_transactions()?.iter().any(|t| t.hash == *id))
+}
+
+/// Mine pending transactions
+fn mine_pending(rpc: &mut Rpc) -> rpc::Result<()> {
+ let mut notifier = rpc.subscribe_new_head()?;
+ rpc.miner_start()?;
+ while !rpc.pending_transactions()?.is_empty() {
+ notifier.next()?;
+ }
+ rpc.miner_stop()?;
+ Ok(())
+}
+
+/// Run test track success and errors
+struct TestRunner {
+ nb_ok: usize,
+ nb_err: usize,
+}
+
+impl TestRunner {
+ fn new() -> Self {
+ Self {
+ nb_err: 0,
+ nb_ok: 0,
+ }
+ }
+
+ fn test(&mut self, name: &str, test: impl FnOnce()) {
+ println!("{}", name);
+
+ let result = std::panic::catch_unwind(AssertUnwindSafe(test));
+ if result.is_ok() {
+ println!("OK");
+ self.nb_ok += 1;
+ } else {
+ println!("ERR");
+ self.nb_err += 1;
+ }
+ }
+
+ /// Wait for tests completion and print results
+ fn conclude(self) {
+ println!(
+ "Result for {} tests: {} ok and {} err",
+ self.nb_ok + self.nb_err,
+ self.nb_ok,
+ self.nb_err
+ );
+ }
+}
diff --git a/eth-wire/src/bin/eth-wire-cli.rs b/eth-wire/src/bin/eth-wire-cli.rs
@@ -0,0 +1,85 @@
+/*
+ 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 eth_wire::{rpc::Rpc, BlockState};
+use taler_common::{
+ config::{Config, CoreConfig},
+ postgres::{Client, NoTls},
+};
+
+fn main() {
+ let args: Vec<_> = std::env::args().collect();
+ let config = CoreConfig::load_from_file(&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.current_block().expect("Failed to get current block");
+ let state = BlockState {
+ hash: block.hash.unwrap(),
+ nb: block.number.unwrap(),
+ };
+ let nb_row = db
+ .execute(
+ "INSERT INTO state (name, value) VALUES ('last_block', $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.nb);
+ }
+
+ // Load previous address
+ let mut addresses = rpc.list_accounts().expect("Failed to get accounts");
+ if addresses.is_empty() {
+ addresses = rpc
+ .new_account("password")
+ .expect("Failed creating account");
+ println!("Created new wallet");
+ } else {
+ println!("Created new wallet");
+ }
+
+ let addr = addresses[0];
+ 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
diff --git a/eth-wire/src/lib.rs b/eth-wire/src/lib.rs
@@ -0,0 +1,122 @@
+/*
+ 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 ethereum_types::{Address, H256, U256, U64};
+use metadata::{InMetadata, OutMetadata};
+use rpc::hex::Hex;
+use taler_common::url::Url;
+
+pub mod metadata;
+pub mod rpc;
+
+impl rpc::Rpc {
+ pub fn deposit(
+ &mut self,
+ from: Address,
+ to: Address,
+ value: U256,
+ reserve_pub: [u8; 32],
+ ) -> rpc::Result<H256> {
+ let metadata = InMetadata::Deposit { reserve_pub };
+ self.send_transaction(&rpc::TransactionRequest {
+ from,
+ to,
+ value,
+ gas: None,
+ gas_price: None,
+ data: Hex(metadata.encode()),
+ })
+ }
+
+ pub fn withdraw(
+ &mut self,
+ from: Address,
+ to: Address,
+ value: U256,
+ wtid: [u8; 32],
+ url: Url,
+ ) -> rpc::Result<H256> {
+ let metadata = OutMetadata::Withdraw { wtid, url };
+ self.send_transaction(&rpc::TransactionRequest {
+ from,
+ to,
+ value,
+ gas: None,
+ gas_price: None,
+ data: Hex(metadata.encode()),
+ })
+ }
+
+ pub fn bounce(&mut self, hash: H256, bounce_fee: U256) -> rpc::Result<H256> {
+ let tx = self
+ .get_transaction(&hash)?
+ .expect("Cannot bounce a non existent transaction");
+ let bounce_value = tx.value.saturating_sub(bounce_fee);
+ let metadata = OutMetadata::Bounce { bounced: hash };
+ // TODO do not bounce empty amount
+ self.send_transaction(&rpc::TransactionRequest {
+ from: tx.to.expect("Cannot bounce contract transaction"),
+ to: tx.from.expect("Cannot bounce coinbase transaction"),
+ value: bounce_value,
+ gas: None,
+ gas_price: None,
+ data: Hex(metadata.encode()),
+ })
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct BlockState {
+ pub hash: H256,
+ pub nb: U64,
+}
+
+impl BlockState {
+ pub fn to_bytes(&self) -> [u8; 40] {
+ let mut bytes = [0; 40];
+ bytes[..32].copy_from_slice(self.hash.as_bytes());
+ self.nb.to_little_endian(&mut bytes[32..]);
+ bytes
+ }
+
+ pub fn from_bytes(bytes: &[u8; 40]) -> Self {
+ Self {
+ hash: H256::from_slice(&bytes[..32]),
+ nb: U64::from_little_endian(&bytes[32..]),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use ethereum_types::{H256, U64};
+ use taler_common::{rand::random, rand_slice};
+
+ use crate::BlockState;
+
+ #[test]
+ fn to_from_bytes_block_state() {
+ for _ in 0..4 {
+ let state = BlockState {
+ hash: H256::from_slice(&rand_slice::<32>()),
+ nb: U64::from(random::<u64>()),
+ };
+ let encoded = state.to_bytes();
+ let decoded = BlockState::from_bytes(&encoded);
+ assert_eq!(state, decoded);
+ }
+ }
+}
diff --git a/eth-wire/src/main.rs b/eth-wire/src/main.rs
@@ -0,0 +1,146 @@
+/*
+ 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 std::time::Duration;
+
+use crate::rpc::Rpc;
+
+mod rpc;
+
+fn main() {
+ taler_common::log::init();
+ let home = std::env::var("HOME").unwrap();
+ let mut rpc = Rpc::new(format!("{}/.ethereum/geth.ipc", home)).unwrap();
+
+ /*let config = taler_common::config::Config::load_from_file(
+ std::env::args_os().nth(1).expect("Missing conf path arg"),
+ );*/
+ //println!("Calling accounts.");
+ //let mut accounts = web3.eth().accounts().await?;
+ //println!("Accounts: {:?}", accounts);
+ /*accounts.push("00a329c0648769a73afac7f9381e08fb43dbea72".parse().unwrap());
+
+ println!("Calling balance.");
+ for account in &accounts {
+ let balance = web3.eth().balance(*account, None).await?;
+ println!("Balance of {:?}: {}", account, balance);
+ }
+
+ web3.eth()
+ .send_transaction(TransactionRequest {
+ from: accounts[0],
+ to: Some(accounts[1]),
+ value: Some(U256::exp10(8)),
+ data: Some(Bytes::from(vec![0, 1, 2, 3, 4, 5])),
+ ..Default::default()
+ })
+ .await?;
+
+ println!("Calling balance.");
+ for account in &accounts {
+ let balance = web3.eth().balance(*account, None).await?;
+ println!("Balance of {:?}: {}", account, balance);
+ }
+
+ let filter = web3
+ .eth_filter()
+ .create_logs_filter(
+ FilterBuilder::default()
+ .from_block(BlockNumber::Earliest)
+ .to_block(BlockNumber::Latest)
+ .address(vec![accounts[1]])
+ .build(),
+ )
+ .await?;
+
+ let result = filter.poll().await?;
+ dbg!(result);
+ web3.eth()
+ .send_transaction(TransactionRequest {
+ from: accounts[0],
+ to: Some(accounts[1]),
+ value: Some(U256::exp10(8)),
+ data: Some(Bytes::from(vec![0, 1, 2, 3, 4, 5])),
+ ..Default::default()
+ })
+ .await?;
+
+ println!("Calling balance.");
+ for account in &accounts {
+ let balance = web3.eth().balance(*account, None).await?;
+ println!("Balance of {:?}: {}", account, balance);
+ }
+ let result = filter.poll().await?;
+ dbg!(result);
+
+ let filter = web3.eth_filter().create_blocks_filter().await?;
+ let result = filter.poll().await?;
+ dbg!(result);*/
+
+ //let nb = web3.eth().block_number().await.unwrap().as_u64();
+ //println!("{} blocks", nb);
+
+ /*let mut db = rusty_leveldb::DB::open(
+ format!("{}/.ethereum/geth/chaindata", home),
+ Options::default(),
+ )
+ .unwrap();
+
+ // Getting hash using hashKey
+ let mut key = [b'h', 0, 0, 0, 0, 0, 0, 0, 0, b'n'];
+ U64::from(40).to_big_endian(&mut key[1..9]);
+ dbg!(&key);
+ let block_hash = db.get(&key).unwrap();
+ dbg!(&block_hash);
+
+ let mut key = Vec::new();
+ key.push(b'h');
+ key.extend_from_slice(&40u64.to_be_bytes());
+ key.extend_from_slice(&block_hash);
+ dbg!(&key);
+ let header = db.get(&key).unwrap();
+ //let header = BlockHeader:: ::from(header);
+ dbg!(header);
+
+ return;*/
+ /*let start = Instant::now();
+ let mut nb_block = 0;
+ let mut nb_tx = 0;
+ let mut prev = 0;
+ {
+ let stdout = std::io::stdout();
+ let mut stdout = stdout.lock();
+ while let Some(block) = rpc.block(nb_block).unwrap() {
+ nb_block += 1;
+ nb_tx += block.transactions.len();
+ if nb_block % 1000 == 0 {
+ let elapsed = start.elapsed().as_secs();
+ writeln!(
+ &mut stdout,
+ "{:>5}kb {:>10}t {:>7}+ {:}h{:0>2}m{:0>2}s",
+ nb_block / 1000,
+ nb_tx,
+ nb_tx - prev,
+ elapsed / (60 * 60),
+ (elapsed % (60 * 60)) / 60,
+ elapsed % 60
+ )
+ .ok();
+ prev = nb_tx;
+ }
+ }
+ }
+ println!("Done scanned {} blocks in {:?}", nb_block, start.elapsed());*/
+}
diff --git a/eth-wire/src/metadata.rs b/eth-wire/src/metadata.rs
@@ -0,0 +1,177 @@
+/*
+ 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 taler_common::url::Url;
+use ethereum_types::H256;
+
+#[derive(Debug, Clone, Copy, thiserror::Error)]
+pub enum DecodeErr {
+ #[error("Unknown first byte: {0}")]
+ UnknownFirstByte(u8),
+ #[error(transparent)]
+ UriPack(#[from] uri_pack::DecodeErr),
+ #[error("Unexpected end of file")]
+ UnexpectedEOF,
+}
+
+/// Encoded metadata for outgoing transaction
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum OutMetadata {
+ Withdraw { wtid: [u8; 32], url: Url },
+ Bounce { bounced: H256 },
+}
+
+// We leave a potential special meaning for u8::MAX
+const BOUNCE_BYTE: u8 = u8::MAX - 1;
+
+impl OutMetadata {
+ pub fn encode(&self) -> Vec<u8> {
+ let mut buffer = Vec::new();
+ match self {
+ OutMetadata::Withdraw { wtid, url } => {
+ buffer.push(if url.scheme() == "http" { 1 } else { 0 });
+ buffer.extend_from_slice(wtid);
+ let parts = format!("{}{}", url.domain().unwrap_or(""), url.path());
+ let packed = uri_pack::pack_uri(&parts).unwrap();
+ buffer.extend_from_slice(&packed);
+ }
+ OutMetadata::Bounce { bounced } => {
+ buffer.push(BOUNCE_BYTE);
+ buffer.extend_from_slice(bounced.as_bytes());
+ }
+ }
+ return buffer;
+ }
+
+ pub fn decode(bytes: &[u8]) -> Result<Self, DecodeErr> {
+ if bytes.is_empty() {
+ return Err(DecodeErr::UnexpectedEOF);
+ }
+ match bytes[0] {
+ 0..=1 => {
+ if bytes.len() < 33 {
+ return Err(DecodeErr::UnexpectedEOF);
+ }
+ let packed = format!(
+ "http{}://{}",
+ if bytes[0] == 0 { "s" } else { "" },
+ uri_pack::unpack_uri(&bytes[33..])?,
+ );
+ let url = Url::parse(&packed).unwrap();
+ Ok(OutMetadata::Withdraw {
+ wtid: bytes[1..33].try_into().unwrap(),
+ url,
+ })
+ }
+ BOUNCE_BYTE => {
+ if bytes.len() < 33 {
+ return Err(DecodeErr::UnexpectedEOF);
+ }
+ Ok(OutMetadata::Bounce {
+ bounced: H256::from_slice(&bytes[1..33]),
+ })
+ }
+ unknown => Err(DecodeErr::UnknownFirstByte(unknown)),
+ }
+ }
+}
+
+/// Encoded metadata for incoming transaction
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum InMetadata {
+ Deposit { reserve_pub: [u8; 32] },
+}
+
+impl InMetadata {
+ pub fn encode(&self) -> Vec<u8> {
+ let mut buffer = Vec::new();
+ match self {
+ InMetadata::Deposit { reserve_pub } => {
+ buffer.push(0);
+ buffer.extend_from_slice(reserve_pub);
+ }
+ }
+ return buffer;
+ }
+
+ pub fn decode(bytes: &[u8]) -> Result<Self, DecodeErr> {
+ if bytes.is_empty() {
+ return Err(DecodeErr::UnexpectedEOF);
+ }
+ match bytes[0] {
+ 0 => {
+ if bytes.len() < 33 {
+ return Err(DecodeErr::UnexpectedEOF);
+ }
+ Ok(InMetadata::Deposit {
+ reserve_pub: bytes[1..33].try_into().unwrap(),
+ })
+ }
+ unknown => Err(DecodeErr::UnknownFirstByte(unknown)),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use taler_common::{rand_slice, url::Url};
+ use ethereum_types::H256;
+
+ use crate::metadata::{InMetadata, OutMetadata};
+
+ #[test]
+ fn decode_encode_deposit() {
+ for _ in 0..4 {
+ let metadata = InMetadata::Deposit {
+ reserve_pub: rand_slice(),
+ };
+ let encoded = metadata.encode();
+ let decoded = InMetadata::decode(&encoded).unwrap();
+ assert_eq!(decoded, metadata);
+ }
+ }
+
+ #[test]
+ fn decode_encode_withdraw() {
+ 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 wtid = rand_slice();
+ let url = Url::parse(url).unwrap();
+ let metadata = OutMetadata::Withdraw { wtid, url };
+ let encoded = metadata.encode();
+ let decoded = OutMetadata::decode(&encoded).unwrap();
+ assert_eq!(decoded, metadata);
+ }
+ }
+
+ #[test]
+ fn decode_encode_bounce() {
+ for _ in 0..4 {
+ let id: [u8; 32] = rand_slice();
+ let metadata = OutMetadata::Bounce {
+ bounced: H256::from_slice(&id),
+ };
+ let encoded = metadata.encode();
+ let decoded = OutMetadata::decode(&encoded).unwrap();
+ assert_eq!(decoded, metadata);
+ }
+ }
+}
diff --git a/eth-wire/src/rpc.rs b/eth-wire/src/rpc.rs
@@ -0,0 +1,400 @@
+/*
+ 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/>
+*/
+//! This is a very simple RPC client designed only for a specific geth version
+//! and to use on an secure unix domain socket to a trusted node
+//!
+//! We only parse the thing we actually use, this reduce memory usage and
+//! make our code more compatible with future deprecation
+
+use ethereum_types::{Address, H256, U256, U64};
+use serde::de::DeserializeOwned;
+use serde_json::error::Category;
+use std::{
+ fmt::Debug,
+ io::{self, BufWriter, ErrorKind, Read, Write},
+ marker::PhantomData,
+ os::unix::net::UnixStream,
+ path::{Path, PathBuf},
+};
+
+use self::hex::Hex;
+
+#[derive(Debug, serde::Serialize)]
+struct RpcRequest<'a, T: serde::Serialize> {
+ method: &'a str,
+ id: u64,
+ params: &'a T,
+}
+
+#[derive(Debug, serde::Deserialize)]
+struct RpcResponse<T> {
+ result: Option<T>,
+ error: Option<RpcErr>,
+ id: u64,
+}
+
+#[derive(Debug, serde::Deserialize)]
+struct RpcErr {
+ code: i64,
+ message: String,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("{0:?}")]
+ Transport(#[from] std::io::Error),
+ #[error("{code:?} - {msg}")]
+ RPC { code: i64, msg: String },
+ #[error("JSON: {0}")]
+ Json(#[from] serde_json::Error),
+ #[error("No result or error")]
+ Null,
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+const EMPTY: [(); 0] = [];
+
+pub trait RpcTrait {}
+
+/// Bitcoin RPC connection
+pub struct Rpc {
+ path: PathBuf,
+ id: u64,
+ conn: BufWriter<UnixStream>,
+ read_buf: Vec<u8>,
+ cursor: usize,
+}
+
+impl Rpc {
+ pub fn new(path: impl AsRef<Path>) -> io::Result<Self> {
+ let conn = UnixStream::connect(&path)?;
+
+ Ok(Self {
+ path: path.as_ref().to_path_buf(),
+ id: 0,
+ conn: BufWriter::new(conn),
+ read_buf: vec![0u8; 8 * 1024],
+ cursor: 0,
+ })
+ }
+
+ fn send(&mut self, method: &str, params: &impl serde::Serialize) -> Result<()> {
+ // TODO rethink timeout
+ let request = RpcRequest {
+ method,
+ id: self.id,
+ params,
+ };
+
+ // Send request
+ serde_json::to_writer(&mut self.conn, &request)?;
+ self.conn.flush()?;
+ Ok(())
+ }
+
+ fn receive<T>(&mut self) -> Result<T>
+ where
+ T: serde::de::DeserializeOwned + Debug,
+ {
+ loop {
+ if self.cursor == self.read_buf.len() {
+ self.read_buf.resize(self.cursor * 2, 0);
+ }
+ match self.conn.get_mut().read(&mut self.read_buf[self.cursor..]) {
+ Ok(nb) => {
+ self.cursor += nb;
+ let mut de: serde_json::StreamDeserializer<_, T> =
+ serde_json::Deserializer::from_slice(&self.read_buf[..self.cursor])
+ .into_iter();
+
+ if let Some(result) = de.next() {
+ match result {
+ Ok(response) => {
+ let read = de.byte_offset();
+ self.read_buf.copy_within(read..self.cursor, 0);
+ self.cursor -= read;
+ return Ok(response);
+ }
+ Err(err) if err.classify() == Category::Eof => {
+ if nb == 0 {
+ return Err(std::io::Error::new(
+ ErrorKind::UnexpectedEof,
+ "Stream EOF",
+ ))?;
+ }
+ }
+ Err(e) => Err(e)?,
+ }
+ }
+ }
+ Err(e) if e.kind() == ErrorKind::Interrupted => {}
+ Err(e) => Err(e)?,
+ }
+ }
+ }
+
+ fn call<T>(&mut self, method: &str, params: &impl serde::Serialize) -> Result<T>
+ where
+ T: serde::de::DeserializeOwned + Debug,
+ {
+ self.send(method, params)?;
+ let response: RpcResponse<T> = self.receive()?;
+
+ assert_eq!(self.id, response.id);
+ self.id += 1;
+ return if let Some(ok) = response.result {
+ Ok(ok)
+ } else {
+ Err(match response.error {
+ Some(err) => Error::RPC {
+ code: err.code,
+ msg: err.message,
+ },
+ None => Error::Null,
+ })
+ };
+ }
+
+ pub fn list_accounts(&mut self) -> Result<Vec<Address>> {
+ self.call("personal_listAccounts", &EMPTY)
+ }
+
+ pub fn new_account(&mut self, passwd: &str) -> Result<Vec<Address>> {
+ self.call("personal_newAccount", &[passwd])
+ }
+
+ pub fn import_account(&mut self, hex: &str, passwd: &str) -> Result<bool> {
+ self.call("personal_importRawKey", &(hex, passwd))
+ }
+
+ pub fn unlock_account(&mut self, account: &Address, passwd: &str) -> Result<bool> {
+ self.call("personal_unlockAccount", &(account, passwd, 0))
+ }
+
+ pub fn get_transaction(&mut self, hash: &H256) -> Result<Option<Transaction>> {
+ self.call("eth_getTransactionByHash", &[hash])
+ }
+
+ pub fn send_transaction(&mut self, params: &TransactionRequest) -> Result<H256> {
+ self.call("eth_sendTransaction", &[params])
+ }
+
+ pub fn block(&mut self, nb: U64) -> Result<Option<Block>> {
+ self.call("eth_getBlockByNumber", &(nb, &true))
+ }
+
+ pub fn pending_transactions(&mut self) -> Result<Vec<Transaction>> {
+ self.call("eth_pendingTransactions", &EMPTY)
+ }
+
+ pub fn miner_start(&mut self) -> Result<()> {
+ match self.call("miner_start", &[1]) {
+ Err(Error::Null) => Ok(()),
+ i => i,
+ }
+ }
+
+ pub fn miner_stop(&mut self) -> Result<()> {
+ match self.call("miner_stop", &EMPTY) {
+ Err(Error::Null) => Ok(()),
+ i => i,
+ }
+ }
+
+ pub fn subscribe_new_head(&mut self) -> Result<RpcStream<BlockHead>> {
+ let mut rpc = Self::new(&self.path)?;
+ let id: String = rpc.call("eth_subscribe", &["newHeads"])?;
+ Ok(RpcStream::new(rpc, id))
+ }
+
+ pub fn current_block(&mut self) -> Result<Block> {
+ let number: U64 = self.call("eth_blockNumber", &EMPTY)?;
+ Ok(self.block(number)?.expect("Current block must exist"))
+ }
+}
+
+pub struct RpcStream<T: Debug + DeserializeOwned> {
+ rpc: Rpc,
+ id: String,
+ phantom: PhantomData<T>,
+}
+
+impl<T: Debug + DeserializeOwned> RpcStream<T> {
+ fn new(rpc: Rpc, id: String) -> Self {
+ Self {
+ rpc,
+ id,
+ phantom: PhantomData,
+ }
+ }
+
+ pub fn next(&mut self) -> Result<T> {
+ let notification: Wrapper<T> = self.rpc.receive()?;
+ let notification = notification.params;
+ assert_eq!(self.id, notification.subscription);
+ Ok(notification.result)
+ }
+}
+
+#[derive(Debug, serde::Deserialize)]
+pub struct Notification<T> {
+ subscription: String,
+ result: T,
+}
+
+#[derive(Debug, serde::Deserialize)]
+struct Wrapper<T> {
+ params: Notification<T>,
+}
+
+#[derive(Debug, serde::Deserialize)]
+#[serde(untagged)]
+pub enum NotifEnd<T> {
+ Notification(Notification<T>),
+ End(bool),
+}
+
+#[derive(Debug, serde::Deserialize)]
+pub struct Block {
+ /// Hash of the block
+ pub hash: Option<H256>,
+ /// Block number (None if pending)
+ pub number: Option<U64>,
+ /// Hash of the parent
+ #[serde(rename = "parentHash")]
+ pub parent_hash: H256,
+ /// Transactions
+ pub transactions: Vec<Transaction>,
+}
+
+#[derive(Debug, serde::Deserialize)]
+pub struct BlockHead {}
+
+/// Description of a Transaction, pending or in the chain.
+#[derive(Debug, serde::Deserialize)]
+pub struct Transaction {
+ /// Hash
+ pub hash: H256,
+ /// Sender address (None when coinbase)
+ pub from: Option<Address>,
+ /// Recipient address (None when contract creation)
+ pub to: Option<Address>,
+ /// Transferred value
+ pub value: U256,
+ /// Input data
+ pub input: Hex,
+}
+
+/// Send Transaction Parameters
+#[derive(Debug, serde::Serialize)]
+pub struct TransactionRequest {
+ /// Sender address
+ pub from: Address,
+ /// Recipient address
+ pub to: Address,
+ /// Transferred value
+ pub value: U256,
+ /// Supplied gas (None for sensible default)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub gas: Option<U256>,
+ /// Gas price (None for sensible default)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(rename = "gasPrice")]
+ pub gas_price: Option<U256>,
+ /// Transaction data
+ pub data: Hex,
+}
+
+pub mod hex {
+ use std::{
+ fmt,
+ ops::{Deref, DerefMut},
+ };
+
+ use serde::{
+ de::{Error, Unexpected, Visitor},
+ Deserialize, Deserializer, Serialize, Serializer,
+ };
+
+ /// Raw bytes wrapper
+ #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
+ pub struct Hex(pub Vec<u8>);
+
+ impl Deref for Hex {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl DerefMut for Hex {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ impl Serialize for Hex {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut serialized = "0x".to_owned();
+ serialized.push_str(&hex::encode(&self.0));
+ serializer.serialize_str(serialized.as_ref())
+ }
+ }
+
+ impl<'a> Deserialize<'a> for Hex {
+ fn deserialize<D>(deserializer: D) -> Result<Hex, D::Error>
+ where
+ D: Deserializer<'a>,
+ {
+ deserializer.deserialize_identifier(BytesVisitor)
+ }
+ }
+
+ struct BytesVisitor;
+
+ impl<'a> Visitor<'a> for BytesVisitor {
+ type Value = Hex;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ write!(formatter, "a 0x-prefixed hex-encoded vector of bytes")
+ }
+
+ fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ if value.len() >= 2 && &value[0..2] == "0x" {
+ let bytes = hex::decode(&value[2..])
+ .map_err(|e| Error::custom(format!("Invalid hex: {}", e)))?;
+ Ok(Hex(bytes))
+ } else {
+ Err(Error::invalid_value(Unexpected::Str(value), &"0x prefix"))
+ }
+ }
+
+ fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ self.visit_str(value.as_ref())
+ }
+ }
+}
diff --git a/eth_wire/Cargo.toml b/eth_wire/Cargo.toml
@@ -1,21 +0,0 @@
-[package]
-name = "eth_wire"
-version = "0.1.0"
-edition = "2021"
-license = "AGPL-3.0-or-later"
-
-[dependencies]
-# Cli args
-argh = "0.1.7"
-# Serialization library
-serde = { version = "1.0.133", features = ["derive"] }
-serde_json = "1.0.75"
-serde_repr = "0.1.7"
-# Ethereum RPC client
-web3 = { version = "0.17.0", default-features = false}
-# Error macros
-thiserror = "1.0.30"
-# Optimized uri binary format
-uri-pack = { path = "../uri-pack" }
-# Taler libs
-taler-common = { path = "../taler-common" }
diff --git a/eth_wire/src/bin/eth_test.rs b/eth_wire/src/bin/eth_test.rs
@@ -1,139 +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 std::{panic::AssertUnwindSafe, str::FromStr};
-
-use eth_wire::{
- metadata::{InMetadata, OutMetadata},
- rpc::{self, Rpc},
-};
-use taler_common::{rand_slice, url::Url};
-use web3::types::{H256, U256};
-
-pub fn main() {
- let path = std::env::args().nth(1).unwrap();
- let mut rpc = Rpc::new(path).unwrap();
-
- let accounts = rpc.list_accounts().unwrap();
- for account in &accounts {
- rpc.unlock_account(&account, "password").unwrap();
- }
- let wire = accounts[0];
- let client = accounts[1];
- let reserve = accounts[2];
-
- let test_value = U256::from(1500032);
- let bounce_value = U256::from(10000u32);
- let test_url = Url::from_str("http://test.com").unwrap();
-
- let mut runner = TestRunner::new();
-
- runner.test("Deposit", || {
- let rng = rand_slice();
- let hash = rpc.deposit(client, wire, test_value, rng).unwrap();
- assert!(tx_pending(&mut rpc, &hash).unwrap());
- mine_pending(&mut rpc).unwrap();
- assert!(!tx_pending(&mut rpc, &hash).unwrap());
- let tx = rpc.get_transaction(&hash).unwrap().unwrap();
- let metadata = InMetadata::decode(&tx.input.0).unwrap();
- assert!(matches!(metadata, InMetadata::Deposit { reserve_pub } if reserve_pub == rng));
- assert_eq!(tx.value, test_value);
- });
-
- runner.test("Withdraw", || {
- let rng = rand_slice();
- let hash = rpc
- .withdraw(wire, client, test_value, rng, test_url.clone())
- .unwrap();
- assert!(tx_pending(&mut rpc, &hash).unwrap());
- mine_pending(&mut rpc).unwrap();
- assert!(!tx_pending(&mut rpc, &hash).unwrap());
- let tx = rpc.get_transaction(&hash).unwrap().unwrap();
- let metadata = OutMetadata::decode(&tx.input.0).unwrap();
- assert!(
- matches!(metadata, OutMetadata::Withdraw { wtid, url } if wtid == rng && url == url)
- );
- assert_eq!(tx.value, test_value);
- });
-
- runner.test("Bounce", || {
- let rng = rand_slice();
- let deposit = rpc.deposit(client, wire, test_value, rng).unwrap();
- let hash = rpc.bounce(deposit, bounce_value).unwrap();
- assert!(tx_pending(&mut rpc, &hash).unwrap());
- mine_pending(&mut rpc).unwrap();
- assert!(!tx_pending(&mut rpc, &hash).unwrap());
- let tx = rpc.get_transaction(&hash).unwrap().unwrap();
- let metadata = OutMetadata::decode(&tx.input.0).unwrap();
- assert!(matches!(metadata, OutMetadata::Bounce { bounced } if bounced == deposit));
- assert_eq!(tx.value, test_value - bounce_value);
- });
-
- runner.conclude();
-}
-
-/// Check a specific transaction is pending
-fn tx_pending(rpc: &mut Rpc, id: &H256) -> rpc::Result<bool> {
- Ok(rpc.pending_transactions()?.iter().any(|t| t.hash == *id))
-}
-
-/// Mine pending transactions
-fn mine_pending(rpc: &mut Rpc) -> rpc::Result<()> {
- let mut notifier = rpc.subscribe_new_head()?;
- rpc.miner_start()?;
- while !rpc.pending_transactions()?.is_empty() {
- notifier.next()?;
- }
- rpc.miner_stop()?;
- Ok(())
-}
-
-/// Run test track success and errors
-struct TestRunner {
- nb_ok: usize,
- nb_err: usize,
-}
-
-impl TestRunner {
- fn new() -> Self {
- Self {
- nb_err: 0,
- nb_ok: 0,
- }
- }
-
- fn test(&mut self, name: &str, test: impl FnOnce()) {
- println!("{}", name);
-
- let result = std::panic::catch_unwind(AssertUnwindSafe(test));
- if result.is_ok() {
- println!("OK");
- self.nb_ok += 1;
- } else {
- println!("ERR");
- self.nb_err += 1;
- }
- }
-
- /// Wait for tests completion and print results
- fn conclude(self) {
- println!(
- "Result for {} tests: {} ok and {} err",
- self.nb_ok + self.nb_err,
- self.nb_ok,
- self.nb_err
- );
- }
-}
diff --git a/eth_wire/src/lib.rs b/eth_wire/src/lib.rs
@@ -1,84 +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 metadata::{InMetadata, OutMetadata};
-use taler_common::url::Url;
-use web3::{
- ethabi::Address,
- types::{Bytes, H256, U256},
-};
-
-pub mod metadata;
-pub mod rpc;
-
-impl rpc::Rpc {
- pub fn deposit(
- &mut self,
- from: Address,
- to: Address,
- value: U256,
- reserve_pub: [u8; 32],
- ) -> rpc::Result<H256> {
- let metadata = InMetadata::Deposit { reserve_pub };
- let encoded = metadata.encode();
- self.send_transaction(&rpc::TransactionRequest {
- from,
- to,
- value,
- gas: None,
- gas_price: None,
- data: Some(Bytes::from(encoded)),
- })
- }
-
- pub fn withdraw(
- &mut self,
- from: Address,
- to: Address,
- value: U256,
- wtid: [u8; 32],
- url: Url,
- ) -> rpc::Result<H256> {
- let metadata = OutMetadata::Withdraw { wtid, url };
- let encoded = metadata.encode();
- self.send_transaction(&rpc::TransactionRequest {
- from,
- to,
- value,
- gas: None,
- gas_price: None,
- data: Some(Bytes::from(encoded)),
- })
- }
-
- pub fn bounce(&mut self, hash: H256, bounce_fee: U256) -> rpc::Result<H256> {
- let tx = self
- .get_transaction(&hash)?
- .expect("Cannot bounce a non existent transaction");
- let bounce_value = tx.value.saturating_sub(bounce_fee);
- let metadata = OutMetadata::Bounce { bounced: hash };
- let encoded = metadata.encode();
- // TODO do not bounce empty amount
- self.send_transaction(&rpc::TransactionRequest {
- from: tx.to.expect("Cannot bounce contract transaction"),
- to: tx.from.expect("Cannot bounce coinbase transaction"),
- value: bounce_value,
- gas: None,
- gas_price: None,
- data: Some(Bytes::from(encoded)),
- })
- }
-}
diff --git a/eth_wire/src/main.rs b/eth_wire/src/main.rs
@@ -1,165 +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 std::time::Duration;
-
-use web3::types::SyncState;
-
-use crate::rpc::Rpc;
-
-mod rpc;
-
-fn main() {
- taler_common::log::init();
- let home = std::env::var("HOME").unwrap();
- let mut rpc = Rpc::new(format!("{}/.ethereum/geth.ipc", home)).unwrap();
-
- let config = taler_common::config::Config::load_from_file(
- std::env::args_os().nth(1).expect("Missing conf path arg"),
- );
- //println!("Calling accounts.");
- //let mut accounts = web3.eth().accounts().await?;
- //println!("Accounts: {:?}", accounts);
- /*accounts.push("00a329c0648769a73afac7f9381e08fb43dbea72".parse().unwrap());
-
- println!("Calling balance.");
- for account in &accounts {
- let balance = web3.eth().balance(*account, None).await?;
- println!("Balance of {:?}: {}", account, balance);
- }
-
- web3.eth()
- .send_transaction(TransactionRequest {
- from: accounts[0],
- to: Some(accounts[1]),
- value: Some(U256::exp10(8)),
- data: Some(Bytes::from(vec![0, 1, 2, 3, 4, 5])),
- ..Default::default()
- })
- .await?;
-
- println!("Calling balance.");
- for account in &accounts {
- let balance = web3.eth().balance(*account, None).await?;
- println!("Balance of {:?}: {}", account, balance);
- }
-
- let filter = web3
- .eth_filter()
- .create_logs_filter(
- FilterBuilder::default()
- .from_block(BlockNumber::Earliest)
- .to_block(BlockNumber::Latest)
- .address(vec![accounts[1]])
- .build(),
- )
- .await?;
-
- let result = filter.poll().await?;
- dbg!(result);
- web3.eth()
- .send_transaction(TransactionRequest {
- from: accounts[0],
- to: Some(accounts[1]),
- value: Some(U256::exp10(8)),
- data: Some(Bytes::from(vec![0, 1, 2, 3, 4, 5])),
- ..Default::default()
- })
- .await?;
-
- println!("Calling balance.");
- for account in &accounts {
- let balance = web3.eth().balance(*account, None).await?;
- println!("Balance of {:?}: {}", account, balance);
- }
- let result = filter.poll().await?;
- dbg!(result);
-
- let filter = web3.eth_filter().create_blocks_filter().await?;
- let result = filter.poll().await?;
- dbg!(result);*/
-
- loop {
- let sync = rpc.syncing().unwrap();
- match sync {
- SyncState::Syncing(info) => {
- println!(
- "{}% ({}/{})",
- info.current_block * 100 / info.highest_block,
- info.current_block,
- info.highest_block
- );
- break;
- std::thread::sleep(Duration::from_secs(60));
- }
- SyncState::NotSyncing => break,
- }
- }
-
- //let nb = web3.eth().block_number().await.unwrap().as_u64();
- //println!("{} blocks", nb);
-
- /*let mut db = rusty_leveldb::DB::open(
- format!("{}/.ethereum/geth/chaindata", home),
- Options::default(),
- )
- .unwrap();
-
- // Getting hash using hashKey
- let mut key = [b'h', 0, 0, 0, 0, 0, 0, 0, 0, b'n'];
- U64::from(40).to_big_endian(&mut key[1..9]);
- dbg!(&key);
- let block_hash = db.get(&key).unwrap();
- dbg!(&block_hash);
-
- let mut key = Vec::new();
- key.push(b'h');
- key.extend_from_slice(&40u64.to_be_bytes());
- key.extend_from_slice(&block_hash);
- dbg!(&key);
- let header = db.get(&key).unwrap();
- //let header = BlockHeader:: ::from(header);
- dbg!(header);
-
- return;*/
- /*let start = Instant::now();
- let mut nb_block = 0;
- let mut nb_tx = 0;
- let mut prev = 0;
- {
- let stdout = std::io::stdout();
- let mut stdout = stdout.lock();
- while let Some(block) = rpc.block(nb_block).unwrap() {
- nb_block += 1;
- nb_tx += block.transactions.len();
- if nb_block % 1000 == 0 {
- let elapsed = start.elapsed().as_secs();
- writeln!(
- &mut stdout,
- "{:>5}kb {:>10}t {:>7}+ {:}h{:0>2}m{:0>2}s",
- nb_block / 1000,
- nb_tx,
- nb_tx - prev,
- elapsed / (60 * 60),
- (elapsed % (60 * 60)) / 60,
- elapsed % 60
- )
- .ok();
- prev = nb_tx;
- }
- }
- }
- println!("Done scanned {} blocks in {:?}", nb_block, start.elapsed());*/
-}
diff --git a/eth_wire/src/metadata.rs b/eth_wire/src/metadata.rs
@@ -1,177 +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 taler_common::url::Url;
-use web3::types::H256;
-
-#[derive(Debug, Clone, Copy, thiserror::Error)]
-pub enum DecodeErr {
- #[error("Unknown first byte: {0}")]
- UnknownFirstByte(u8),
- #[error(transparent)]
- UriPack(#[from] uri_pack::DecodeErr),
- #[error("Unexpected end of file")]
- UnexpectedEOF,
-}
-
-/// Encoded metadata for outgoing transaction
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum OutMetadata {
- Withdraw { wtid: [u8; 32], url: Url },
- Bounce { bounced: H256 },
-}
-
-// We leave a potential special meaning for u8::MAX
-const BOUNCE_BYTE: u8 = u8::MAX - 1;
-
-impl OutMetadata {
- pub fn encode(&self) -> Vec<u8> {
- let mut buffer = Vec::new();
- match self {
- OutMetadata::Withdraw { wtid, url } => {
- buffer.push(if url.scheme() == "http" { 1 } else { 0 });
- buffer.extend_from_slice(wtid);
- let parts = format!("{}{}", url.domain().unwrap_or(""), url.path());
- let packed = uri_pack::pack_uri(&parts).unwrap();
- buffer.extend_from_slice(&packed);
- }
- OutMetadata::Bounce { bounced } => {
- buffer.push(BOUNCE_BYTE);
- buffer.extend_from_slice(bounced.as_bytes());
- }
- }
- return buffer;
- }
-
- pub fn decode(bytes: &[u8]) -> Result<Self, DecodeErr> {
- if bytes.is_empty() {
- return Err(DecodeErr::UnexpectedEOF);
- }
- match bytes[0] {
- 0..=1 => {
- if bytes.len() < 33 {
- return Err(DecodeErr::UnexpectedEOF);
- }
- let packed = format!(
- "http{}://{}",
- if bytes[0] == 0 { "s" } else { "" },
- uri_pack::unpack_uri(&bytes[33..])?,
- );
- let url = Url::parse(&packed).unwrap();
- Ok(OutMetadata::Withdraw {
- wtid: bytes[1..33].try_into().unwrap(),
- url,
- })
- }
- BOUNCE_BYTE => {
- if bytes.len() < 33 {
- return Err(DecodeErr::UnexpectedEOF);
- }
- Ok(OutMetadata::Bounce {
- bounced: H256::from_slice(&bytes[1..33]),
- })
- }
- unknown => Err(DecodeErr::UnknownFirstByte(unknown)),
- }
- }
-}
-
-/// Encoded metadata for incoming transaction
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum InMetadata {
- Deposit { reserve_pub: [u8; 32] },
-}
-
-impl InMetadata {
- pub fn encode(&self) -> Vec<u8> {
- let mut buffer = Vec::new();
- match self {
- InMetadata::Deposit { reserve_pub } => {
- buffer.push(0);
- buffer.extend_from_slice(reserve_pub);
- }
- }
- return buffer;
- }
-
- pub fn decode(bytes: &[u8]) -> Result<Self, DecodeErr> {
- if bytes.is_empty() {
- return Err(DecodeErr::UnexpectedEOF);
- }
- match bytes[0] {
- 0 => {
- if bytes.len() < 33 {
- return Err(DecodeErr::UnexpectedEOF);
- }
- Ok(InMetadata::Deposit {
- reserve_pub: bytes[1..33].try_into().unwrap(),
- })
- }
- unknown => Err(DecodeErr::UnknownFirstByte(unknown)),
- }
- }
-}
-
-#[cfg(test)]
-mod test {
- use taler_common::{rand_slice, url::Url};
- use web3::types::H256;
-
- use crate::metadata::{InMetadata, OutMetadata};
-
- #[test]
- fn decode_encode_deposit() {
- for _ in 0..4 {
- let metadata = InMetadata::Deposit {
- reserve_pub: rand_slice(),
- };
- let encoded = metadata.encode();
- let decoded = InMetadata::decode(&encoded).unwrap();
- assert_eq!(decoded, metadata);
- }
- }
-
- #[test]
- fn decode_encode_withdraw() {
- 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 wtid = rand_slice();
- let url = Url::parse(url).unwrap();
- let metadata = OutMetadata::Withdraw { wtid, url };
- let encoded = metadata.encode();
- let decoded = OutMetadata::decode(&encoded).unwrap();
- assert_eq!(decoded, metadata);
- }
- }
-
- #[test]
- fn decode_encode_bounce() {
- for _ in 0..4 {
- let id: [u8; 32] = rand_slice();
- let metadata = OutMetadata::Bounce {
- bounced: H256::from_slice(&id),
- };
- let encoded = metadata.encode();
- let decoded = OutMetadata::decode(&encoded).unwrap();
- assert_eq!(decoded, metadata);
- }
- }
-}
diff --git a/eth_wire/src/rpc.rs b/eth_wire/src/rpc.rs
@@ -1,316 +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/>
-*/
-//! This is a very simple RPC client designed only for a specific geth version
-//! and to use on an secure unix domain socket to a trusted node
-//!
-//! We only parse the thing we actually use, this reduce memory usage and
-//! make our code more compatible with future deprecation
-
-use serde::de::DeserializeOwned;
-use serde_json::error::Category;
-use std::{
- fmt::Debug,
- io::{self, BufWriter, ErrorKind, Read, Write},
- marker::PhantomData,
- os::unix::net::UnixStream,
- path::{Path, PathBuf},
-};
-use web3::types::{Address, Bytes, SyncState, H256, U256, U64};
-
-#[derive(Debug, serde::Serialize)]
-struct RpcRequest<'a, T: serde::Serialize> {
- method: &'a str,
- id: u64,
- params: &'a T,
-}
-
-#[derive(Debug, serde::Deserialize)]
-struct RpcResponse<T> {
- result: Option<T>,
- error: Option<RpcErr>,
- id: u64,
-}
-
-#[derive(Debug, serde::Deserialize)]
-struct RpcErr {
- code: i64,
- message: String,
-}
-
-#[derive(Debug, thiserror::Error)]
-pub enum Error {
- #[error("{0:?}")]
- Transport(#[from] std::io::Error),
- #[error("{code:?} - {msg}")]
- RPC { code: i64, msg: String },
- #[error("JSON: {0}")]
- Json(#[from] serde_json::Error),
- #[error("No result or error")]
- Null,
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-const EMPTY: [(); 0] = [];
-
-pub trait RpcTrait {}
-
-/// Bitcoin RPC connection
-pub struct Rpc {
- path: PathBuf,
- id: u64,
- conn: BufWriter<UnixStream>,
- read_buf: Vec<u8>,
- cursor: usize,
-}
-
-impl Rpc {
- pub fn new(path: impl AsRef<Path>) -> io::Result<Self> {
- let conn = UnixStream::connect(&path)?;
-
- Ok(Self {
- path: path.as_ref().to_path_buf(),
- id: 0,
- conn: BufWriter::new(conn),
- read_buf: vec![0u8; 8 * 1024],
- cursor: 0,
- })
- }
-
- fn send(&mut self, method: &str, params: &impl serde::Serialize) -> Result<()> {
- // TODO rethink timeout
- let request = RpcRequest {
- method,
- id: self.id,
- params,
- };
-
- // Send request
- serde_json::to_writer(&mut self.conn, &request)?;
- self.conn.flush()?;
- Ok(())
- }
-
- fn receive<T>(&mut self) -> Result<T>
- where
- T: serde::de::DeserializeOwned + Debug,
- {
- loop {
- if self.cursor == self.read_buf.len() {
- self.read_buf.resize(self.cursor * 2, 0);
- }
- match self.conn.get_mut().read(&mut self.read_buf[self.cursor..]) {
- Ok(nb) => {
- self.cursor += nb;
- let mut de: serde_json::StreamDeserializer<_, T> =
- serde_json::Deserializer::from_slice(&self.read_buf[..self.cursor])
- .into_iter();
-
- if let Some(result) = de.next() {
- match result {
- Ok(response) => {
- let read = de.byte_offset();
- self.read_buf.copy_within(read..self.cursor, 0);
- self.cursor -= read;
- return Ok(response);
- }
- Err(err) if err.classify() == Category::Eof => {
- if nb == 0 {
- return Err(std::io::Error::new(
- ErrorKind::UnexpectedEof,
- "Stream EOF",
- ))?;
- }
- }
- Err(e) => Err(e)?,
- }
- }
- }
- Err(e) if e.kind() == ErrorKind::Interrupted => {}
- Err(e) => Err(e)?,
- }
- }
- }
-
- fn call<T>(&mut self, method: &str, params: &impl serde::Serialize) -> Result<T>
- where
- T: serde::de::DeserializeOwned + Debug,
- {
- self.send(method, params)?;
- let response: RpcResponse<T> = self.receive()?;
-
- assert_eq!(self.id, response.id);
- self.id += 1;
- return if let Some(ok) = response.result {
- Ok(ok)
- } else {
- Err(match response.error {
- Some(err) => Error::RPC {
- code: err.code,
- msg: err.message,
- },
- None => Error::Null,
- })
- };
- }
-
- pub fn syncing(&mut self) -> Result<SyncState> {
- self.call("eth_syncing", &EMPTY)
- }
-
- pub fn list_accounts(&mut self) -> Result<Vec<Address>> {
- self.call("personal_listAccounts", &EMPTY)
- }
-
- pub fn new_account(&mut self, passwd: &str) -> Result<Vec<Address>> {
- self.call("personal_newAccount", &[passwd])
- }
-
- pub fn import_account(&mut self, hex: &str, passwd: &str) -> Result<bool> {
- self.call("personal_importRawKey", &(hex, passwd))
- }
-
- pub fn unlock_account(&mut self, account: &Address, passwd: &str) -> Result<bool> {
- self.call("personal_unlockAccount", &(account, passwd, 0))
- }
-
- pub fn get_transaction(&mut self, hash: &H256) -> Result<Option<Transaction>> {
- self.call("eth_getTransactionByHash", &[hash])
- }
-
- pub fn send_transaction(&mut self, params: &TransactionRequest) -> Result<H256> {
- self.call("eth_sendTransaction", &[params])
- }
-
- pub fn block(&mut self, nb: u64) -> Result<Option<Block>> {
- self.call("eth_getBlockByNumber", &(U64::from(nb), &true))
- }
-
- pub fn pending_transactions(&mut self) -> Result<Vec<Transaction>> {
- self.call("eth_pendingTransactions", &EMPTY)
- }
-
- pub fn miner_start(&mut self) -> Result<()> {
- match self.call("miner_start", &[1]) {
- Err(Error::Null) => Ok(()),
- i => i,
- }
- }
-
- pub fn miner_stop(&mut self) -> Result<()> {
- match self.call("miner_stop", &EMPTY) {
- Err(Error::Null) => Ok(()),
- i => i,
- }
- }
-
- pub fn subscribe_new_head(&mut self) -> Result<RpcStream<BlockHead>> {
- let mut rpc = Self::new(&self.path)?;
- let id: String = rpc.call("eth_subscribe", &["newHeads"])?;
- Ok(RpcStream::new(rpc, id))
- }
-}
-
-pub struct RpcStream<T: Debug + DeserializeOwned> {
- rpc: Rpc,
- id: String,
- phantom: PhantomData<T>,
-}
-
-impl<T: Debug + DeserializeOwned> RpcStream<T> {
- fn new(rpc: Rpc, id: String) -> Self {
- Self {
- rpc,
- id,
- phantom: PhantomData,
- }
- }
-
- pub fn next(&mut self) -> Result<T> {
- let notification: Wrapper<T> = self.rpc.receive()?;
- let notification = notification.params;
- assert_eq!(self.id, notification.subscription);
- Ok(notification.result)
- }
-}
-
-#[derive(Debug, serde::Deserialize)]
-pub struct Notification<T> {
- subscription: String,
- result: T,
-}
-
-#[derive(Debug, serde::Deserialize)]
-struct Wrapper<T> {
- params: Notification<T>,
-}
-
-#[derive(Debug, serde::Deserialize)]
-#[serde(untagged)]
-pub enum NotifEnd<T> {
- Notification(Notification<T>),
- End(bool),
-}
-
-#[derive(Debug, serde::Deserialize)]
-pub struct Block {
- /// Hash of the block
- pub hash: Option<H256>,
- /// Hash of the parent
- #[serde(rename = "parentHash")]
- pub parent_hash: H256,
- /// Transactions
- pub transactions: Vec<Transaction>,
-}
-
-#[derive(Debug, serde::Deserialize)]
-pub struct BlockHead {}
-
-/// Description of a Transaction, pending or in the chain.
-#[derive(Debug, serde::Deserialize)]
-pub struct Transaction {
- /// Hash
- pub hash: H256,
- /// Sender address (None when coinbase)
- pub from: Option<Address>,
- /// Recipient address (None when contract creation)
- pub to: Option<Address>,
- /// Transferred value
- pub value: U256,
- /// Input data
- pub input: Bytes,
-}
-
-/// Send Transaction Parameters
-#[derive(Debug, serde::Serialize)]
-pub struct TransactionRequest {
- /// Sender address
- pub from: Address,
- /// Recipient address
- pub to: Address,
- /// Transferred value
- pub value: U256,
- /// Supplied gas (None for sensible default)
- #[serde(skip_serializing_if = "Option::is_none")]
- pub gas: Option<U256>,
- /// Gas price (None for sensible default)
- #[serde(skip_serializing_if = "Option::is_none")]
- #[serde(rename = "gasPrice")]
- pub gas_price: Option<U256>,
- /// Transaction data
- #[serde(skip_serializing_if = "Option::is_none")]
- pub data: Option<Bytes>,
-}
diff --git a/makefile b/makefile
@@ -1,5 +1,6 @@
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 wire-gateway
test_gateway:
diff --git a/taler-common/Cargo.toml b/taler-common/Cargo.toml
@@ -8,11 +8,11 @@ license = "AGPL-3.0-or-later"
[dependencies]
# Serialization framework
-serde = { version = "1.0.133", features = ["derive"] }
+serde = { version = "1.0.136", features = ["derive"] }
# Serialization helper
serde_with = "1.11.0"
# JSON serialization
-serde_json = "1.0.75"
+serde_json = "1.0.78"
# Url format
url = { version = "2.2.2", features = ["serde"] }
# Crockford’s base32
@@ -23,11 +23,11 @@ thiserror = "1.0.30"
rust-ini = "0.17.0"
# Logging
log = "0.4.14"
-flexi_logger = { version = "0.22.2", default-features = false, features = [
+flexi_logger = { version = "0.22.3", default-features = false, features = [
"use_chrono_for_offset", # Temporary hack for multithreaded code https://rustsec.org/advisories/RUSTSEC-2020-0159
] }
# Local timz
-time = { version = "0.3.5", features = ["formatting", "macros"] }
+time = { version = "0.3.7", features = ["formatting", "macros"] }
# Postgres client
postgres = "0.19.2"
# Secure random
diff --git a/taler-common/src/config.rs b/taler-common/src/config.rs
@@ -20,69 +20,115 @@ use std::{
};
use url::Url;
-pub struct InitConfig {
+pub trait Config: Sized {
+ /// Load from a file
+ fn load_from_file(config_file: impl AsRef<Path>) -> Self {
+ let conf = ini::Ini::load_from_file(config_file).expect("Failed to open the config file");
+ let taler = section(&conf, "taler");
+ let currency = require(&taler, "CURRENCY", string);
+ let section_name = match currency.as_str() {
+ "BTC" => "depolymerizer-bitcoin",
+ "ETH" => "depolymerizer-ethereum",
+ currency => unimplemented!("Unsupported currency {}", currency),
+ };
+
+ let dep = section(&conf, section_name);
+ return Self::load_from_ini(&conf, ¤cy, dep);
+ }
+ /// Load from loaded ini file
+ fn load_from_ini(ini: &Ini, currency: &str, dep: &Properties) -> Self;
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CoreConfig {
pub db_url: String,
- pub btc_data_dir: Option<PathBuf>,
+ pub data_dir: Option<PathBuf>,
+ pub currency: String,
}
-impl InitConfig {
- /// Load from a file
- pub fn load_from_file(config_file: impl AsRef<Path>) -> Self {
- let conf = ini::Ini::load_from_file(config_file).unwrap();
- assert(section(&conf, "taler"), "CURRENCY", "BTC");
- let self_conf = section(&conf, "depolymerizer-bitcoin");
+impl Config for CoreConfig {
+ fn load_from_ini(_: &Ini, currency: &str, dep: &Properties) -> Self {
Self {
- db_url: require(self_conf, "DB_URL", string),
- btc_data_dir: path(self_conf, "BTC_DATA_DIR"),
+ db_url: require(dep, "DB_URL", string),
+ data_dir: path(dep, "DATA_DIR"),
+ currency: currency.to_string(),
}
}
}
-/// Taler config with depolymerizer config
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Config {
- pub base_url: Url,
- pub db_url: String,
+pub struct GatewayConfig {
+ pub http_lifetime: Option<u32>,
pub port: u16,
pub unix_path: Option<PathBuf>,
- pub btc_data_dir: Option<PathBuf>,
pub payto: Url,
+ pub init: CoreConfig,
+}
+
+impl Config for GatewayConfig {
+ fn load_from_ini(ini: &Ini, currency: &str, dep: &Properties) -> Self {
+ Self {
+ port: nb(dep, "PORT").unwrap_or(8080),
+ unix_path: path(dep, "UNIXPATH"),
+ payto: require(dep, "PAYTO", url),
+ http_lifetime: nb(dep, "HTTP_LIFETIME")
+ .and_then(|nb| (nb != 0).then(|| Some(nb)))
+ .unwrap_or(None),
+ init: CoreConfig::load_from_ini(ini, currency, dep),
+ }
+ }
+}
+
+// TODO currency name as const generic
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WireConfig<const DEFAULT_FEE: u64, const DEFAULT_CONFIRMATION: u16> {
+ pub base_url: Url,
pub confirmation: u16,
pub bounce_fee: u64,
pub wire_lifetime: Option<u32>,
- pub http_lifetime: Option<u32>,
pub bump_delay: Option<u32>,
+ pub payto: Url,
+ pub init: CoreConfig,
}
-impl Config {
- /// Load from a file
- pub fn load_from_file(config_file: impl AsRef<Path>) -> Self {
- let conf = ini::Ini::load_from_file(config_file).unwrap();
- assert(section(&conf, "taler"), "CURRENCY", "BTC");
- let ex_conf = section(&conf, "exchange");
- let self_conf = section(&conf, "depolymerizer-bitcoin");
+impl<const DEFAULT_FEE: u64, const DEFAULT_CONFIRMATION: u16> Config
+ for WireConfig<DEFAULT_FEE, DEFAULT_CONFIRMATION>
+{
+ fn load_from_ini(ini: &Ini, currency: &str, dep: &Properties) -> Self {
+ let ex = section(ini, "exchange");
Self {
- base_url: require(ex_conf, "BASE_URL", url),
- db_url: require(self_conf, "DB_URL", string),
- port: nb(self_conf, "PORT").unwrap_or(8080),
- unix_path: path(self_conf, "UNIXPATH"),
- btc_data_dir: path(self_conf, "BTC_DATA_DIR"),
- payto: require(self_conf, "PAYTO", url),
- confirmation: nb(self_conf, "CONFIRMATION").unwrap_or(6),
- bounce_fee: nb(self_conf, "BOUNCE_FEE").unwrap_or(1000),
- wire_lifetime: nb(self_conf, "WIRE_LIFETIME")
- .and_then(|nb| (nb != 0).then(|| Some(nb)))
- .unwrap_or(None),
- http_lifetime: nb(self_conf, "HTTP_LIFETIME")
+ base_url: require(ex, "BASE_URL", url),
+ payto: require(dep, "PAYTO", url),
+ confirmation: nb(dep, "CONFIRMATION").unwrap_or(DEFAULT_CONFIRMATION),
+ bounce_fee: nb(dep, "BOUNCE_FEE").unwrap_or(DEFAULT_FEE),
+ wire_lifetime: nb(dep, "WIRE_LIFETIME")
.and_then(|nb| (nb != 0).then(|| Some(nb)))
.unwrap_or(None),
- bump_delay: nb(self_conf, "BUMP_DELAY")
+ bump_delay: nb(dep, "BUMP_DELAY")
.and_then(|nb| (nb != 0).then(|| Some(nb)))
.unwrap_or(None),
+ init: CoreConfig::load_from_ini(ini, currency, dep),
}
}
}
+pub type BtcConfig = WireConfig<1000, 6>;
+
+pub fn load_btc_config(path: impl AsRef<Path>) -> BtcConfig {
+ let config = WireConfig::load_from_file(path);
+ assert_eq!(config.init.currency, "BTC");
+ return config;
+}
+
+pub type EthConfig = WireConfig<1000000, 24>;
+
+pub fn load_eth_config(path: impl AsRef<Path>) -> EthConfig {
+ let config = WireConfig::load_from_file(path);
+ assert_eq!(config.init.currency, "ETH");
+ return config;
+}
+
/* ----- Helper functions ----- */
fn section<'a>(ini: &'a Ini, name: &str) -> &'a Properties {
@@ -90,13 +136,6 @@ fn section<'a>(ini: &'a Ini, name: &str) -> &'a Properties {
.unwrap_or_else(|| panic!("missing config section {}", name))
}
-fn assert(properties: &Properties, name: &str, expected: &str) {
- let value = require(properties, name, string);
- if value != expected {
- panic!("config {} expected '{}' got '{}'", name, expected, value);
- }
-}
-
fn require<T>(
properties: &Properties,
name: &str,
diff --git a/test/btc/analysis.sh b/test/btc/analysis.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
@@ -28,7 +29,7 @@ echo "Loose second bitcoin node"
btc2_deco
echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $BTC_DIR transfer 0.042 > /dev/null
+btc-wire-utils -d $WIRE_DIR transfer 0.042 > /dev/null
next_btc # Trigger btc_wire
check_balance 9.95799209 0.04200000
echo " OK"
@@ -50,7 +51,7 @@ echo "Loose second bitcoin node"
btc2_deco
echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $BTC_DIR transfer 0.064 > /dev/null
+btc-wire-utils -d $WIRE_DIR transfer 0.064 > /dev/null
next_btc 5 # More block needed to confirm
check_balance 9.89398418 0.10600000
echo " OK"
diff --git a/test/btc/bumpfee.sh b/test/btc/bumpfee.sh
@@ -6,7 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
-CONFIG=taler_bump.conf
+CONFIG=taler_btc_bump.conf
echo "----- Setup -----"
echo "Load config file"
@@ -28,7 +28,7 @@ SEQ="seq 10 30"
echo -n "Making wire transfer to exchange:"
for n in `$SEQ`; do
- btc-wire-utils -d $BTC_DIR transfer 0.$n > /dev/null
+ btc-wire-utils -d $WIRE_DIR transfer 0.$n > /dev/null
mine_btc # Mine transactions
done
next_btc # Trigger btc_wire
diff --git a/test/btc/config.sh b/test/btc/config.sh
@@ -5,6 +5,7 @@
set -eu
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
function test() {
echo "----- Config $1 -----"
diff --git a/test/btc/conflict.sh b/test/btc/conflict.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
@@ -25,7 +26,7 @@ echo ""
echo "----- Conflict send -----"
echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $BTC_DIR transfer 0.042 > /dev/null
+btc-wire-utils -d $WIRE_DIR transfer 0.042 > /dev/null
next_btc
check_balance 9.95799209 0.04200000
echo " OK"
@@ -41,7 +42,7 @@ echo " OK"
echo -n "Abandon pending transaction:"
restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $BTC_DIR abandon
+btc-wire-utils -d $WIRE_DIR abandon
check_balance 9.95799209 0.04200000
echo " OK"
@@ -92,7 +93,7 @@ echo " OK"
echo -n "Abandon pending transaction:"
restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $BTC_DIR abandon
+btc-wire-utils -d $WIRE_DIR abandon
check_balance 9.95999859 0.04000000
echo " OK"
diff --git a/test/btc/hell.sh b/test/btc/hell.sh
@@ -5,6 +5,7 @@
set -eu
source "${BASH_SOURCE%/*}/../common.sh"
+CONFIG=taler_btc.conf
SCHEMA=btc.sql
echo "----- Setup -----"
@@ -28,7 +29,7 @@ echo "Loose second bitcoin node"
btc2_deco
echo -n "Gen incoming transactions:"
-btc-wire-utils -d $BTC_DIR transfer 0.0042 > /dev/null
+btc-wire-utils -d $WIRE_DIR transfer 0.0042 > /dev/null
next_btc # Trigger btc_wire
check_balance 9.99579209 0.00420000
echo " OK"
@@ -42,8 +43,8 @@ echo " OK"
echo -n "Generate conflict:"
restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $BTC_DIR abandon client
-btc-wire-utils -d $BTC_DIR transfer 0.0054 > /dev/null
+btc-wire-utils -d $WIRE_DIR abandon client
+btc-wire-utils -d $WIRE_DIR transfer 0.0054 > /dev/null
next_btc
check_balance 9.99457382 0.00540000
echo " OK"
@@ -95,8 +96,8 @@ echo " OK"
echo -n "Generate conflict:"
restart_btc -minrelaytxfee=0.0001
-btc-wire-utils -d $BTC_DIR abandon client
-btc-wire-utils -d $BTC_DIR transfer 0.054 > /dev/null
+btc-wire-utils -d $WIRE_DIR abandon client
+btc-wire-utils -d $WIRE_DIR transfer 0.054 > /dev/null
next_btc
check_balance 9.94597382 0.05400000
echo " OK"
diff --git a/test/btc/lifetime.sh b/test/btc/lifetime.sh
@@ -2,12 +2,12 @@
## Check btc-wire and wire-gateway correctly stop when a lifetime limit is configured
-CONFIG=taler_lifetime.conf
-
set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc_lifetime.conf
+
echo "----- Setup -----"
echo "Load config file"
@@ -33,7 +33,7 @@ echo " OK"
echo -n "Do some work:"
for n in `$SEQ`; do
- btc-wire-utils -d $BTC_DIR transfer 0.000$n > /dev/null
+ btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
mine_btc # Mine transactions
done
next_btc # Trigger btc_wire
diff --git a/test/btc/maxfee.sh b/test/btc/maxfee.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
@@ -26,7 +27,7 @@ SEQ="seq 10 30"
echo -n "Making wire transfer to exchange:"
for n in `$SEQ`; do
- btc-wire-utils -d $BTC_DIR transfer 0.$n > /dev/null
+ btc-wire-utils -d $WIRE_DIR transfer 0.$n > /dev/null
mine_btc # Mine transactions
done
next_btc # Trigger btc_wire
diff --git a/test/btc/reconnect.sh b/test/btc/reconnect.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
@@ -22,7 +23,7 @@ echo ""
echo "----- With DB -----"
echo "Making wire transfer to exchange:"
-btc-wire-utils -d $BTC_DIR transfer 0.000042 > /dev/null
+btc-wire-utils -d $WIRE_DIR transfer 0.000042 > /dev/null
next_btc
check_balance 9.99995009 0.00004200
echo -n "Requesting exchange incoming transaction list:"
@@ -35,7 +36,7 @@ pg_ctl stop -D $DB_DIR > /dev/null
echo "Making incomplete wire transfer to exchange"
$BTC_CLI -rpcwallet=client sendtoaddress $WIRE 0.00042 &> /dev/null
echo -n "Making wire transfer to exchange:"
-btc-wire-utils -d $BTC_DIR transfer 0.00004 > /dev/null
+btc-wire-utils -d $WIRE_DIR transfer 0.00004 > /dev/null
next_btc
check_balance 9.99948077 0.00050200
echo " OK"
diff --git a/test/btc/reorg.sh b/test/btc/reorg.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
@@ -31,7 +32,7 @@ btc2_deco
echo -n "Gen incoming transactions:"
for n in `$SEQ`; do
- btc-wire-utils -d $BTC_DIR transfer 0.000$n > /dev/null
+ btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
mine_btc # Mine transactions
done
next_btc # Trigger btc_wire
diff --git a/test/btc/stress.sh b/test/btc/stress.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup stressed -----"
echo "Load config file"
@@ -26,7 +27,7 @@ echo "----- Handle incoming -----"
echo -n "Making wire transfer to exchange:"
for n in `$SEQ`; do
- btc-wire-utils -d $BTC_DIR transfer 0.000$n > /dev/null
+ btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
mine_btc # Mine transactions
done
next_btc # Confirm all transactions
diff --git a/test/btc/wire.sh b/test/btc/wire.sh
@@ -6,6 +6,7 @@ set -eu
source "${BASH_SOURCE%/*}/../common.sh"
SCHEMA=btc.sql
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
@@ -26,7 +27,7 @@ echo "----- Receive -----"
echo -n "Making wire transfer to exchange:"
for n in `$SEQ`; do
- btc-wire-utils -d $BTC_DIR transfer 0.000$n > /dev/null
+ btc-wire-utils -d $WIRE_DIR transfer 0.000$n > /dev/null
mine_btc # Mine transactions
done
next_btc # Trigger btc_wire
diff --git a/test/common.sh b/test/common.sh
@@ -2,6 +2,8 @@
## Test utils
+set -eu
+
# Cleanup to run whenever we exit
function cleanup() {
pg_ctl stop -D $DB_DIR -w &> /dev/null
@@ -17,13 +19,11 @@ trap cleanup EXIT
# Init temporary dirs
DIR=$(mktemp -d)
-BTC_DIR=$DIR/bitcoin
-BTC_DIR2=$DIR/bitcoin2
-ETH_DIR=$DIR/eth
-ETH_DIR2=$DIR/eth2
+WIRE_DIR=$DIR/wire
+WIRE_DIR2=$DIR/wire2
DB_DIR=$DIR/db
CONF=$DIR/taler.conf
-for dir in $BTC_DIR $BTC_DIR2 $ETH_DIR $ETH_DIR2 $DB_DIR log; do
+for dir in $WIRE_DIR $WIRE_DIR2 $DB_DIR log; do
mkdir -p $dir
done
@@ -33,17 +33,24 @@ for log in log/*; do
done
# Setup command helpers
-BTC_CLI="bitcoin-cli -datadir=$BTC_DIR"
-BTC_CLI2="bitcoin-cli -datadir=$BTC_DIR2"
-ETH_CLI="geth -datadir=$ETH_DIR"
-ETH_CLI2="geth -datadir=$ETH_DIR2"
+BTC_CLI="bitcoin-cli -datadir=$WIRE_DIR"
+BTC_CLI2="bitcoin-cli -datadir=$WIRE_DIR2"
+ETH_CLI="geth -datadir=$WIRE_DIR"
+ETH_CLI2="geth -datadir=$WIRE_DIR2"
# Load test.conf as bash variables
function load_config() {
- cp ${BASH_SOURCE%/*}/conf/${CONFIG:-taler_test.conf} $CONF
- echo -e "\nBTC_DATA_DIR = ${BTC_DIR}" >> $CONF
+ cp ${BASH_SOURCE%/*}/conf/$CONFIG $CONF
+ echo -e "\nDATA_DIR = ${WIRE_DIR}" >> $CONF
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
+ else
+ WIRE_CLI=eth-wire-cli
+ WIRE_UTILS=eth-wire-utils
+ fi
}
# Check process is running
@@ -72,27 +79,27 @@ 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
- btc-wire-cli initdb $CONF > /dev/null
+ $WIRE_CLI initdb $CONF > /dev/null
}
# Erase database
function reset_db() {
- btc-wire-utils cleardb $CONF
- btc-wire-cli initdb $CONF
+ $WIRE_UTILS cleardb $CONF
+ $WIRE_CLI initdb $CONF
}
# ----- Bitcoin node ----- #
# Start a bitcoind regtest node, generate money, wallet and addresses
function init_btc() {
- cp ${BASH_SOURCE%/*}/conf/${BTC_CONFIG:-bitcoin.conf} $BTC_DIR/bitcoin.conf
- bitcoind -datadir=$BTC_DIR $* &>> log/btc.log &
+ cp ${BASH_SOURCE%/*}/conf/${BTC_CONFIG:-bitcoin.conf} $WIRE_DIR/bitcoin.conf
+ bitcoind -datadir=$WIRE_DIR $* &>> log/btc.log &
BTC_PID="$!"
# Wait for RPC server to be online
$BTC_CLI -rpcwait getnetworkinfo > /dev/null
# Create wire wallet
btc-wire-cli initwallet $CONF > /dev/null
- # Load wallets
+ # Create other wallets
for wallet in client reserve; do
$BTC_CLI createwallet $wallet > /dev/null
done
@@ -108,8 +115,8 @@ function init_btc() {
# Start a second bitcoind regtest node connected to the first one
function init_btc2() {
- cp ${BASH_SOURCE%/*}/conf/bitcoin2.conf $BTC_DIR2/bitcoin.conf
- bitcoind -datadir=$BTC_DIR2 $* &>> log/btc2.log &
+ cp ${BASH_SOURCE%/*}/conf/bitcoin2.conf $WIRE_DIR2/bitcoin.conf
+ bitcoind -datadir=$WIRE_DIR2 $* &>> log/btc2.log &
$BTC_CLI2 -rpcwait getnetworkinfo > /dev/null
$BTC_CLI addnode 127.0.0.1:8346 onetry
}
@@ -129,7 +136,7 @@ function btc2_fork() {
# Restart a bitcoind regest server in a previously created temporary directory and load wallets
function resume_btc() {
# Restart node
- bitcoind -datadir=$BTC_DIR $* &>> log/btc.log &
+ bitcoind -datadir=$WIRE_DIR $* &>> log/btc.log &
BTC_PID="$!"
# Load wallets
for wallet in wire client reserve; do
@@ -204,13 +211,17 @@ function stress_btc_wire() {
# Start a geth dev node, generate money, wallet and addresses
function init_eth() {
+ # TODO find a way to init with eth-wire-cli
+ # Create wallets
for pswd in "wire" "client" "reserve"; do
$ETH_CLI account new --password <(echo "password") &> /dev/null
done
+ # Retrieve addresses
local ADDR=`$ETH_CLI account list 2> /dev/null | grep -oP '(?<={).*?(?=})'`
WIRE=`sed -n '1p' <(echo "$ADDR")`
CLIENT=`sed -n '2p' <(echo "$ADDR")`
RESERVE=`sed -n '3p' <(echo "$ADDR")`
+ # Generate genesis
echo "{
\"config\": {
\"chainId\": 42,
@@ -232,8 +243,10 @@ function init_eth() {
\"$WIRE\": { \"balance\": \"500000000000000000\" }
}
}" > $DIR/genesis.json
+ # Initialize blockchain
$ETH_CLI init $DIR/genesis.json
- $ETH_CLI
+ # Start node
+ $ETH_CLI $* &>> log/eth.log &
}
# ----- Gateway ------ #
diff --git a/test/conf/taler_btc.conf b/test/conf/taler_btc.conf
@@ -0,0 +1,11 @@
+[taler]
+CURRENCY = BTC
+
+[exchange]
+BASE_URL = http://test.com
+
+[depolymerizer-bitcoin]
+DB_URL = postgres://localhost:5454/postgres?user=postgres&password=password
+PORT = 8060
+PAYTO = payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj
+CONFIRMATION = 3
+\ No newline at end of file
diff --git a/test/conf/taler_bump.conf b/test/conf/taler_btc_bump.conf
diff --git a/test/conf/taler_lifetime.conf b/test/conf/taler_btc_lifetime.conf
diff --git a/test/conf/taler_eth.conf b/test/conf/taler_eth.conf
@@ -0,0 +1,11 @@
+[taler]
+CURRENCY = ETH
+
+[exchange]
+BASE_URL = http://test.com
+
+[depolymerizer-ethereum]
+DB_URL = postgres://localhost:5454/postgres?user=postgres&password=password
+PORT = 8060
+PAYTO = payto://euthereum/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj
+CONFIRMATION = 3
+\ No newline at end of file
diff --git a/test/conf/taler_test.conf b/test/conf/taler_test.conf
@@ -1,12 +0,0 @@
-[taler]
-CURRENCY = BTC
-
-[exchange]
-BASE_URL = http://test.com
-
-[depolymerizer-bitcoin]
-DB_URL = postgres://localhost:5454/postgres?user=postgres&password=password
-PORT = 8060
-PAYTO = payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj
-CONFIRMATION = 3
-BOUNCE_FEE = 1000
-\ No newline at end of file
diff --git a/test/eth/test.sh b/test/eth/test.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -eu
+
+source "${BASH_SOURCE%/*}/../common.sh"
+SCHEMA=eth.sql
+CONFIG=taler_eth.conf
+
+echo "----- Setup -----"
+echo "Load config file"
+load_config
+echo "Start database"
+setup_db
+echo "Start ethereum node"
+init_eth
+echo ""
+
+echo "----- Test -----"
+cargo run --bin eth-test --release -- $CONF
+\ No newline at end of file
diff --git a/test/gateway/api.sh b/test/gateway/api.sh
@@ -19,6 +19,7 @@ trap cleanup EXIT
source "${BASH_SOURCE%/*}/../common.sh"
ADDRESS=mpTJZxWPerz1Gife6mQSdHT8mMuJK6FP85
SCHEMA=btc.sq
+CONFIG=taler_btc.conf
echo "----- Setup -----"
echo "Load config file"
diff --git a/uri-pack/Cargo.toml b/uri-pack/Cargo.toml
@@ -19,7 +19,7 @@ url = "2.2.2"
# statistics-driven micro-benchmarks
criterion = "0.3.5"
# Fast insecure random
-fastrand = "1.6.0"
+fastrand = "1.7.0"
# Fuzzing test
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
diff --git a/wire-gateway/Cargo.toml b/wire-gateway/Cargo.toml
@@ -14,13 +14,13 @@ hyper = { version = "0.14.16", features = ["http1", "server", "runtime"] }
# Hyper compat lib for unix domain socket
hyperlocal = "0.8.0"
# Async runtime
-tokio = { version = "1.15.0", features = ["net", "macros", "rt-multi-thread"] }
+tokio = { version = "1.16.1", features = ["net", "macros", "rt-multi-thread"] }
# Serialization framework
-serde = { version = "1.0.133", features = ["derive"] }
+serde = { version = "1.0.136", features = ["derive"] }
# Serialization helper
serde_with = "1.11.0"
# JSON serialization
-serde_json = "1.0.75"
+serde_json = "1.0.78"
# Url query serialization
serde_urlencoded = "0.7.1"
# Error macros
diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs
@@ -34,10 +34,12 @@ use taler_common::{
HistoryParams, IncomingBankTransaction, IncomingHistory, OutgoingBankTransaction,
OutgoingHistory, TransferRequest, TransferResponse,
},
+ config::{Config, GatewayConfig},
error_codes::ErrorCode,
log::log::{error, info, log, Level},
+ postgres::{fallible_iterator::FallibleIterator, Client},
sql::{sql_amount, sql_array, sql_safe_u64, sql_url},
- url::Url, postgres::{Client, fallible_iterator::FallibleIterator},
+ url::Url,
};
use tokio::sync::Notify;
use tokio_postgres::{config::Host, NoTls};
@@ -47,7 +49,7 @@ mod json;
struct ServerState {
pool: Pool,
- config: taler_common::config::Config,
+ config: GatewayConfig,
notify: Notify,
lifetime: Option<AtomicU32>,
status: AtomicBool,
@@ -85,15 +87,14 @@ impl ServerState {
async fn main() {
taler_common::log::init();
- let conf = taler_common::config::Config::load_from_file(
- std::env::args_os().nth(1).expect("Missing conf path arg"),
- );
+ let conf =
+ GatewayConfig::load_from_file(std::env::args_os().nth(1).expect("Missing conf path arg"));
#[cfg(feature = "test")]
taler_common::log::log::warn!("Running with test admin endpoint unsuitable for production");
// Parse postgres url
- let config = tokio_postgres::Config::from_str(&conf.db_url).unwrap();
+ let config = tokio_postgres::Config::from_str(&conf.init.db_url).unwrap();
// TODO find a way to clean this ugly mess
let mut cfg = deadpool_postgres::Config::new();
cfg.user = config.get_user().map(|it| it.to_string());
@@ -276,7 +277,7 @@ async fn router(
ErrorCode::GENERIC_PAYTO_URI_MALFORMED,
));
}
- if request.amount.currency != "BTC" {
+ if request.amount.currency != state.config.init.currency {
return Err(ServerError::code(
StatusCode::BAD_REQUEST,
ErrorCode::GENERIC_PARAMETER_MALFORMED,
@@ -449,7 +450,7 @@ async fn router(
/// Listen to backend status change
fn status_watcher(state: &'static ServerState) {
fn inner(state: &'static ServerState) -> Result<(), Box<dyn std::error::Error>> {
- let mut db = Client::connect(&state.config.db_url, NoTls)?;
+ let mut db = Client::connect(&state.config.init.db_url, NoTls)?;
// Register as listener
db.batch_execute("LISTEN status")?;
loop {