WIP - Refactor monerod to use @thomaseizinger rust-jsonrpc-client

This commit is contained in:
Daniel Karzel 2020-12-04 19:54:12 +11:00
parent 0ce3c54f13
commit ffb9d0e000
9 changed files with 76 additions and 101 deletions

24
Cargo.lock generated
View file

@ -1550,6 +1550,28 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonrpc_client"
version = "0.3.0"
source = "git+https://github.com/thomaseizinger/rust-jsonrpc-client?rev=c7010817e0f86ab24b3dc10d6bb0463faa0aace4#c7010817e0f86ab24b3dc10d6bb0463faa0aace4"
dependencies = [
"async-trait",
"jsonrpc_client_macro",
"reqwest",
"serde",
"serde_json",
"url",
]
[[package]]
name = "jsonrpc_client_macro"
version = "0.1.0"
source = "git+https://github.com/thomaseizinger/rust-jsonrpc-client?rev=c7010817e0f86ab24b3dc10d6bb0463faa0aace4#c7010817e0f86ab24b3dc10d6bb0463faa0aace4"
dependencies = [
"quote 1.0.7",
"syn 1.0.48",
]
[[package]]
name = "keccak"
version = "0.1.0"
@ -2000,8 +2022,10 @@ name = "monero-harness"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"digest_auth",
"futures",
"jsonrpc_client",
"port_check",
"rand 0.7.3",
"reqwest",

View file

@ -5,9 +5,11 @@ authors = ["CoBloX Team <team@coblox.tech>"]
edition = "2018"
[dependencies]
async-trait = "0.1"
anyhow = "1"
digest_auth = "0.2.3"
futures = "0.3"
jsonrpc_client = { git = "https://github.com/thomaseizinger/rust-jsonrpc-client", rev = "c7010817e0f86ab24b3dc10d6bb0463faa0aace4", features = ["reqwest"] }
port_check = "0.1"
rand = "0.7"
reqwest = { version = "0.10", default-features = false, features = ["json", "native-tls"] }

View file

