commit b19e53a7a82b9972b720f8f0424c17ae1afe2004
parent 99f474072cebe75081d835c02838553b734b87fc
Author: Antoine A <>
Date: Mon, 29 Nov 2021 11:46:26 +0100
Add btc-wire-cli
Diffstat:
9 files changed, 474 insertions(+), 353 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1053,9 +1053,9 @@ dependencies = [
[[package]]
name = "ryu"
-version = "1.0.5"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
[[package]]
name = "same-file"
@@ -1393,6 +1393,7 @@ dependencies = [
"idna",
"matches",
"percent-encoding",
+ "serde",
]
[[package]]
@@ -1580,4 +1581,5 @@ dependencies = [
"serde_with",
"thiserror",
"tokio",
+ "url",
]
diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs
@@ -0,0 +1,115 @@
+use std::collections::HashMap;
+
+use bitcoincore_rpc::{
+ bitcoin::{Address, Amount},
+ Client, RpcApi,
+};
+use btc_wire::{
+ rpc::{common_rpc, dirty_guess_network, wallet_rpc, Network},
+ utils::rand_key,
+ ClientExtended,
+};
+
+#[derive(argh::FromArgs)]
+/// Bitcoin wire test client
+struct Args {
+ #[argh(subcommand)]
+ cmd: Cmd,
+}
+
+#[derive(argh::FromArgs)]
+#[argh(subcommand)]
+enum Cmd {
+ Transfer(TransferCmd),
+ NextBlock(NextBlockCmd),
+}
+
+#[derive(argh::FromArgs)]
+#[argh(subcommand, name = "transfer")]
+/// Wait or mine the next block
+struct TransferCmd {
+ #[argh(option, short = 'k')]
+ /// reserve public key
+ key: Option<String>,
+
+ #[argh(option, short = 'f', default = "String::from(\"client\")")]
+ /// sender wallet
+ from: String,
+
+ #[argh(option, short = 't', default = "String::from(\"wire\")")]
+ /// receiver wallet
+ to: String,
+
+ #[argh(positional)]
+ /// amount to send in btc
+ amount: f64,
+}
+
+#[derive(argh::FromArgs)]
+#[argh(subcommand, name = "nblock")]
+/// Wait or mine the next block
+struct NextBlockCmd {
+ #[argh(option, short = 't', default = "String::from(\"reserve\")")]
+ /// receiver wallet
+ to: String,
+}
+
+struct App {
+ network: Network,
+ client: Client,
+}
+
+impl App {
+ pub fn start() -> Self {
+ let network = dirty_guess_network();
+ let client = common_rpc(network).expect("Failed to connect to bitcoin core server");
+
+ Self { network, client }
+ }
+
+ pub fn auto_wallet(&self, name: &str) -> (Client, Address) {
+ // Auto load
+ self.client.load_wallet(name).ok();
+ let wallet = wallet_rpc(self.network, name);
+ let addr = wallet
+ .get_new_address(None, None)
+ .expect(&format!("Failed to get wallet address {}", name));
+ (wallet, addr)
+ }
+
+ pub fn next_block(&self, wallet: &str) {
+ match self.network {
+ Network::RegTest => {
+ // Manually mine a block
+ let (_, addr) = self.auto_wallet(wallet);
+ self.client.generate_to_address(1, &addr).unwrap();
+ }
+ _ => {
+ // Wait for next network block
+ self.client.wait_for_new_block(0).ok();
+ }
+ }
+ }
+}
+
+fn main() {
+ let app = App::start();
+ let args: Args = argh::from_env();
+ match args.cmd {
+ Cmd::Transfer(TransferCmd {
+ key,
+ from,
+ to,
+ amount,
+ }) => {
+ let (client, _) = app.auto_wallet(&from);
+ let (_, to) = app.auto_wallet(&to);
+ client
+ .send_segwit_key(&to, Amount::from_btc(amount).unwrap(), &rand_key())
+ .unwrap();
+ }
+ Cmd::NextBlock(NextBlockCmd { to }) => {
+ app.next_block(&to);
+ }
+ }
+}
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
@@ -16,8 +16,8 @@ struct Args {
}
#[derive(argh::FromArgs)]
-/// Mine block for the given wallet
#[argh(subcommand, name = "mine")]
+/// Mine block for the given wallet
struct MineCmd {
#[argh(option, short = 'r')]
/// repeat every ? ms
@@ -34,8 +34,8 @@ struct MineCmd {
#[derive(argh::FromArgs)]
-/// Send message
#[argh(subcommand, name = "send")]
+/// Send message
struct SendRole {
#[argh(switch, short = 'm')]
/// mine on send
@@ -83,8 +83,8 @@ impl FromStr for Metadata {
}
#[derive(argh::FromArgs)]
-/// Msg exchange msg using metadata
#[argh(subcommand, name = "msg")]
+/// Msg exchange msg using metadata
struct MsgCmd {
#[argh(subcommand)]
role: Role,
diff --git a/script/test_bank.sh b/script/test_bank.sh
@@ -20,7 +20,7 @@ echo "OK"
BANK_ENDPOINT=http://localhost:8080/
echo -n "Making wire transfer to exchange ..."
-
+# btc-wire-cli.exe transfer 0.0004
taler-exchange-wire-gateway-client \
-b $BANK_ENDPOINT \
-S 0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00 \
diff --git a/uri-pack/Cargo.toml b/uri-pack/Cargo.toml
@@ -1,19 +1,19 @@
-[package]
-name = "uri-pack"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-# Error macros
-thiserror = "1.0.30"
-csv = "1.1.6"
-
-[dev-dependencies]
-# Json parser
-serde_json = "1.0.72"
-# Url parser
-url = "2.2.2"
-idna = "0.2.3"
-percent-encoding = "2.1.0"
+[package]
+name = "uri-pack"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+# Error macros
+thiserror = "1.0.30"
+csv = "1.1.6"
+
+[dev-dependencies]
+# Json parser
+serde_json = "1.0.72"
+# Url parser
+url = "2.2.2"
+idna = "0.2.3"
+percent-encoding = "2.1.0"
diff --git a/uri-pack/src/main.rs b/uri-pack/src/main.rs
@@ -1,280 +1,280 @@
-fn main() {
- let mut majestic = csv::Reader::from_reader(include_str!("majestic_million.csv").as_bytes());
- let mut ascii_counter = [0u64; 255];
- let mut count = 0;
- let mut before = 0;
- let mut after = 0;
- for record in majestic.records() {
- let domain = &record.unwrap()[2];
- for ascii in domain.as_bytes() {
- ascii_counter[*ascii as usize] += 1;
- }
- count += 1;
- before += domain.as_bytes().len();
- after += encode_str(domain).unwrap().len();
- }
- let sum: u64 = ascii_counter.iter().sum();
- for (ascii, count) in ascii_counter
- .into_iter()
- .enumerate()
- .filter(|(_, count)| *count > 0)
- {
- println!(
- "{} {:.2}% {:>4$} {:=<5$}",
- ascii as u8 as char,
- count as f32 / sum as f32 * 100.,
- count,
- "",
- sum.to_string().len(),
- (count * 100 / sum) as usize,
- )
- }
- println!("\nBefore: {} After: {}", before / count, after / count);
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum Encoded {
- Simple(u8), // u5: 0..32
- Extended(u8), // u6: 0..54
-}
-
-pub fn encode_ascii(c: u8) -> Encoded {
- let simple = match c {
- b'a'..=b'z' => c - b'a',
- b'.' => 26,
- b'/' => 27,
- b'-' => 28,
- b'_' => 29,
- b'%' => 30,
- c => {
- let extended = match c {
- b'A'..=b'Z' => c as u8 - 'A' as u8,
- b'0'..=b'9' => c as u8 - '0' as u8 + 26,
- b'!' => 36,
- b'"' => 37,
- b'#' => 38,
- b'$' => 39,
- b'&' => 40,
- b'\'' => 41,
- b'(' => 42,
- b')' => 43,
- b'*' => 44,
- b'+' => 45,
- b',' => 46,
- b':' => 47,
- b';' => 48,
- b'<' => 49,
- b'=' => 50,
- b'>' => 51,
- b'?' => 52,
- b'@' => 53,
- b'[' => 54,
- b'\\' => 55,
- b']' => 56,
- b'^' => 57,
- b'`' => 58,
- b'{' => 59,
- b'|' => 60,
- b'}' => 61,
- b'~' => 62,
- b' ' => 63,
- _ => unreachable!(),
- };
- return Encoded::Extended(extended);
- }
- };
- Encoded::Simple(simple)
-}
-
-pub fn decode_ascii(c: Encoded) -> u8 {
- match c {
- Encoded::Simple(c) => match c {
- 0..=25 => b'a' + c,
- 26 => b'.',
- 27 => b'/',
- 28 => b'-',
- 29 => b'_',
- 30 => b'%',
- _ => unreachable!(),
- },
- Encoded::Extended(c) => match c {
- 0..=25 => b'A' + c,
- 26..=35 => b'0' + c - 26,
- 36 => b'!',
- 37 => b'"',
- 38 => b'#',
- 39 => b'$',
- 40 => b'&',
- 41 => b'\'',
- 42 => b'(',
- 43 => b')',
- 44 => b'*',
- 45 => b'+',
- 46 => b',',
- 47 => b':',
- 48 => b';',
- 49 => b'<',
- 50 => b'=',
- 51 => b'>',
- 52 => b'?',
- 53 => b'@',
- 54 => b'[',
- 55 => b'\\',
- 56 => b']',
- 57 => b'^',
- 58 => b'`',
- 59 => b'{',
- 60 => b'|',
- 61 => b'}',
- 62 => b'~',
- 63 => b' ',
- _ => unreachable!(),
- },
- }
-}
-
-const EXTENDED: u8 = 31;
-
-#[derive(Debug, Clone, Copy, thiserror::Error)]
-#[error("{0} is not a valid uri char")]
-pub struct EncodeErr(char);
-
-#[derive(Debug, Clone, Copy, thiserror::Error)]
-pub enum DecodeErr {
- #[error("An extended encoded char have been passed as an simple one")]
- ExpectedExtended,
- #[error("{0} is not an simple encoded char")]
- UnexpectedSimpleChar(u8),
- #[error("{0} is not an extended encoded char")]
- UnexpectedExtendedChar(u8),
-}
-
-pub fn encode_str(str: &str) -> Result<Vec<u8>, EncodeErr> {
- let mut vec = Vec::new();
-
- assert!(str.as_bytes().iter().all(|c| supported_char(*c as char)));
-
- // Amount of pending bits stored in buffer.
- let mut buffer_bits = 0u8;
- // Holds pending bits beginning from the most significant bits
- let mut buffer: u8 = 0;
- let mut write_bits = |nb: u8, mut nb_bits: u8| {
- while nb_bits > 0 {
- let writable = (8 - buffer_bits).min(nb_bits);
- let remove_right = nb_bits - writable;
- let remove_left = 8 - writable;
- let mask = ((nb >> remove_right) << (remove_left)) >> buffer_bits;
- buffer = buffer | mask;
- buffer_bits += writable;
- nb_bits -= writable;
- // Write filled byte
- if buffer_bits == 8 {
- vec.push(buffer);
- buffer = 0;
- buffer_bits = 0;
- }
- }
- };
-
- for c in str.bytes() {
- match encode_ascii(c) {
- Encoded::Simple(nb) => write_bits(nb, 5),
- Encoded::Extended(nb) => {
- write_bits(EXTENDED, 5);
- write_bits(nb, 6);
- }
- }
- }
-
- if buffer_bits > 0 {
- vec.push(buffer);
- }
-
- return Ok(vec);
-}
-
-pub fn decode_str(bytes: &[u8], len: usize) -> Result<String, DecodeErr> {
- let mut buf = String::with_capacity(len);
- let mut iter = bytes.iter();
- // Amount of pending bits stored in buffer.
- let mut buffer_bits = 0u8;
- // Holds pending bits beginning from the most significant bits
- let mut buffer: u8 = 0;
- let mut read_nb = |mut nb_bits: u8| -> u8 {
- let mut nb = 8;
- while nb_bits > 0 {
- if buffer_bits == 0 {
- buffer = *iter.next().unwrap();
- buffer_bits = 8;
- }
- let readable = buffer_bits.min(nb_bits);
- let mask = (buffer << 8 - buffer_bits) >> (8 - readable);
- nb = (nb << readable) | mask;
- buffer_bits -= readable;
- nb_bits -= readable;
- }
- return nb;
- };
-
- for _ in 0..len {
- let encoded = match read_nb(5) {
- EXTENDED => Encoded::Extended(read_nb(6)),
- nb => Encoded::Simple(nb),
- };
- buf.push(decode_ascii(encoded) as char);
- }
-
- return Ok(buf);
-}
-
-fn supported_char(c: char) -> bool {
- c.is_ascii_graphic() || c == ' '
-}
-
-#[cfg(test)]
-mod test {
- use std::str::FromStr;
-
- use serde_json::Value;
-
- use crate::{decode_ascii, decode_str, encode_ascii, encode_str, supported_char};
-
- #[test]
- /// Check support every ascii graphic character and space
- fn supported() {
- for c in (0..=255u8).filter(|c| supported_char(*c as char)) {
- assert_eq!(decode_ascii(encode_ascii(c)), c);
- }
- }
-
- #[test]
- fn url_simple() {
- let mut majestic =
- csv::Reader::from_reader(include_str!("majestic_million.csv").as_bytes());
- for record in majestic.records() {
- let domain = &record.unwrap()[2];
- let decoded = decode_str(&encode_str(domain).unwrap(), domain.len()).unwrap();
- assert_eq!(domain, decoded);
- }
- }
-
- #[test]
- fn url_complex() {
- let mut json = Value::from_str(include_str!("urltestdata.json"))
- .expect("JSON parse error in urltestdata.json");
- for entry in json.as_array_mut().unwrap() {
- if entry.is_string() {
- continue; // ignore comments
- }
-
- let href = entry.get("href").and_then(|it| it.as_str()).unwrap_or("");
- if href.chars().any(|c| !c.is_ascii_graphic() || c != ' ') {
- continue; // extended ascii
- }
- let encoded = encode_str(&href).expect(&format!("Failed to encode {}", &href));
- let decoded = decode_str(&encoded, href.len())
- .expect(&format!("Failed to decode encoded {}", &href));
- assert_eq!(href, decoded);
- }
- }
-}
+fn main() {
+ let mut majestic = csv::Reader::from_reader(include_str!("majestic_million.csv").as_bytes());
+ let mut ascii_counter = [0u64; 255];
+ let mut count = 0;
+ let mut before = 0;
+ let mut after = 0;
+ for record in majestic.records() {
+ let domain = &record.unwrap()[2];
+ for ascii in domain.as_bytes() {
+ ascii_counter[*ascii as usize] += 1;
+ }
+ count += 1;
+ before += domain.as_bytes().len();
+ after += encode_str(domain).unwrap().len();
+ }
+ let sum: u64 = ascii_counter.iter().sum();
+ for (ascii, count) in ascii_counter
+ .into_iter()
+ .enumerate()
+ .filter(|(_, count)| *count > 0)
+ {
+ println!(
+ "{} {:.2}% {:>4$} {:=<5$}",
+ ascii as u8 as char,
+ count as f32 / sum as f32 * 100.,
+ count,
+ "",
+ sum.to_string().len(),
+ (count * 100 / sum) as usize,
+ )
+ }
+ println!("\nBefore: {} After: {}", before / count, after / count);
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Encoded {
+ Simple(u8), // u5: 0..32
+ Extended(u8), // u6: 0..54
+}
+
+pub fn encode_ascii(c: u8) -> Encoded {
+ let simple = match c {
+ b'a'..=b'z' => c - b'a',
+ b'.' => 26,
+ b'/' => 27,
+ b'-' => 28,
+ b'_' => 29,
+ b'%' => 30,
+ c => {
+ let extended = match c {
+ b'A'..=b'Z' => c as u8 - 'A' as u8,
+ b'0'..=b'9' => c as u8 - '0' as u8 + 26,
+ b'!' => 36,
+ b'"' => 37,
+ b'#' => 38,
+ b'$' => 39,
+ b'&' => 40,
+ b'\'' => 41,
+ b'(' => 42,
+ b')' => 43,
+ b'*' => 44,
+ b'+' => 45,
+ b',' => 46,
+ b':' => 47,
+ b';' => 48,
+ b'<' => 49,
+ b'=' => 50,
+ b'>' => 51,
+ b'?' => 52,
+ b'@' => 53,
+ b'[' => 54,
+ b'\\' => 55,
+ b']' => 56,
+ b'^' => 57,
+ b'`' => 58,
+ b'{' => 59,
+ b'|' => 60,
+ b'}' => 61,
+ b'~' => 62,
+ b' ' => 63,
+ _ => unreachable!(),
+ };
+ return Encoded::Extended(extended);
+ }
+ };
+ Encoded::Simple(simple)
+}
+
+pub fn decode_ascii(c: Encoded) -> u8 {
+ match c {
+ Encoded::Simple(c) => match c {
+ 0..=25 => b'a' + c,
+ 26 => b'.',
+ 27 => b'/',
+ 28 => b'-',
+ 29 => b'_',
+ 30 => b'%',
+ _ => unreachable!(),
+ },
+ Encoded::Extended(c) => match c {
+ 0..=25 => b'A' + c,
+ 26..=35 => b'0' + c - 26,
+ 36 => b'!',
+ 37 => b'"',
+ 38 => b'#',
+ 39 => b'$',
+ 40 => b'&',
+ 41 => b'\'',
+ 42 => b'(',
+ 43 => b')',
+ 44 => b'*',
+ 45 => b'+',
+ 46 => b',',
+ 47 => b':',
+ 48 => b';',
+ 49 => b'<',
+ 50 => b'=',
+ 51 => b'>',
+ 52 => b'?',
+ 53 => b'@',
+ 54 => b'[',
+ 55 => b'\\',
+ 56 => b']',
+ 57 => b'^',
+ 58 => b'`',
+ 59 => b'{',
+ 60 => b'|',
+ 61 => b'}',
+ 62 => b'~',
+ 63 => b' ',
+ _ => unreachable!(),
+ },
+ }
+}
+
+const EXTENDED: u8 = 31;
+
+#[derive(Debug, Clone, Copy, thiserror::Error)]
+#[error("{0} is not a valid uri char")]
+pub struct EncodeErr(char);
+
+#[derive(Debug, Clone, Copy, thiserror::Error)]
+pub enum DecodeErr {
+ #[error("An extended encoded char have been passed as an simple one")]
+ ExpectedExtended,
+ #[error("{0} is not an simple encoded char")]
+ UnexpectedSimpleChar(u8),
+ #[error("{0} is not an extended encoded char")]
+ UnexpectedExtendedChar(u8),
+}
+
+pub fn encode_str(str: &str) -> Result<Vec<u8>, EncodeErr> {
+ let mut vec = Vec::new();
+
+ assert!(str.as_bytes().iter().all(|c| supported_char(*c as char)));
+
+ // Amount of pending bits stored in buffer.
+ let mut buffer_bits = 0u8;
+ // Holds pending bits beginning from the most significant bits
+ let mut buffer: u8 = 0;
+ let mut write_bits = |nb: u8, mut nb_bits: u8| {
+ while nb_bits > 0 {
+ let writable = (8 - buffer_bits).min(nb_bits);
+ let remove_right = nb_bits - writable;
+ let remove_left = 8 - writable;
+ let mask = ((nb >> remove_right) << (remove_left)) >> buffer_bits;
+ buffer = buffer | mask;
+ buffer_bits += writable;
+ nb_bits -= writable;
+ // Write filled byte
+ if buffer_bits == 8 {
+ vec.push(buffer);
+ buffer = 0;
+ buffer_bits = 0;
+ }
+ }
+ };
+
+ for c in str.bytes() {
+ match encode_ascii(c) {
+ Encoded::Simple(nb) => write_bits(nb, 5),
+ Encoded::Extended(nb) => {
+ write_bits(EXTENDED, 5);
+ write_bits(nb, 6);
+ }
+ }
+ }
+
+ if buffer_bits > 0 {
+ vec.push(buffer);
+ }
+
+ return Ok(vec);
+}
+
+pub fn decode_str(bytes: &[u8], len: usize) -> Result<String, DecodeErr> {
+ let mut buf = String::with_capacity(len);
+ let mut iter = bytes.iter();
+ // Amount of pending bits stored in buffer.
+ let mut buffer_bits = 0u8;
+ // Holds pending bits beginning from the most significant bits
+ let mut buffer: u8 = 0;
+ let mut read_nb = |mut nb_bits: u8| -> u8 {
+ let mut nb = 8;
+ while nb_bits > 0 {
+ if buffer_bits == 0 {
+ buffer = *iter.next().unwrap();
+ buffer_bits = 8;
+ }
+ let readable = buffer_bits.min(nb_bits);
+ let mask = (buffer << 8 - buffer_bits) >> (8 - readable);
+ nb = (nb << readable) | mask;
+ buffer_bits -= readable;
+ nb_bits -= readable;
+ }
+ return nb;
+ };
+
+ for _ in 0..len {
+ let encoded = match read_nb(5) {
+ EXTENDED => Encoded::Extended(read_nb(6)),
+ nb => Encoded::Simple(nb),
+ };
+ buf.push(decode_ascii(encoded) as char);
+ }
+
+ return Ok(buf);
+}
+
+fn supported_char(c: char) -> bool {
+ c.is_ascii_graphic() || c == ' '
+}
+
+#[cfg(test)]
+mod test {
+ use std::str::FromStr;
+
+ use serde_json::Value;
+
+ use crate::{decode_ascii, decode_str, encode_ascii, encode_str, supported_char};
+
+ #[test]
+ /// Check support every ascii graphic character and space
+ fn supported() {
+ for c in (0..=255u8).filter(|c| supported_char(*c as char)) {
+ assert_eq!(decode_ascii(encode_ascii(c)), c);
+ }
+ }
+
+ #[test]
+ fn url_simple() {
+ let mut majestic =
+ csv::Reader::from_reader(include_str!("majestic_million.csv").as_bytes());
+ for record in majestic.records() {
+ let domain = &record.unwrap()[2];
+ let decoded = decode_str(&encode_str(domain).unwrap(), domain.len()).unwrap();
+ assert_eq!(domain, decoded);
+ }
+ }
+
+ #[test]
+ fn url_complex() {
+ let mut json = Value::from_str(include_str!("urltestdata.json"))
+ .expect("JSON parse error in urltestdata.json");
+ for entry in json.as_array_mut().unwrap() {
+ if entry.is_string() {
+ continue; // ignore comments
+ }
+
+ let href = entry.get("href").and_then(|it| it.as_str()).unwrap_or("");
+ if href.chars().any(|c| !c.is_ascii_graphic() || c != ' ') {
+ continue; // extended ascii
+ }
+ let encoded = encode_str(&href).expect(&format!("Failed to encode {}", &href));
+ let decoded = decode_str(&encoded, href.len())
+ .expect(&format!("Failed to decode encoded {}", &href));
+ assert_eq!(href, decoded);
+ }
+ }
+}
diff --git a/wire-gateway/Cargo.toml b/wire-gateway/Cargo.toml
@@ -1,26 +1,28 @@
-[package]
-name = "wire-gateway"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-# Http library
-hyper = { version = "0.14.15", features = ["http1", "server", "runtime"] }
-# Async runtime
-tokio = { version = "1.14.0", features = ["full"] }
-# Serialization framework
-serde = { version = "1.0.130", features = ["derive"] }
-# Serialization helper
-serde_with = "1.11.0"
-# JSON serialization
-serde_json = "1.0.71"
-# Url query serialization
-serde_urlencoded = "0.7.0"
-# Crockford’s base32
-base32 = "0.4.0"
-# Error macros
-thiserror = "1.0.30"
-# Async friendly compression
-async-compression = { version = "0.3.8", features = ["tokio", "zlib"] }
-# Rng
-rand = { version = "0.8.4", features = ["getrandom"] }
+[package]
+name = "wire-gateway"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# Http library
+hyper = { version = "0.14.15", features = ["http1", "server", "runtime"] }
+# Async runtime
+tokio = { version = "1.14.0", features = ["full"] }
+# Serialization framework
+serde = { version = "1.0.130", features = ["derive"] }
+# Serialization helper
+serde_with = "1.11.0"
+# JSON serialization
+serde_json = "1.0.72"
+# Url query serialization
+serde_urlencoded = "0.7.0"
+# Crockford’s base32
+base32 = "0.4.0"
+# Error macros
+thiserror = "1.0.30"
+# Async friendly compression
+async-compression = { version = "0.3.8", features = ["tokio", "zlib"] }
+# Rng
+rand = { version = "0.8.4", features = ["getrandom"] }
+# Url format
+url = { version = "2.2.2", features = ["serde"] }
diff --git a/wire-gateway/src/api_wire.rs b/wire-gateway/src/api_wire.rs
@@ -1,3 +1,5 @@
+use url::Url;
+
use crate::api_common::{Amount, EddsaPublicKey, HashCode, SafeUint64, ShortHashCode, Timestamp};
/// <https://docs.taler.net/core/api-wire.html#tsref-type-TransferResponse>
@@ -12,9 +14,9 @@ pub struct TransferResponse {
pub struct TransferRequest {
pub request_uid: HashCode,
pub amount: Amount,
- pub exchange_base_url: String,
+ pub exchange_base_url: Url,
pub wtid: ShortHashCode,
- pub credit_account: String,
+ pub credit_account: Url,
}
/// <https://docs.taler.net/core/api-wire.html#tsref-type-OutgoingHistory>
@@ -29,10 +31,10 @@ pub struct OutgoingBankTransaction {
pub row_id: SafeUint64,
pub date: Timestamp,
pub amount: Amount,
- pub credit_account: String,
- pub debit_account: String,
+ pub credit_account: Url,
+ pub debit_account: Url,
pub wtid: ShortHashCode,
- pub exchange_base_url: String,
+ pub exchange_base_url: Url,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -47,19 +49,13 @@ pub enum IncomingBankTransaction {
row_id: SafeUint64,
date: Timestamp,
amount: Amount,
- credit_account: String,
- debit_account: String,
+ credit_account: Url,
+ debit_account: Url,
reserve_pub: EddsaPublicKey,
},
#[serde(rename = "WAD")]
IncomingWadTransaction {
- row_id: SafeUint64,
- date: Timestamp,
- amount: Amount,
- credit_account: String,
- debit_account: String,
- origin_exchange_url: String,
- // TODO wad_id: WadId,
+ // TODO not yet supported
},
}
@@ -68,7 +64,7 @@ pub enum IncomingBankTransaction {
pub struct AddIncomingRequest {
pub amount: Amount,
pub reserve_pub: EddsaPublicKey,
- pub debit_account: String,
+ pub debit_account: Url,
}
/// <https://docs.taler.net/core/api-wire.html#tsref-type-AddIncomingResponse>
@@ -83,4 +79,4 @@ pub struct HistoryParams {
pub start: Option<u64>,
pub delta: i64,
pub long_pool_ms: Option<u64>,
-}
+}
+\ No newline at end of file
diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs
@@ -9,6 +9,7 @@ use hyper::{
Body, Error, Method, Request, Response, Server, StatusCode,
};
use tokio::{io::AsyncReadExt, sync::Mutex};
+use url::Url;
use crate::{
api_common::ErrorDetail,
@@ -71,7 +72,8 @@ struct IncomingTransaction {
date: Timestamp,
amount: Amount,
reserve_pub: ShortHashCode,
- debit_account: String,
+ debit_account: Url,
+ credit_account: Url,
}
struct OutgoingTransaction {
@@ -79,7 +81,8 @@ struct OutgoingTransaction {
date: Timestamp,
amount: Amount,
wtid: ShortHashCode,
- credit_account: String,
+ debit_account: Url,
+ credit_account: Url,
}
struct ServerState {
@@ -114,7 +117,7 @@ async fn json_response<J: serde::Serialize>(status: StatusCode, json: &J) -> Res
/* let mut encoder = ZlibEncoder::new(Vec::new());
encoder.write_all(&json).await.unwrap();
encoder.shutdown().await.unwrap();
-
+
let encoded = encoder.into_inner();*/
Response::builder()
.status(status)
@@ -155,6 +158,7 @@ async fn router(
amount: request.amount,
wtid: request.wtid,
credit_account: request.credit_account,
+ debit_account: Url::parse("payto://bitcoin").unwrap(),
});
json_response(StatusCode::OK, &TransferResponse { timestamp, row_id }).await
}
@@ -169,7 +173,7 @@ async fn router(
row_id: tx.row_id,
date: tx.date,
amount: tx.amount.clone(),
- credit_account: String::new(),
+ credit_account: tx.credit_account.clone(),
debit_account: tx.debit_account.clone(),
reserve_pub: tx.reserve_pub.clone(),
})
@@ -195,8 +199,8 @@ async fn router(
amount: tx.amount.clone(),
credit_account: tx.credit_account.clone(),
wtid: tx.wtid.clone(),
- debit_account: String::new(),
- exchange_base_url: String::new(),
+ debit_account: Url::parse("payto://bitcoin").unwrap(),
+ exchange_base_url: Url::parse("").unwrap(),
})
.collect();
json_response(
@@ -219,6 +223,7 @@ async fn router(
amount: request.amount,
reserve_pub: request.reserve_pub,
debit_account: request.debit_account,
+ credit_account: Url::parse("payto://bitcoin").unwrap()
});
json_response(StatusCode::OK, &AddIncomingResponse { timestamp, row_id }).await
}