2 Commits

Author SHA1 Message Date
hinto.janai
68060b4efc fix doc tests 2024-05-16 19:56:36 -04:00
hinto.janai
4af83bd0e5 doc rpc/mod.rs 2024-05-16 19:50:35 -04:00
5 changed files with 61 additions and 26 deletions

View File

@@ -47,7 +47,7 @@ impl BlockHeader {
/// ///
/// let mut writer = vec![]; /// let mut writer = vec![];
/// block_header.write(&mut writer)?; /// block_header.write(&mut writer)?;
/// # } /// # Ok(()) }
/// ``` /// ```
/// ///
/// # Errors /// # Errors
@@ -79,7 +79,7 @@ impl BlockHeader {
/// ///
/// let serialized = block_header.serialize(); /// let serialized = block_header.serialize();
/// assert_eq!(serialized, writer); /// assert_eq!(serialized, writer);
/// # } /// # Ok(()) }
/// ``` /// ```
pub fn serialize(&self) -> Vec<u8> { pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![]; let mut serialized = vec![];
@@ -104,9 +104,9 @@ impl BlockHeader {
/// let mut vec = vec![]; /// let mut vec = vec![];
/// block_header.write(&mut vec)?; /// block_header.write(&mut vec)?;
/// ///
/// let read = BlockHeader::read(& vec)?; /// let read = BlockHeader::read(&mut vec.as_slice())?;
/// assert_eq!(read, block_header); /// assert_eq!(read, block_header);
/// # } /// # Ok(()) }
/// ``` /// ```
/// ///
/// # Errors /// # Errors

View File

@@ -250,7 +250,7 @@ impl Protocol {
} }
} }
/// Transparent structure representing a [https://web.getmonero.org/resources/moneropedia/pedersen-commitment.html]()'s contents. /// Transparent structure representing a [Pedersen commitment](https://web.getmonero.org/resources/moneropedia/pedersen-commitment.html)'s contents.
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct Commitment { pub struct Commitment {

View File

@@ -249,7 +249,7 @@ impl Algorithm<Ed25519> for ClsagMultisig {
let interim = self.interim.as_ref().unwrap(); let interim = self.interim.as_ref().unwrap();
let mut clsag = interim.clsag.clone(); let mut clsag = interim.clsag.clone();
// We produced shares as `r - p x`, yet the signature is `r - p x - c x` // We produced shares as `r - p x`, yet the signature is `r - p x - c x`
// Substract `c x` (saved as `c`) now // Subtract `c x` (saved as `c`) now
clsag.s[usize::from(self.input().decoys.i)] = sum.0 - interim.c; clsag.s[usize::from(self.input().decoys.i)] = sum.0 - interim.c;
if clsag if clsag
.verify( .verify(

View File

@@ -85,13 +85,13 @@ impl RctType {
/// ///
/// ```rust /// ```rust
/// # use monero_serai::ringct::*; /// # use monero_serai::ringct::*;
/// assert_eq!(RctType::Null.to_bytes(), 0); /// assert_eq!(RctType::Null.to_byte(), 0);
/// assert_eq!(RctType::MlsagAggregate.to_bytes(), 1); /// assert_eq!(RctType::MlsagAggregate.to_byte(), 1);
/// assert_eq!(RctType::MlsagIndividual.to_bytes(), 2); /// assert_eq!(RctType::MlsagIndividual.to_byte(), 2);
/// assert_eq!(RctType::Bulletproofs.to_bytes(), 3); /// assert_eq!(RctType::Bulletproofs.to_byte(), 3);
/// assert_eq!(RctType::BulletproofsCompactAmount.to_bytes(), 4); /// assert_eq!(RctType::BulletproofsCompactAmount.to_byte(), 4);
/// assert_eq!(RctType::Clsag.to_bytes(), 5); /// assert_eq!(RctType::Clsag.to_byte(), 5);
/// assert_eq!(RctType::BulletproofsPlus.to_bytes(), 6); /// assert_eq!(RctType::BulletproofsPlus.to_byte(), 6);
/// ``` /// ```
pub fn to_byte(self) -> u8 { pub fn to_byte(self) -> u8 {
match self { match self {
@@ -109,20 +109,20 @@ impl RctType {
/// ///
/// ```rust /// ```rust
/// # use monero_serai::ringct::*; /// # use monero_serai::ringct::*;
/// assert_eq!(RctType::from_bytes(0).unwrap(), RctType::Null); /// assert_eq!(RctType::from_byte(0).unwrap(), RctType::Null);
/// assert_eq!(RctType::from_bytes(1).unwrap(), RctType::MlsagAggregate); /// assert_eq!(RctType::from_byte(1).unwrap(), RctType::MlsagAggregate);
/// assert_eq!(RctType::from_bytes(2).unwrap(), RctType::MlsagIndividual); /// assert_eq!(RctType::from_byte(2).unwrap(), RctType::MlsagIndividual);
/// assert_eq!(RctType::from_bytes(3).unwrap(), RctType::Bulletproofs); /// assert_eq!(RctType::from_byte(3).unwrap(), RctType::Bulletproofs);
/// assert_eq!(RctType::from_bytes(4).unwrap(), RctType::BulletproofsCompactAmount); /// assert_eq!(RctType::from_byte(4).unwrap(), RctType::BulletproofsCompactAmount);
/// assert_eq!(RctType::from_bytes(5).unwrap(), RctType::Clsag); /// assert_eq!(RctType::from_byte(5).unwrap(), RctType::Clsag);
/// assert_eq!(RctType::from_bytes(6).unwrap(), RctType::BulletproofsPlus); /// assert_eq!(RctType::from_byte(6).unwrap(), RctType::BulletproofsPlus);
/// ``` /// ```
/// ///
/// # Errors /// # Errors
/// This function returns [`None`] if the byte representation is invalid. /// This function returns [`None`] if the byte representation is invalid.
/// ```rust /// ```rust
/// # use monero_serai::ringct::*; /// # use monero_serai::ringct::*;
/// assert_eq!(RctType::from_bytes(7), None); /// assert_eq!(RctType::from_byte(7), None);
/// ``` /// ```
pub fn from_byte(byte: u8) -> Option<Self> { pub fn from_byte(byte: u8) -> Option<Self> {
Some(match byte { Some(match byte {

View File

@@ -34,8 +34,10 @@ pub use http::*;
// src/wallet/wallet2.cpp#L121 // src/wallet/wallet2.cpp#L121
const GRACE_BLOCKS_FOR_FEE_ESTIMATE: u64 = 10; const GRACE_BLOCKS_FOR_FEE_ESTIMATE: u64 = 10;
/// A empty marker struct representing an empty response.
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct EmptyResponse {} pub struct EmptyResponse {}
/// A generic JSON-RPC response.
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct JsonRpcResponse<T> { pub struct JsonRpcResponse<T> {
result: T, result: T,
@@ -54,6 +56,7 @@ struct TransactionsResponse {
txs: Vec<TransactionResponse>, txs: Vec<TransactionResponse>,
} }
/// The response data from an [`Rpc::get_outs`] call.
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct OutputResponse { pub struct OutputResponse {
pub height: usize, pub height: usize,
@@ -63,27 +66,41 @@ pub struct OutputResponse {
txid: String, txid: String,
} }
/// Possible errors that can occur from an RPC call.
///
/// This represents errors on the client side, as well
/// as valid error responses from the server.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))] #[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum RpcError { pub enum RpcError {
/// There was an internal error.
#[cfg_attr(feature = "std", error("internal error ({0})"))] #[cfg_attr(feature = "std", error("internal error ({0})"))]
InternalError(&'static str), InternalError(&'static str),
/// There was a connection error.
#[cfg_attr(feature = "std", error("connection error ({0})"))] #[cfg_attr(feature = "std", error("connection error ({0})"))]
ConnectionError(String), ConnectionError(String),
/// The data response received from the node was invalid.
#[cfg_attr(feature = "std", error("invalid node ({0})"))] #[cfg_attr(feature = "std", error("invalid node ({0})"))]
InvalidNode(String), InvalidNode(String),
/// The Monero [`Protocol`] version was invalid.
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))] #[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
UnsupportedProtocol(usize), UnsupportedProtocol(usize),
/// Requested transaction hashes were not found.
#[cfg_attr(feature = "std", error("transactions not found"))] #[cfg_attr(feature = "std", error("transactions not found"))]
TransactionsNotFound(Vec<[u8; 32]>), TransactionsNotFound(Vec<[u8; 32]>),
/// A curve point received from the node was invalid.
#[cfg_attr(feature = "std", error("invalid point ({0})"))] #[cfg_attr(feature = "std", error("invalid point ({0})"))]
InvalidPoint(String), InvalidPoint(String),
/// The transaction(s) requested were pruned from the node.
#[cfg_attr(feature = "std", error("pruned transaction"))] #[cfg_attr(feature = "std", error("pruned transaction"))]
PrunedTransaction, PrunedTransaction,
/// An invalid transaction was either sent/received to/from the node.
#[cfg_attr(feature = "std", error("invalid transaction ({0:?})"))] #[cfg_attr(feature = "std", error("invalid transaction ({0:?})"))]
InvalidTransaction([u8; 32]), InvalidTransaction([u8; 32]),
/// The node failed to return a fee.
#[cfg_attr(feature = "std", error("unexpected fee response"))] #[cfg_attr(feature = "std", error("unexpected fee response"))]
InvalidFee, InvalidFee,
/// The transaction priority level given was invalid.
#[cfg_attr(feature = "std", error("invalid priority"))] #[cfg_attr(feature = "std", error("invalid priority"))]
InvalidPriority, InvalidPriority,
} }
@@ -114,12 +131,15 @@ fn read_epee_vi<R: io::Read>(reader: &mut R) -> io::Result<u64> {
_ => unreachable!(), _ => unreachable!(),
}; };
let mut vi = u64::from(vi_start >> 2); let mut vi = u64::from(vi_start >> 2);
for i in 1 .. len { for i in 1..len {
vi |= u64::from(read_byte(reader)?) << (((i - 1) * 8) + 6); vi |= u64::from(read_byte(reader)?) << (((i - 1) * 8) + 6);
} }
Ok(vi) Ok(vi)
} }
/// A trait representing an RPC connection.
///
/// Note that [`HttpRpc`] already implements this trait.
#[async_trait] #[async_trait]
pub trait RpcConnection: Clone + Debug { pub trait RpcConnection: Clone + Debug {
/// Perform a POST request to the specified route with the specified body. /// Perform a POST request to the specified route with the specified body.
@@ -128,6 +148,7 @@ pub trait RpcConnection: Clone + Debug {
async fn post(&self, route: &str, body: Vec<u8>) -> Result<Vec<u8>, RpcError>; async fn post(&self, route: &str, body: Vec<u8>) -> Result<Vec<u8>, RpcError>;
} }
/// A generic RPC client.
// TODO: Make this provided methods for RpcConnection? // TODO: Make this provided methods for RpcConnection?
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Rpc<R: RpcConnection>(R); pub struct Rpc<R: RpcConnection>(R);
@@ -202,6 +223,7 @@ impl<R: RpcConnection> Rpc<R> {
) )
} }
/// Get the node's current block height.
pub async fn get_height(&self) -> Result<usize, RpcError> { pub async fn get_height(&self) -> Result<usize, RpcError> {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct HeightResponse { struct HeightResponse {
@@ -210,6 +232,7 @@ impl<R: RpcConnection> Rpc<R> {
Ok(self.rpc_call::<Option<()>, HeightResponse>("get_height", None).await?.height) Ok(self.rpc_call::<Option<()>, HeightResponse>("get_height", None).await?.height)
} }
/// Get [`Transaction`]s by their `hashes`.
pub async fn get_transactions(&self, hashes: &[[u8; 32]]) -> Result<Vec<Transaction>, RpcError> { pub async fn get_transactions(&self, hashes: &[[u8; 32]]) -> Result<Vec<Transaction>, RpcError> {
if hashes.is_empty() { if hashes.is_empty() {
return Ok(vec![]); return Ok(vec![]);
@@ -274,6 +297,7 @@ impl<R: RpcConnection> Rpc<R> {
.collect() .collect()
} }
/// Get a single [`Transaction`] by its hash.
pub async fn get_transaction(&self, tx: [u8; 32]) -> Result<Transaction, RpcError> { pub async fn get_transaction(&self, tx: [u8; 32]) -> Result<Transaction, RpcError> {
self.get_transactions(&[tx]).await.map(|mut txs| txs.swap_remove(0)) self.get_transactions(&[tx]).await.map(|mut txs| txs.swap_remove(0))
} }
@@ -314,6 +338,7 @@ impl<R: RpcConnection> Rpc<R> {
Ok(block) Ok(block)
} }
/// Get a [`Block`] by its height number.
pub async fn get_block_by_number(&self, number: usize) -> Result<Block, RpcError> { pub async fn get_block_by_number(&self, number: usize) -> Result<Block, RpcError> {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct BlockResponse { struct BlockResponse {
@@ -341,6 +366,7 @@ impl<R: RpcConnection> Rpc<R> {
} }
} }
/// Get all the [`Transaction`]s belonging to the corresponding block `hash`.
pub async fn get_block_transactions(&self, hash: [u8; 32]) -> Result<Vec<Transaction>, RpcError> { pub async fn get_block_transactions(&self, hash: [u8; 32]) -> Result<Vec<Transaction>, RpcError> {
let block = self.get_block(hash).await?; let block = self.get_block(hash).await?;
let mut res = vec![block.miner_tx]; let mut res = vec![block.miner_tx];
@@ -405,7 +431,7 @@ impl<R: RpcConnection> Rpc<R> {
let read_object = |reader: &mut &[u8]| -> io::Result<Vec<u64>> { let read_object = |reader: &mut &[u8]| -> io::Result<Vec<u64>> {
let fields = read_byte(reader)? >> 2; let fields = read_byte(reader)? >> 2;
for _ in 0 .. fields { for _ in 0..fields {
let name_len = read_byte(reader)?; let name_len = read_byte(reader)?;
let name = read_raw_vec(read_byte, name_len.into(), reader)?; let name = read_raw_vec(read_byte, name_len.into(), reader)?;
@@ -458,7 +484,7 @@ impl<R: RpcConnection> Rpc<R> {
}; };
let mut bytes_res = vec![]; let mut bytes_res = vec![];
for _ in 0 .. iters { for _ in 0..iters {
bytes_res.push(f(reader)?); bytes_res.push(f(reader)?);
} }
@@ -478,8 +504,8 @@ impl<R: RpcConnection> Rpc<R> {
if bytes_res if bytes_res
.first() .first()
.ok_or_else(|| io::Error::other("status wasn't a string"))? .ok_or_else(|| io::Error::other("status wasn't a string"))?
.as_slice() != .as_slice()
b"OK" != b"OK"
{ {
// TODO: Better handle non-OK responses // TODO: Better handle non-OK responses
Err(io::Error::other("response wasn't OK"))?; Err(io::Error::other("response wasn't OK"))?;
@@ -623,6 +649,7 @@ impl<R: RpcConnection> Rpc<R> {
.collect() .collect()
} }
/// Get a [`Fee`] based on [`Protocol::v14`] rules.
async fn get_fee_v14(&self, priority: FeePriority) -> Result<Fee, RpcError> { async fn get_fee_v14(&self, priority: FeePriority) -> Result<Fee, RpcError> {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct FeeResponseV14 { struct FeeResponseV14 {
@@ -702,6 +729,7 @@ impl<R: RpcConnection> Rpc<R> {
} }
} }
/// Broadcast a [`Transaction`] to the network.
pub async fn publish_transaction(&self, tx: &Transaction) -> Result<(), RpcError> { pub async fn publish_transaction(&self, tx: &Transaction) -> Result<(), RpcError> {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@@ -730,6 +758,13 @@ impl<R: RpcConnection> Rpc<R> {
Ok(()) Ok(())
} }
/// Generate blocks.
///
/// - `address` is the address that will receive the coinbase reward
/// - `block_count` is the number of blocks that will be generated
///
/// Note this is only for testing with nodes started with `--regtest`, see:
/// <https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#generateblocks>.
// TODO: Take &Address, not &str? // TODO: Take &Address, not &str?
pub async fn generate_blocks( pub async fn generate_blocks(
&self, &self,