commit 5f43dd660186d631fcd05e505ee8fd2a5393a113
parent ca6277fe082e3e025cc83d6e2366238c639874c8
Author: Antoine A <>
Date: Mon, 27 Dec 2021 18:43:50 +0100
Lower level rpc and fix failing test
Diffstat:
7 files changed, 87 insertions(+), 204 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -166,7 +166,6 @@ dependencies = [
"taler-config",
"taler-log",
"thiserror",
- "ureq",
"uri-pack",
"url",
]
@@ -211,12 +210,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
-name = "chunked_transfer"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
-
-[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -237,15 +230,6 @@ dependencies = [
]
[[package]]
-name = "crc32fast"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
name = "criterion"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -477,18 +461,6 @@ dependencies = [
]
[[package]]
-name = "flate2"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
-dependencies = [
- "cfg-if",
- "crc32fast",
- "libc",
- "miniz_oxide 0.4.4",
-]
-
-[[package]]
name = "flexi_logger"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -862,16 +834,6 @@ dependencies = [
[[package]]
name = "miniz_oxide"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
-dependencies = [
- "adler",
- "autocfg",
-]
-
-[[package]]
-name = "miniz_oxide"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
@@ -930,12 +892,6 @@ dependencies = [
]
[[package]]
-name = "once_cell"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
-
-[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1233,21 +1189,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
-name = "ring"
-version = "0.16.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
-dependencies = [
- "cc",
- "libc",
- "once_cell",
- "spin",
- "untrusted",
- "web-sys",
- "winapi",
-]
-
-[[package]]
name = "rust-ini"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1267,18 +1208,6 @@ dependencies = [
]
[[package]]
-name = "rustls"
-version = "0.20.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84"
-dependencies = [
- "log",
- "ring",
- "sct",
- "webpki",
-]
-
-[[package]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1306,16 +1235,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
-name = "sct"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
name = "secp256k1"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1467,12 +1386,6 @@ dependencies = [
]
[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
-[[package]]
name = "stringprep"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1741,31 +1654,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
-[[package]]
-name = "ureq"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9399fa2f927a3d327187cbd201480cee55bee6ac5d3c77dd27f0c6814cff16d5"
-dependencies = [
- "base64",
- "chunked_transfer",
- "flate2",
- "log",
- "once_cell",
- "rustls",
- "serde",
- "serde_json",
- "url",
- "webpki",
- "webpki-roots",
-]
-
-[[package]]
name = "uri-pack"
version = "0.1.0"
dependencies = [
@@ -1896,25 +1784,6 @@ dependencies = [
]
[[package]]
-name = "webpki"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c475786c6f47219345717a043a37ec04cb4bc185e28853adcc4fa0a947eba630"
-dependencies = [
- "webpki",
-]
-
-[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1953,7 +1822,7 @@ dependencies = [
"deadpool-postgres",
"hyper",
"listenfd",
- "miniz_oxide 0.5.1",
+ "miniz_oxide",
"rand",
"serde",
"serde_json",
diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml
@@ -35,7 +35,6 @@ uri-pack = { path = "../uri-pack" }
url = { version = "2.2.2", features = ["serde"] }
# Ansi color
owo-colors = "3.1.1"
-ureq = { version = "2.3.1", features = ["json"] }
base64 = "0.13.0"
# Taler libs
taler-api = { path = "../taler-api" }
diff --git a/btc-wire/src/rpc.rs b/btc-wire/src/rpc.rs
@@ -3,14 +3,15 @@ use bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, Network, SignedAmo
use serde_json::{json, Value};
use std::{
fmt::Debug,
- io,
+ io::{self, BufRead, BufReader, ErrorKind, Write},
+ net::{SocketAddr, TcpStream},
path::Path,
sync::atomic::{AtomicU64, Ordering},
- time::Duration,
+ time::{Duration, Instant},
};
// This is a very simple RPC client designed only for a specific bitcoincore version
-// and to un on a secure localhost
+// and to use on an secure localhost connection to a trusted node
#[derive(Debug, serde::Serialize)]
struct BtcRequest<'a, T: serde::Serialize> {
@@ -34,8 +35,8 @@ struct BtcErr {
#[derive(Debug, thiserror::Error)]
pub enum Error {
- #[error(transparent)]
- Transport(#[from] ureq::Transport),
+ #[error("{0:?}")]
+ Transport(#[from] std::io::Error),
#[error("{code:?} - {msg}")]
RPC { code: ErrorCode, msg: String },
#[error("JSON: {0}")]
@@ -44,67 +45,93 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>;
+const EMPTY: [(); 0] = [];
+
pub struct BtcRpc {
+ addr: SocketAddr,
path: String,
- agent: ureq::Agent,
id: AtomicU64,
cookie: String,
}
impl BtcRpc {
pub fn common(data_dir: &Path, network: Network) -> io::Result<Self> {
- Self::new(data_dir, rpc_url(network), network)
+ Self::new(data_dir, rpc_url(network), "/".into(), network)
}
pub fn wallet(data_dir: &Path, network: Network, wallet: &str) -> io::Result<Self> {
Self::new(
data_dir,
- format!("{}/wallet/{}", rpc_url(network), wallet),
+ rpc_url(network),
+ format!("/wallet/{}", wallet),
network,
)
}
- fn new(data_dir: &Path, path: String, network: Network) -> io::Result<Self> {
+ fn new(data_dir: &Path, addr: SocketAddr, path: String, network: Network) -> io::Result<Self> {
let cookie_path = data_dir.join(rpc_dir(network)).join(".cookie");
let cookie = std::fs::read(cookie_path)?;
- let agent = ureq::builder().redirects(0).build();
Ok(Self {
+ addr,
path,
- agent,
id: AtomicU64::new(0),
cookie: format!("Basic {}", base64::encode(&cookie)),
})
}
- fn call<T>(
- &self,
- method: &str,
- params: &impl serde::Serialize,
- timeout: Option<Duration>,
- ) -> Result<T>
+ fn call<T>(&self, method: &str, params: &impl serde::Serialize) -> Result<T>
where
T: serde::de::DeserializeOwned + Debug,
{
let id = self.id.fetch_add(1, Ordering::SeqCst);
let request = BtcRequest { method, id, params };
- let body =
- serde_json::to_vec(&request).expect("Serialization into a vec should never fail");
- let result = self
- .agent
- .post(&self.path)
- .set("Authorization", &self.cookie)
- .set("Content-Type", "application/json-rpc")
- .set("Accept", "application/json-rpc")
- .timeout(timeout.unwrap_or(Duration::from_secs(10)))
- .send_bytes(&body);
- let response = match result {
- Ok(it) => it,
- Err(it) => match it {
- ureq::Error::Status(_, it) => it,
- ureq::Error::Transport(it) => return Err(it.into()),
- },
- };
- let response: BtcResponse<T> = serde_json::from_reader(response.into_reader())?;
+
+ // Some call might hang waiting for a new block to be mined
+ let timeout = Duration::from_secs(666);
+ let request_deadline = Instant::now() + timeout;
+
+ // Open connection
+ let mut sock = TcpStream::connect_timeout(&self.addr, timeout)?;
+ sock.set_read_timeout(Some(timeout))?;
+ sock.set_write_timeout(Some(timeout))?;
+
+ // Serialize the body first so we can set the Content-Length header.
+ let body = serde_json::to_vec(&request)?;
+ let mut buf = Vec::new();
+ // Write HTTP request
+ {
+ // Send HTTP request
+ writeln!(buf, "POST {} HTTP/1.1\r", self.path)?;
+ // Write headers
+ writeln!(buf, "Accept: application/json-rpc\r")?;
+ writeln!(buf, "Connection: close\r")?;
+ writeln!(buf, "Authorization: {}\r", self.cookie)?;
+ writeln!(buf, "Content-Type: application/json-rpc\r")?;
+ writeln!(buf, "Content-Length: {}\r", body.len())?;
+ // Write separator
+ writeln!(buf, "\r")?;
+ sock.write_all(&buf).unwrap();
+ buf.clear();
+ // Write body
+ sock.write_all(&body).unwrap();
+ sock.flush().unwrap();
+ }
+ // Receive response
+ let mut reader = BufReader::new(sock);
+ // Skip response
+ loop {
+ let amount = reader.read_until(b'\n', &mut buf).unwrap();
+ let sep = buf[..amount] == [b'\r', b'\n'];
+ buf.clear();
+ if sep {
+ break;
+ } else if Instant::now() > request_deadline {
+ Err(std::io::Error::new(ErrorKind::TimedOut, "Timeout"))?
+ }
+ }
+ // Read body
+ let amount = reader.read_until(b'\n', &mut buf).unwrap();
+ let response: BtcResponse<T> = serde_json::from_slice(&buf[..amount])?;
assert_eq!(id, response.id);
if let Some(ok) = response.result {
Ok(ok)
@@ -118,42 +145,37 @@ impl BtcRpc {
}
pub fn net_info(&self) -> Result<Empty> {
- let params: [(); 0] = [];
- self.call("getnetworkinfo", ¶ms, None)
+ self.call("getnetworkinfo", &EMPTY)
}
pub fn load_wallet(&self, name: &str) -> Result<Wallet> {
- self.call("loadwallet", &[name], None)
+ self.call("loadwallet", &[name])
}
pub fn create_wallet(&self, name: &str) -> Result<Wallet> {
- self.call("createwallet", &[name], None)
+ self.call("createwallet", &[name])
}
pub fn get_new_address(&self) -> Result<Address> {
- self.call("getnewaddress", &[()], None)
+ self.call("getnewaddress", &EMPTY)
}
pub fn generate(&self, nb: u16, address: &Address) -> Result<Vec<BlockHash>> {
- self.call(
- "generatetoaddress",
- &(nb, address),
- Some(Duration::from_secs(10 * 60 + 10)),
- )
+ self.call("generatetoaddress", &(nb, address))
}
pub fn wait_for_new_block(&self, timeout: u64) -> Result<Empty> {
- self.call("waitfornewblock", &[timeout], None)
+ self.call("waitfornewblock", &[timeout])
}
pub fn get_balance(&self) -> Result<Amount> {
- let btc: f64 = self.call("getbalance", &[()], None)?;
+ let btc: f64 = self.call("getbalance", &EMPTY)?;
Ok(Amount::from_btc(btc).unwrap())
}
pub fn send(&self, address: &Address, amount: &Amount, subtract_fee: bool) -> Result<Txid> {
let btc = amount.as_btc();
- self.call("sendtoaddress", &(address, btc, (), (), subtract_fee), None)
+ self.call("sendtoaddress", &(address, btc, (), (), subtract_fee))
}
/// Send transaction to multiple recipients
@@ -167,7 +189,7 @@ impl BtcRpc {
.map(|(addr, amount)| (addr.to_string(), amount.as_btc().into()))
.collect(),
);
- self.call("sendmany", &("", amounts), None)
+ self.call("sendmany", &("", amounts))
}
pub fn send_custom<'a, 'b, 'c>(
@@ -197,11 +219,10 @@ impl BtcRpc {
vec
}),
],
- None,
)?;
- let funded: HexWrapper = self.call("fundrawtransaction", &[hex], None)?;
- let signed: HexWrapper = self.call("signrawtransactionwithwallet", &[&funded.hex], None)?;
- self.call("sendrawtransaction", &[&signed.hex], None)
+ let funded: HexWrapper = self.call("fundrawtransaction", &[hex])?;
+ let signed: HexWrapper = self.call("signrawtransactionwithwallet", &[&funded.hex])?;
+ self.call("sendrawtransaction", &[&signed.hex])
}
pub fn list_since_block(
@@ -210,19 +231,15 @@ impl BtcRpc {
confirmation: u8,
include_remove: bool,
) -> Result<ListSinceBlock> {
- self.call(
- "listsinceblock",
- &(hash, confirmation, (), include_remove),
- None,
- )
+ self.call("listsinceblock", &(hash, confirmation, (), include_remove))
}
pub fn get_tx(&self, id: &Txid) -> Result<TransactionFull> {
- self.call("gettransaction", &(id, (), true), None)
+ self.call("gettransaction", &(id, (), true))
}
pub fn get_raw(&self, id: &Txid) -> Result<RawTransaction> {
- self.call("getrawtransaction", &(id, true), None)
+ self.call("getrawtransaction", &(id, true))
}
}
diff --git a/btc-wire/src/rpc_utils.rs b/btc-wire/src/rpc_utils.rs
@@ -1,4 +1,4 @@
-use std::{path::PathBuf, str::FromStr};
+use std::{net::SocketAddr, path::PathBuf, str::FromStr};
use bitcoin::{Address, Amount, Network};
@@ -16,14 +16,14 @@ pub fn rpc_dir(network: Network) -> &'static str {
}
}
-pub fn rpc_url(network: Network) -> String {
+pub fn rpc_url(network: Network) -> SocketAddr {
let port = match network {
Network::Bitcoin => 8332,
Network::Testnet => 18332,
Network::Regtest => 18443,
_ => unreachable!("Unsupported network"),
};
- format!("http://127.0.0.1:{}", port)
+ ([127, 0, 0, 1], port).into()
}
pub fn default_data_dir() -> PathBuf {
diff --git a/script/setup.sh b/script/setup.sh
@@ -19,14 +19,14 @@ function reset_db() {
function init_btc() {
BTC_DIR=$(mktemp -d)
BTC_CLI="bitcoin-cli -regtest -datadir=$BTC_DIR"
- bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 -rpcworkqueue=64 &> btc.log &
+ bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &> btc.log &
$BTC_CLI -rpcwait getnetworkinfo > /dev/null
}
# Start a bitcoind regest server in a previously created temporary directory and load wallets
function restart_btc() {
BTC_CLI="bitcoin-cli -regtest -datadir=$BTC_DIR"
- bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 -rpcworkqueue=64 &>> btc.log &
+ bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &>> btc.log &
$BTC_CLI -rpcwait getnetworkinfo > /dev/null
for wallet in wire client reserve; do
$BTC_CLI loadwallet $wallet > /dev/null
diff --git a/script/test_btc_fail.sh b/script/test_btc_fail.sh
@@ -65,7 +65,6 @@ for n in `$SEQ`; do
-b $BANK_ENDPOINT \
-C payto://bitcoin/$CLIENT \
-a BTC:0.0000$n > /dev/null
- mine_btc
done
sleep 15
mine_btc # Mine transactions
diff --git a/script/test_btc_stress.sh b/script/test_btc_stress.sh
@@ -33,7 +33,7 @@ echo "Start gateway"
gateway
echo ""
-SEQ="seq 10 50"
+SEQ="seq 10 99"
function check() {
check_delta "$1?delta=-100" "$SEQ"
@@ -56,7 +56,7 @@ check incoming
echo " OK"
echo -n "Check balance:"
-check_balance 9.99844569 1.00123000
+check_balance 9.99438310 1.00490500
echo " OK"
echo "----- Handle outgoing -----"
@@ -67,7 +67,6 @@ for n in `$SEQ`; do
-b $BANK_ENDPOINT \
-C payto://bitcoin/$CLIENT \
-a BTC:0.0000$n > /dev/null
- mine_btc
done
sleep 10 # Give time for btc_wire sender to process
next_btc # Mine transactions
@@ -80,7 +79,7 @@ check outgoing
echo " OK"
echo -n "Check balance:"
-check_balance 9.99967569
+check_balance 9.99928810
echo " OK"
next_btc # Mine transactions
@@ -101,7 +100,7 @@ echo " OK"
echo -n "Check balance:"
# Balance should not have changed
-check_balance 9.99967569
+check_balance 9.99928810
echo " OK"
echo "All tests passed"
\ No newline at end of file