All checks were successful
ci / test (push) Successful in 2m50s
Signed inv/rcp codecs keyed by identity, payments table with unverified incoming receipts, optional JSON-RPC sidecar (loopback/.onion HTTP only), and TUI /pay /tip. OnionWire still holds no spend keys.
98 lines
3.8 KiB
Rust
98 lines
3.8 KiB
Rust
//! M8: optional monero-wallet-rpc JSON client. Mock TCP only — no live monerod.
|
|
|
|
use onionwire::wallet::{self, Wallet};
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use tokio::net::TcpListener;
|
|
|
|
fn json_rpc_ok(result: &str) -> String {
|
|
let body = format!(r#"{{"jsonrpc":"2.0","id":"0","result":{result}}}"#);
|
|
format!(
|
|
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
|
|
body.len()
|
|
)
|
|
}
|
|
|
|
async fn serve_once(listener: TcpListener, response: String) {
|
|
let (mut sock, _) = listener.accept().await.expect("accept");
|
|
let mut buf = vec![0u8; 4096];
|
|
let _ = sock.read(&mut buf).await;
|
|
sock.write_all(response.as_bytes()).await.expect("write");
|
|
}
|
|
|
|
#[test]
|
|
fn disabled_create_address_is_not_configured() {
|
|
let w = Wallet::disabled();
|
|
let err = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap()
|
|
.block_on(w.create_address())
|
|
.unwrap_err();
|
|
assert!(err.to_string().contains("not configured"), "got {err}");
|
|
}
|
|
|
|
#[test]
|
|
fn refuse_non_loopback_non_onion_host() {
|
|
let err = Wallet::from_url("http://example.com:18083").unwrap_err();
|
|
assert!(
|
|
err.to_string().to_ascii_lowercase().contains("host")
|
|
|| err.to_string().contains("loopback")
|
|
|| err.to_string().contains("onion"),
|
|
"got {err}"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mock_get_address_parses_string() {
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let addr = listener.local_addr().unwrap();
|
|
let canned = json_rpc_ok(
|
|
r#"{"address":"4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}"#,
|
|
);
|
|
tokio::spawn(serve_once(listener, canned));
|
|
let w = Wallet::from_url(&format!("http://127.0.0.1:{}", addr.port())).unwrap();
|
|
let got = w.get_address().await.expect("get_address");
|
|
assert!(got.starts_with('4'), "got {got}");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mock_create_address_and_transfer() {
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let addr = listener.local_addr().unwrap();
|
|
let canned = json_rpc_ok(
|
|
r#"{"address":"8BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB","tx_hash":"abc123"}"#,
|
|
);
|
|
tokio::spawn(serve_once(listener, canned.clone()));
|
|
let w = Wallet::from_url(&format!("http://127.0.0.1:{}", addr.port())).unwrap();
|
|
let created = w.create_address().await.expect("create_address");
|
|
assert!(created.starts_with('8'));
|
|
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let addr = listener.local_addr().unwrap();
|
|
tokio::spawn(serve_once(listener, canned));
|
|
let w = Wallet::from_url(&format!("http://127.0.0.1:{}", addr.port())).unwrap();
|
|
let txid = w
|
|
.transfer("8BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", 1)
|
|
.await
|
|
.expect("transfer");
|
|
assert_eq!(txid, "abc123");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mock_get_transfers_matches_txid() {
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let addr = listener.local_addr().unwrap();
|
|
let canned = json_rpc_ok(
|
|
r#"{"in":[{"txid":"deadbeef","amount":5,"address":"8BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"}],"pending":[]}"#,
|
|
);
|
|
tokio::spawn(serve_once(listener, canned));
|
|
let w = Wallet::from_url(&format!("http://127.0.0.1:{}", addr.port())).unwrap();
|
|
let rows = w.get_transfers().await.expect("get_transfers");
|
|
assert!(wallet::transfers_match(
|
|
&rows,
|
|
"deadbeef",
|
|
"5",
|
|
"8BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
|
|
));
|
|
assert!(!wallet::transfers_match(&rows, "nope", "1", "nope"));
|
|
}
|