depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit b19e53a7a82b9972b720f8f0424c17ae1afe2004
parent 99f474072cebe75081d835c02838553b734b87fc
Author: Antoine A <>
Date:   Mon, 29 Nov 2021 11:46:26 +0100

Add btc-wire-cli

Diffstat:
MCargo.lock | 6++++--
Abtc-wire/src/bin/btc-wire-cli.rs | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbtc-wire/src/main.rs | 6+++---
Mscript/test_bank.sh | 2+-
Muri-pack/Cargo.toml | 38+++++++++++++++++++-------------------
Muri-pack/src/main.rs | 560++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mwire-gateway/Cargo.toml | 54++++++++++++++++++++++++++++--------------------------
Mwire-gateway/src/api_wire.rs | 29+++++++++++++----------------
Mwire-gateway/src/main.rs | 17+++++++++++------
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 }