diff --git a/coins/monero/src/frost.rs b/coins/monero/src/frost.rs index a1955451..265a86eb 100644 --- a/coins/monero/src/frost.rs +++ b/coins/monero/src/frost.rs @@ -22,7 +22,7 @@ use crate::random_scalar; pub type Transcript = DigestTranscript::; -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] pub enum MultisigError { #[error("internal error ({0})")] InternalError(String), diff --git a/coins/monero/src/ringct/clsag/mod.rs b/coins/monero/src/ringct/clsag/mod.rs index 31d4d3dd..51d9718b 100644 --- a/coins/monero/src/ringct/clsag/mod.rs +++ b/coins/monero/src/ringct/clsag/mod.rs @@ -27,7 +27,7 @@ lazy_static! { static ref INV_EIGHT: Scalar = Scalar::from(8 as u8).invert(); } -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] pub enum ClsagError { #[error("internal error ({0})")] InternalError(String), diff --git a/coins/monero/src/rpc.rs b/coins/monero/src/rpc.rs index 2c3b8c25..13ba9026 100644 --- a/coins/monero/src/rpc.rs +++ b/coins/monero/src/rpc.rs @@ -18,14 +18,14 @@ pub struct JsonRpcResponse { result: T } -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] pub enum RpcError { #[error("internal error ({0})")] InternalError(String), #[error("connection error")] ConnectionError, - #[error("transaction not found (expected {1}, got {0})")] - TransactionsNotFound(usize, usize), + #[error("transactions not found")] + TransactionsNotFound(Vec<[u8; 32]>), #[error("invalid point ({0})")] InvalidPoint(String), #[error("pruned transaction")] @@ -99,45 +99,67 @@ impl Rpc { Ok(self.rpc_call::, HeightResponse>("get_height", None).await?.height) } - pub async fn get_transactions(&self, hashes: &[[u8; 32]]) -> Result, RpcError> { + async fn get_transactions_core( + &self, + hashes: &[[u8; 32]] + ) -> Result<(Vec>, Vec<[u8; 32]>), RpcError> { if hashes.len() == 0 { - return Ok(vec![]); + return Ok((vec![], vec![])); } #[derive(Deserialize, Debug)] struct TransactionResponse { + tx_hash: String, as_hex: String, pruned_as_hex: String } #[derive(Deserialize, Debug)] struct TransactionsResponse { + #[serde(default)] + missed_tx: Vec, txs: Vec } let txs: TransactionsResponse = self.rpc_call("get_transactions", Some(json!({ - "txs_hashes": hashes.iter().map(|hash| hex::encode(&hash)).collect::>() + "txs_hashes": hashes.iter().map(|hash| hex::encode(&hash)).collect::>() }))).await?; - if txs.txs.len() != hashes.len() { - Err(RpcError::TransactionsNotFound(txs.txs.len(), hashes.len()))?; - } - txs.txs.iter().enumerate().map(|(i, res)| { - let tx = Transaction::deserialize( - &mut std::io::Cursor::new( - rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap() - ) - ).map_err(|_| RpcError::InvalidTransaction(hashes[i]))?; + Ok(( + txs.txs.iter().map(|res| { + let tx = Transaction::deserialize( + &mut std::io::Cursor::new( + rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap() + ) + ).map_err(|_| RpcError::InvalidTransaction(hex::decode(&res.tx_hash).unwrap().try_into().unwrap()))?; - // https://github.com/monero-project/monero/issues/8311 - if res.as_hex.len() == 0 { - match tx.prefix.inputs.get(0) { - Some(Input::Gen { .. }) => (), - _ => Err(RpcError::PrunedTransaction)? + // https://github.com/monero-project/monero/issues/8311 + if res.as_hex.len() == 0 { + match tx.prefix.inputs.get(0) { + Some(Input::Gen { .. }) => (), + _ => Err(RpcError::PrunedTransaction)? + } } - } - Ok(tx) - }).collect::>() + Ok(tx) + }).collect(), + + txs.missed_tx.iter().map(|hash| hex::decode(&hash).unwrap().try_into().unwrap()).collect() + )) + } + + pub async fn get_transactions(&self, hashes: &[[u8; 32]]) -> Result, RpcError> { + let (txs, missed) = self.get_transactions_core(hashes).await?; + if missed.len() != 0 { + Err(RpcError::TransactionsNotFound(missed))?; + } + // This will clone several KB and is accordingly inefficient + // TODO: Optimize + txs.iter().cloned().collect::>() + } + + pub async fn get_transactions_possible(&self, hashes: &[[u8; 32]]) -> Result, RpcError> { + let (txs, _) = self.get_transactions_core(hashes).await?; + Ok(txs.iter().cloned().filter_map(|tx| tx.ok()).collect()) } pub async fn get_block(&self, height: usize) -> Result { @@ -160,13 +182,31 @@ impl Rpc { ) } - pub async fn get_block_transactions(&self, height: usize) -> Result, RpcError> { + async fn get_block_transactions_core( + &self, + height: usize, + possible: bool + ) -> Result, RpcError> { let block = self.get_block(height).await?; let mut res = vec![block.miner_tx]; - res.extend(self.get_transactions(&block.txs).await?); + res.extend( + if possible { + self.get_transactions_possible(&block.txs).await? + } else { + self.get_transactions(&block.txs).await? + } + ); Ok(res) } + pub async fn get_block_transactions(&self, height: usize) -> Result, RpcError> { + self.get_block_transactions_core(height, false).await + } + + pub async fn get_block_transactions_possible(&self, height: usize) -> Result, RpcError> { + self.get_block_transactions_core(height, true).await + } + pub async fn get_o_indexes(&self, hash: [u8; 32]) -> Result, RpcError> { #[derive(Serialize, Debug)] struct Request { diff --git a/coins/monero/src/wallet/send/mod.rs b/coins/monero/src/wallet/send/mod.rs index cb1bebb5..8d307a38 100644 --- a/coins/monero/src/wallet/send/mod.rs +++ b/coins/monero/src/wallet/send/mod.rs @@ -76,7 +76,7 @@ impl SendOutput { } -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] pub enum TransactionError { #[error("no inputs")] NoInputs, diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index 902d7bc3..2efb5041 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -15,7 +15,7 @@ pub mod sign; pub mod tests; /// Set of errors for curve-related operations, namely encoding and decoding -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] pub enum CurveError { #[error("invalid length for data (expected {0}, got {0})")] InvalidLength(usize, usize), @@ -150,7 +150,7 @@ impl MultisigParams { pub fn i(&self) -> u16 { self.i } } -#[derive(Error, Debug)] +#[derive(Clone, Error, Debug)] pub enum FrostError { #[error("a parameter was 0 (required {0}, participants {1})")] ZeroParameter(u16, u16),