@ -118,7 +118,10 @@ impl<'c> Monero {
// generate the first 70 as bulk
let monerod = &self.monerod;
let block = monerod.client().generate_blocks(70, &miner_address).await?;
let block = monerod
.client()?
.generate_blocks(70, &miner_address)
.await?;
tracing::info!("Generated {:?} blocks", block);
miner_wallet.refresh().await?;
@ -128,7 +131,10 @@ impl<'c> Monero {
let address = wallet.address().await?.address;
miner_wallet.transfer(&address, *amount).await?;
tracing::info!("Funded {} wallet with {}", wallet.name, amount);
monerod.client().generate_blocks(10, &miner_address).await?;
monerod
.client()?
.generate_blocks(10, &miner_address)
.await?;
wallet.refresh().await?;
}
}
@ -136,7 +142,7 @@ impl<'c> Monero {
monerod.start_miner(&miner_address).await?;
tracing::info!("Waiting for miner wallet to catch up...");
let block_height = monerod.client().get_block_count().await?;
let block_height = monerod.client()?.get_block_count_rpc().await?;
miner_wallet
.wait_for_wallet_height(block_height)
.await
@ -204,14 +210,14 @@ impl<'c> Monerod {
))
}
pub fn client(&self) -> monerod::Client {
monerod::Client::localhost(self.rpc_port)
pub fn client(&self) -> anyhow::Result<monerod::Client> {
Ok(monerod::Client::localhost(self.rpc_port)?)
}
/// Spawns a task to mine blocks in a regular interval to the provided
/// address
pub async fn start_miner(&self, miner_wallet_address: &str) -> Result<()> {
let monerod = self.client();
let monerod = self.client()?;
let _ = tokio::spawn(mine(monerod, miner_wallet_address.to_string()));
Ok(())
}

View file

@ -1,5 +1,6 @@
//! JSON RPC clients for `monerd` and `monero-wallet-rpc`.
pub mod monerod;
pub mod monerod_api;
pub mod wallet;
use serde::{Deserialize, Serialize};

View file

@ -1,106 +1,41 @@
use crate::rpc::{Request, Response};
use anyhow::Result;
use reqwest::Url;
use crate::rpc::monerod_api::MonerodRpcApi;
use serde::{Deserialize, Serialize};
use tracing::debug;
/// RPC client for monerod and monero-wallet-rpc.
#[jsonrpc_client::implement(MonerodRpcApi)]
#[derive(Debug, Clone)]
pub struct Client {
pub inner: reqwest::Client,
pub url: Url,
inner: reqwest::Client,
base_url: reqwest::Url,
}
impl Client {
/// New local host monerod RPC client.
pub fn localhost(port: u16) -> Self {
let url = format!("http://127.0.0.1:{}/json_rpc", port);
let url = Url::parse(&url).expect("url is well formed");
Self {
pub fn localhost(port: u16) -> anyhow::Result<Self> {
Ok(Client {
inner: reqwest::Client::new(),
url,
}
base_url: reqwest::Url::parse(format!("http://127.0.0.1:{}/json_rpc", port).as_str())?,
})
}
pub async fn generate_blocks(
&self,
amount_of_blocks: u32,
wallet_address: &str,
) -> Result<GenerateBlocks> {
let params = GenerateBlocksParams {
amount_of_blocks,
wallet_address: wallet_address.to_owned(),
};
let url = self.url.clone();
// // Step 1: Get the auth header
// let res = self.inner.get(url.clone()).send().await?;
// let headers = res.headers();
// let wwwauth = headers["www-authenticate"].to_str()?;
//
// // Step 2: Given the auth header, sign the digest for the real req.
// let tmp_url = url.clone();
// let context = AuthContext::new("username", "password", tmp_url.path());
// let mut prompt = digest_auth::parse(wwwauth)?;
// let answer = prompt.respond(&context)?.to_header_string();
let request = Request::new("generateblocks", params);
let response = self
.inner
.post(url)
.json(&request)
.send()
.await?
.text()
) -> anyhow::Result<GenerateBlocks> {
let res: GenerateBlocks = self
.generateblocks(amount_of_blocks, wallet_address)
.await?;
debug!("generate blocks response: {}", response);
let res: Response<GenerateBlocks> = serde_json::from_str(&response)?;
Ok(res.result)
Ok(res)
}
// $ curl http://127.0.0.1:18081/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_block_header_by_height","params":{"height":1}}' -H 'Content-Type: application/json'
pub async fn get_block_header_by_height(&self, height: u32) -> Result<BlockHeader> {
let params = GetBlockHeaderByHeightParams { height };
let request = Request::new("get_block_header_by_height", params);
let response = self
.inner
.post(self.url.clone())
.json(&request)
.send()
.await?
.text()
.await?;
debug!("get block header by height response: {}", response);
let res: Response<GetBlockHeaderByHeight> = serde_json::from_str(&response)?;
Ok(res.result.block_header)
// TODO: We should not need wrapper functions, why does it not compile without?
pub async fn get_block_header_by_height_rpc(&self, height: u32) -> anyhow::Result<BlockHeader> {
let res: BlockHeader = self.get_block_header_by_height(height).await?;
Ok(res)
}
pub async fn get_block_count(&self) -> Result<u32> {
let request = Request::new("get_block_count", "");
let response = self
.inner
.post(self.url.clone())
.json(&request)
.send()
.await?
.text()
.await?;
debug!("get block count response: {}", response);
let res: Response<BlockCount> = serde_json::from_str(&response)?;
Ok(res.result.count)
pub async fn get_block_count_rpc(&self) -> anyhow::Result<u32> {
let res: u32 = self.get_block_count().await?;
Ok(res)
}
}

View file

@ -0,0 +1,11 @@
use crate::rpc::monerod::{BlockHeader, GenerateBlocks};
#[jsonrpc_client::api(version = "1.0")]
#[async_trait::async_trait]
pub trait MonerodRpcApi {
async fn generateblocks(&self, amount_of_blocks: u32, wallet_address: &str) -> GenerateBlocks;
async fn get_block_header_by_height(&self, height: u32) -> BlockHeader;
async fn get_block_count(&self) -> u32;
}

View file

@ -20,7 +20,12 @@ async fn init_miner_and_mine_to_miner_address() {
time::delay_for(Duration::from_millis(1010)).await;
// after a bit more than 1 sec another block should have been mined
let block_height = monerod.client().get_block_count().await.unwrap();
let block_height = monerod
.client()
.unwrap()
.get_block_count_rpc()
.await
.unwrap();
assert_that(&block_height).is_greater_than(70);
}

View file

@ -42,6 +42,7 @@ async fn fund_transfer_and_check_tx_key() {
monero
.monerod()
.client()
.unwrap()
.generate_blocks(10, &miner_address)
.await
.unwrap();

View file

@ -38,16 +38,6 @@ impl Wallet {
pub async fn new_address(&self) -> Result<Address> {
self.0.new_address().await.map_err(Into::into)
}
pub async fn transaction_fee(&self, txid: Txid) -> Result<Amount> {
let fee = self
.0
.get_wallet_transaction(txid)
.await
.map(|res| bitcoin::Amount::from_btc(-res.fee))??;
Ok(fee)
}
}
#[async_trait]