diff --git a/binaries/cuprated/src/rpc/other.rs b/binaries/cuprated/src/rpc/other.rs index 7cbbd34..dc113c8 100644 --- a/binaries/cuprated/src/rpc/other.rs +++ b/binaries/cuprated/src/rpc/other.rs @@ -37,10 +37,10 @@ use cuprate_rpc_types::{ }, }; use cuprate_types::{ - rpc::{KeyImageSpentStatus, OutKey, PoolInfo, PoolTxInfo}, + rpc::{KeyImageSpentStatus, OutKey, PoolInfo, PoolTxInfo, PublicNode}, TxInPool, TxRelayChecks, }; -use monero_serai::transaction::{Input, Transaction}; +use monero_serai::transaction::{Input, Timelock, Transaction}; use crate::{ constants::UNSUPPORTED_RPC_CALL, @@ -49,6 +49,7 @@ use crate::{ request::{blockchain, blockchain_context, blockchain_manager, txpool}, CupratedRpcHandler, }, + statics::START_INSTANT_UNIX, }; use super::request::address_book; @@ -541,9 +542,12 @@ async fn get_limit( mut state: CupratedRpcHandler, _: GetLimitRequest, ) -> Result { + todo!("waiting on p2p service"); + Ok(GetLimitResponse { base: helper::response_base(false), - ..todo!() + limit_down: todo!(), + limit_up: todo!(), }) } @@ -552,9 +556,12 @@ async fn set_limit( mut state: CupratedRpcHandler, request: SetLimitRequest, ) -> Result { + todo!("waiting on p2p service"); + Ok(SetLimitResponse { base: helper::response_base(false), - ..todo!() + limit_down: todo!(), + limit_up: todo!(), }) } @@ -563,9 +570,11 @@ async fn out_peers( mut state: CupratedRpcHandler, request: OutPeersRequest, ) -> Result { + todo!("waiting on p2p service"); + Ok(OutPeersResponse { base: helper::response_base(false), - ..todo!() + out_peers: todo!(), }) } @@ -574,9 +583,11 @@ async fn in_peers( mut state: CupratedRpcHandler, request: InPeersRequest, ) -> Result { + todo!("waiting on p2p service"); + Ok(InPeersResponse { base: helper::response_base(false), - ..todo!() + in_peers: todo!(), }) } @@ -585,9 +596,15 @@ async fn get_net_stats( mut state: CupratedRpcHandler, _: GetNetStatsRequest, ) -> Result { + todo!("waiting on p2p service"); + Ok(GetNetStatsResponse { base: helper::response_base(false), - ..todo!() + start_time: *START_INSTANT_UNIX, + total_packets_in: todo!(), + total_bytes_in: todo!(), + total_packets_out: todo!(), + total_bytes_out: todo!(), }) } @@ -600,32 +617,39 @@ async fn get_outs( return Err(anyhow!("Too many outs requested")); } - let mut outputs = HashMap::>::with_capacity(request.outputs.len()); - for out in request.outputs { + let outputs = { + let mut outputs = HashMap::>::with_capacity(request.outputs.len()); + + for out in request.outputs { + outputs + .entry(out.amount) + .and_modify(|set| { + set.insert(out.index); + }) + .or_insert_with(|| HashSet::from([out.index])); + } + outputs - .entry(out.amount) - .and_modify(|set| { - set.insert(out.index); - }) - .or_insert_with(|| HashSet::from([out.index])); - } + }; let outs = blockchain::outputs(&mut state.blockchain_read, outputs) .await? .into_iter() .flat_map(|(amount, index_map)| { index_map.into_iter().map(|(index, out)| OutKey { - key: todo!(), - mask: todo!(), - unlocked: todo!(), + key: out.key.map_or(Hex([0; 32]), |e| Hex(e.compress().0)), + mask: Hex(out.commitment.compress().0), + unlocked: matches!(out.time_lock, Timelock::None), height: usize_to_u64(out.height), - txid: todo!(), + txid: if request.get_txid { + hex::encode(out.txid) + } else { + String::new() + }, }) }) .collect::>(); - // TODO: check txpool - Ok(GetOutsResponse { base: helper::response_base(false), outs, @@ -651,9 +675,20 @@ async fn get_transaction_pool_hashes( mut state: CupratedRpcHandler, _: GetTransactionPoolHashesRequest, ) -> Result { + let include_sensitive_txs = !state.is_restricted(); + + // FIXME: this request is a bit overkill, we only need the hashes. + // We could create a separate request for this. + let tx_hashes = txpool::pool(&mut state.txpool_read, include_sensitive_txs) + .await? + .0 + .into_iter() + .map(|tx| tx.id_hash) + .collect(); + Ok(GetTransactionPoolHashesResponse { base: helper::response_base(false), - ..todo!() + tx_hashes, }) } @@ -662,9 +697,37 @@ async fn get_public_nodes( mut state: CupratedRpcHandler, request: GetPublicNodesRequest, ) -> Result { + let (white, gray) = address_book::peerlist::(&mut DummyAddressBook).await?; + + fn map(peers: Vec) -> Vec { + peers + .into_iter() + .map(|peer| { + let cuprate_types::rpc::Peer { + host, + rpc_port, + rpc_credits_per_hash, + last_seen, + .. + } = peer; + + PublicNode { + host, + rpc_port, + rpc_credits_per_hash, + last_seen, + } + }) + .collect() + } + + let white = map(white); + let gray = map(gray); + Ok(GetPublicNodesResponse { base: helper::response_base(false), - ..todo!() + white, + gray, }) } diff --git a/consensus/tests/verify_correct_txs.rs b/consensus/tests/verify_correct_txs.rs index 2c80fac..14ba379 100644 --- a/consensus/tests/verify_correct_txs.rs +++ b/consensus/tests/verify_correct_txs.rs @@ -78,6 +78,7 @@ macro_rules! test_verify_valid_v2_tx { key: CompressedEdwardsY::from_slice(&hex_literal::hex!($ring_member)) .unwrap() .decompress(), + txid: [0; 32], }),)+)+ ]; @@ -107,6 +108,7 @@ macro_rules! test_verify_valid_v2_tx { key: CompressedEdwardsY::from_slice(&hex_literal::hex!($ring_member)) .unwrap() .decompress(), + txid: [0; 32], }),)+)+ ]; diff --git a/storage/blockchain/src/ops/output.rs b/storage/blockchain/src/ops/output.rs index 96d94bb..9f9d1a3 100644 --- a/storage/blockchain/src/ops/output.rs +++ b/storage/blockchain/src/ops/output.rs @@ -7,13 +7,13 @@ use monero_serai::transaction::Timelock; use cuprate_database::{ DbResult, RuntimeError, {DatabaseRo, DatabaseRw}, }; -use cuprate_helper::crypto::compute_zero_commitment; -use cuprate_helper::map::u64_to_timelock; +use cuprate_helper::{cast::u32_to_usize, crypto::compute_zero_commitment}; +use cuprate_helper::{cast::u64_to_usize, map::u64_to_timelock}; use cuprate_types::OutputOnChain; use crate::{ ops::macros::{doc_add_block_inner_invariant, doc_error}, - tables::{Outputs, RctOutputs, Tables, TablesMut, TxUnlockTime}, + tables::{BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxUnlockTime}, types::{Amount, AmountIndex, Output, OutputFlags, PreRctOutputId, RctOutput}, }; @@ -153,6 +153,7 @@ pub fn output_to_output_on_chain( output: &Output, amount: Amount, table_tx_unlock_time: &impl DatabaseRo, + table_block_txs_hashes: &impl DatabaseRo, ) -> DbResult { let commitment = compute_zero_commitment(amount); @@ -169,11 +170,18 @@ pub fn output_to_output_on_chain( .map(|y| y.decompress()) .unwrap_or(None); + let txid = { + let height = u32_to_usize(output.height); + let tx_idx = u64_to_usize(output.tx_idx); + table_block_txs_hashes.get(&height)?[tx_idx] + }; + Ok(OutputOnChain { height: output.height as usize, time_lock, key, commitment, + txid, }) } @@ -189,6 +197,7 @@ pub fn output_to_output_on_chain( pub fn rct_output_to_output_on_chain( rct_output: &RctOutput, table_tx_unlock_time: &impl DatabaseRo, + table_block_txs_hashes: &impl DatabaseRo, ) -> DbResult { // INVARIANT: Commitments stored are valid when stored by the database. let commitment = CompressedEdwardsY::from_slice(&rct_output.commitment) @@ -209,11 +218,18 @@ pub fn rct_output_to_output_on_chain( .map(|y| y.decompress()) .unwrap_or(None); + let txid = { + let height = u32_to_usize(rct_output.height); + let tx_idx = u64_to_usize(rct_output.tx_idx); + table_block_txs_hashes.get(&height)?[tx_idx] + }; + Ok(OutputOnChain { height: rct_output.height as usize, time_lock, key, commitment, + txid, }) } @@ -225,14 +241,22 @@ pub fn id_to_output_on_chain(id: &PreRctOutputId, tables: &impl Tables) -> DbRes // v2 transactions. if id.amount == 0 { let rct_output = get_rct_output(&id.amount_index, tables.rct_outputs())?; - let output_on_chain = rct_output_to_output_on_chain(&rct_output, tables.tx_unlock_time())?; + let output_on_chain = rct_output_to_output_on_chain( + &rct_output, + tables.tx_unlock_time(), + tables.block_txs_hashes(), + )?; Ok(output_on_chain) } else { // v1 transactions. let output = get_output(id, tables.outputs())?; - let output_on_chain = - output_to_output_on_chain(&output, id.amount, tables.tx_unlock_time())?; + let output_on_chain = output_to_output_on_chain( + &output, + id.amount, + tables.tx_unlock_time(), + tables.block_txs_hashes(), + )?; Ok(output_on_chain) } diff --git a/types/types/src/rpc/types.rs b/types/types/src/rpc/types.rs index 675d1fb..7a5c8d2 100644 --- a/types/types/src/rpc/types.rs +++ b/types/types/src/rpc/types.rs @@ -429,7 +429,8 @@ define_struct_and_impl_epee! { mask: Hex<32>, unlocked: bool, height: u64, - txid: Hex<32>, + // Optionally empty with `/get_outs`'s `"get_txid": false`. + txid: String, } #[doc = monero_definition_link!( diff --git a/types/types/src/types.rs b/types/types/src/types.rs index 8291a25..d165e38 100644 --- a/types/types/src/types.rs +++ b/types/types/src/types.rs @@ -145,6 +145,8 @@ pub struct OutputOnChain { pub key: Option, /// The output's commitment. pub commitment: EdwardsPoint, + /// The transaction ID this output belongs to. + pub txid: [u8; 32], } /// The inner response for a request for txs in a block.