mirror of
https://github.com/jwinterm/mwixnet.git
synced 2026-10-26 00:48:31 +01:00
converted to async/.await to support integration testing framework
(cherry picked from commit 26c129fa787ffec44f229ad8047d5c375fb4d257)
This commit is contained in:
+102
-26
@@ -1,26 +1,42 @@
|
||||
use crate::crypto::secp::Commitment;
|
||||
use grin_onion::crypto::secp::Commitment;
|
||||
|
||||
use grin_api::client;
|
||||
use grin_api::json_rpc::{build_request, Request, Response};
|
||||
use grin_api::{client, LocatedTxKernel};
|
||||
use grin_api::{OutputPrintable, OutputType, Tip};
|
||||
use grin_core::consensus::COINBASE_MATURITY;
|
||||
use grin_core::core::{Input, OutputFeatures, Transaction};
|
||||
use grin_util::ToHex;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde_json::json;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
#[async_trait]
|
||||
pub trait GrinNode: Send + Sync {
|
||||
/// Retrieves the unspent output with a matching commitment
|
||||
fn get_utxo(&self, output_commit: &Commitment) -> Result<Option<OutputPrintable>, NodeError>;
|
||||
async fn async_get_utxo(
|
||||
&self,
|
||||
output_commit: &Commitment,
|
||||
) -> Result<Option<OutputPrintable>, NodeError>;
|
||||
|
||||
/// Gets the height of the chain tip
|
||||
fn get_chain_height(&self) -> Result<u64, NodeError>;
|
||||
async fn async_get_chain_height(&self) -> Result<u64, NodeError>;
|
||||
|
||||
/// Posts a transaction to the grin node
|
||||
fn post_tx(&self, tx: &Transaction) -> Result<(), NodeError>;
|
||||
async fn async_post_tx(&self, tx: &Transaction) -> Result<(), NodeError>;
|
||||
|
||||
/// Returns a LocatedTxKernel based on the kernel excess.
|
||||
/// The min_height and max_height parameters are both optional.
|
||||
/// If not supplied, min_height will be set to 0 and max_height will be set to the head of the chain.
|
||||
/// The method will start at the block height max_height and traverse the kernel MMR backwards, until either the kernel is found or min_height is reached.
|
||||
async fn async_get_kernel(
|
||||
&self,
|
||||
excess: &Commitment,
|
||||
min_height: Option<u64>,
|
||||
max_height: Option<u64>,
|
||||
) -> Result<Option<LocatedTxKernel>, NodeError>;
|
||||
}
|
||||
|
||||
/// Error types for interacting with nodes
|
||||
@@ -35,18 +51,21 @@ pub enum NodeError {
|
||||
}
|
||||
|
||||
/// Checks if a commitment is in the UTXO set
|
||||
pub fn is_unspent(node: &Arc<dyn GrinNode>, commit: &Commitment) -> Result<bool, NodeError> {
|
||||
let utxo = node.get_utxo(&commit)?;
|
||||
pub async fn async_is_unspent(
|
||||
node: &Arc<dyn GrinNode>,
|
||||
commit: &Commitment,
|
||||
) -> Result<bool, NodeError> {
|
||||
let utxo = node.async_get_utxo(&commit).await?;
|
||||
Ok(utxo.is_some())
|
||||
}
|
||||
|
||||
/// Checks whether a commitment is spendable at the block height provided
|
||||
pub fn is_spendable(
|
||||
pub async fn async_is_spendable(
|
||||
node: &Arc<dyn GrinNode>,
|
||||
commit: &Commitment,
|
||||
next_block_height: u64,
|
||||
) -> Result<bool, NodeError> {
|
||||
let output = node.get_utxo(&commit)?;
|
||||
let output = node.async_get_utxo(&commit).await?;
|
||||
if let Some(out) = output {
|
||||
let is_coinbase = match out.output_type {
|
||||
OutputType::Coinbase => true,
|
||||
@@ -70,11 +89,11 @@ pub fn is_spendable(
|
||||
}
|
||||
|
||||
/// Builds an input for an unspent output commitment
|
||||
pub fn build_input(
|
||||
pub async fn async_build_input(
|
||||
node: &Arc<dyn GrinNode>,
|
||||
output_commit: &Commitment,
|
||||
) -> Result<Option<Input>, NodeError> {
|
||||
let output = node.get_utxo(&output_commit)?;
|
||||
let output = node.async_get_utxo(&output_commit).await?;
|
||||
|
||||
if let Some(out) = output {
|
||||
let features = match out.output_type {
|
||||
@@ -106,16 +125,20 @@ impl HttpGrinNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_json_request<D: serde::de::DeserializeOwned>(
|
||||
async fn async_send_request<D: serde::de::DeserializeOwned>(
|
||||
&self,
|
||||
method: &str,
|
||||
params: &serde_json::Value,
|
||||
) -> Result<D, NodeError> {
|
||||
let url = format!("http://{}{}", self.node_url, ENDPOINT);
|
||||
let req = build_request(method, params);
|
||||
let res =
|
||||
client::post::<Request, Response>(url.as_str(), self.node_api_secret.clone(), &req)
|
||||
.map_err(NodeError::ApiCommError)?;
|
||||
let res = client::post_async::<Request, Response>(
|
||||
url.as_str(),
|
||||
&req,
|
||||
self.node_api_secret.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(NodeError::ApiCommError)?;
|
||||
let parsed = res
|
||||
.clone()
|
||||
.into_result()
|
||||
@@ -124,8 +147,12 @@ impl HttpGrinNode {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GrinNode for HttpGrinNode {
|
||||
fn get_utxo(&self, output_commit: &Commitment) -> Result<Option<OutputPrintable>, NodeError> {
|
||||
async fn async_get_utxo(
|
||||
&self,
|
||||
output_commit: &Commitment,
|
||||
) -> Result<Option<OutputPrintable>, NodeError> {
|
||||
let commits: Vec<String> = vec![output_commit.to_hex()];
|
||||
let start_height: Option<u64> = None;
|
||||
let end_height: Option<u64> = None;
|
||||
@@ -139,7 +166,9 @@ impl GrinNode for HttpGrinNode {
|
||||
include_proof,
|
||||
include_merkle_proof
|
||||
]);
|
||||
let outputs = self.send_json_request::<Vec<OutputPrintable>>("get_outputs", ¶ms)?;
|
||||
let outputs = self
|
||||
.async_send_request::<Vec<OutputPrintable>>("get_outputs", ¶ms)
|
||||
.await?;
|
||||
if outputs.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -147,29 +176,54 @@ impl GrinNode for HttpGrinNode {
|
||||
Ok(Some(outputs[0].clone()))
|
||||
}
|
||||
|
||||
fn get_chain_height(&self) -> Result<u64, NodeError> {
|
||||
async fn async_get_chain_height(&self) -> Result<u64, NodeError> {
|
||||
let params = json!([]);
|
||||
let tip_json = self.send_json_request::<serde_json::Value>("get_tip", ¶ms)?;
|
||||
let tip_json = self
|
||||
.async_send_request::<serde_json::Value>("get_tip", ¶ms)
|
||||
.await?;
|
||||
let tip =
|
||||
serde_json::from_value::<Tip>(tip_json).map_err(NodeError::DecodeResponseError)?;
|
||||
|
||||
Ok(tip.height)
|
||||
}
|
||||
|
||||
fn post_tx(&self, tx: &Transaction) -> Result<(), NodeError> {
|
||||
async fn async_post_tx(&self, tx: &Transaction) -> Result<(), NodeError> {
|
||||
let params = json!([tx, true]);
|
||||
self.send_json_request::<serde_json::Value>("push_transaction", ¶ms)?;
|
||||
self.async_send_request::<serde_json::Value>("push_transaction", ¶ms)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn async_get_kernel(
|
||||
&self,
|
||||
excess: &Commitment,
|
||||
min_height: Option<u64>,
|
||||
max_height: Option<u64>,
|
||||
) -> Result<Option<LocatedTxKernel>, NodeError> {
|
||||
let params = json!([excess.0.as_ref().to_hex(), min_height, max_height]);
|
||||
let value = self
|
||||
.async_send_request::<serde_json::Value>("get_kernel", ¶ms)
|
||||
.await?;
|
||||
|
||||
let contents = format!("{:?}", value);
|
||||
if contents.contains("NotFound") {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let located_kernel = serde_json::from_value::<LocatedTxKernel>(value)
|
||||
.map_err(NodeError::DecodeResponseError)?;
|
||||
Ok(Some(located_kernel))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod mock {
|
||||
use super::{GrinNode, NodeError};
|
||||
use crate::crypto::secp::Commitment;
|
||||
|
||||
use grin_api::{OutputPrintable, OutputType};
|
||||
use async_trait::async_trait;
|
||||
use grin_api::{LocatedTxKernel, OutputPrintable, OutputType};
|
||||
use grin_core::core::Transaction;
|
||||
use grin_onion::crypto::secp::Commitment;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
|
||||
@@ -178,6 +232,7 @@ pub mod mock {
|
||||
pub struct MockGrinNode {
|
||||
utxos: HashMap<Commitment, OutputPrintable>,
|
||||
txns_posted: RwLock<Vec<Transaction>>,
|
||||
kernels: HashMap<Commitment, LocatedTxKernel>,
|
||||
}
|
||||
|
||||
impl MockGrinNode {
|
||||
@@ -185,6 +240,7 @@ pub mod mock {
|
||||
MockGrinNode {
|
||||
utxos: HashMap::new(),
|
||||
txns_posted: RwLock::new(Vec::new()),
|
||||
kernels: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +248,7 @@ pub mod mock {
|
||||
let mut node = MockGrinNode {
|
||||
utxos: HashMap::new(),
|
||||
txns_posted: RwLock::new(Vec::new()),
|
||||
kernels: HashMap::new(),
|
||||
};
|
||||
for utxo in utxos {
|
||||
node.add_default_utxo(utxo);
|
||||
@@ -222,10 +279,16 @@ pub mod mock {
|
||||
let read = self.txns_posted.read().unwrap();
|
||||
read.clone()
|
||||
}
|
||||
|
||||
pub fn add_kernel(&mut self, kernel: &LocatedTxKernel) {
|
||||
self.kernels
|
||||
.insert(kernel.tx_kernel.excess.clone(), kernel.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GrinNode for MockGrinNode {
|
||||
fn get_utxo(
|
||||
async fn async_get_utxo(
|
||||
&self,
|
||||
output_commit: &Commitment,
|
||||
) -> Result<Option<OutputPrintable>, NodeError> {
|
||||
@@ -236,14 +299,27 @@ pub mod mock {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_chain_height(&self) -> Result<u64, NodeError> {
|
||||
async fn async_get_chain_height(&self) -> Result<u64, NodeError> {
|
||||
Ok(100)
|
||||
}
|
||||
|
||||
fn post_tx(&self, tx: &Transaction) -> Result<(), NodeError> {
|
||||
async fn async_post_tx(&self, tx: &Transaction) -> Result<(), NodeError> {
|
||||
let mut write = self.txns_posted.write().unwrap();
|
||||
write.push(tx.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn async_get_kernel(
|
||||
&self,
|
||||
excess: &Commitment,
|
||||
_min_height: Option<u64>,
|
||||
_max_height: Option<u64>,
|
||||
) -> Result<Option<LocatedTxKernel>, NodeError> {
|
||||
if let Some(kernel) = self.kernels.get(&excess) {
|
||||
return Ok(Some(kernel.clone()));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user