-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cuprated: internal signatures required for RPC #297
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This review is split like such:
Title | Purpose |
---|---|
Misc | Miscellaneous changes added and/or needed in the future |
Signatures | New requests/responses added to our tower::Service s |
Types | New types added (for usage in the request/responses) |
TODO | Things to do after this PR |
Questions that need answering:
- Are any of the requests/responses impossible to fulfill?
- Can any of the requests/responses be done in a better way?
- Do any of the requests/responses belong to a different
Service
?
//! The `DatabaseReadHandle` can be shared as it is cheaply [`Clone`]able, however, | ||
//! the `DatabaseWriteHandle` cannot be cloned. There is only 1 place in Cuprate that | ||
//! writes, so it is passed there and used. | ||
//! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misc
Not true anymore, we need Clone
on CupratedRpcHandler
and thus the DatabaseWriteHandle
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does need to be removed for the tx-pool handle, however the database is still only written to in 1 place, the blockchain manager.
You shouldn't need to hold a write handle to the blockchain in the RPC
/// [`AddressBookRequest::PeerlistSize`] | ||
pub(super) async fn peerlist_size<Z: NetworkZone>( | ||
address_book: &mut impl AddressBook<Z>, | ||
) -> Result<(u64, u64), Error> { | ||
let AddressBookResponse::PeerlistSize { white, grey } = address_book | ||
.ready() | ||
.await | ||
.expect("TODO") | ||
.call(AddressBookRequest::PeerlistSize) | ||
.await | ||
.expect("TODO") | ||
else { | ||
unreachable!(); | ||
}; | ||
|
||
Ok((usize_to_u64(white), usize_to_u64(grey))) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misc
The services that use tower::BoxError
don't work with anyhow::Error
when using ?
, can we swap them to anyhow
?
The services are:
- address book
- blockchain context
- transaction pool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there are other as well, probably should be done in a separate PR
p2p/p2p-core/src/ban.rs
Outdated
//! Data structures related to bans. | ||
|
||
use std::time::{Duration, Instant}; | ||
|
||
use crate::NetZoneAddress; | ||
|
||
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | ||
pub struct SetBan<A: NetZoneAddress> { | ||
pub address: A, | ||
pub ban: bool, | ||
pub duration: Duration, | ||
} | ||
|
||
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | ||
pub struct BanState<A: NetZoneAddress> { | ||
pub address: A, | ||
pub banned: bool, | ||
pub unban_instant: Instant, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Types (address book)
Some of the requests/responses in this PR require defining some types.
A lot of these are 1-1 with the RPC types but I decided on making more specific versions because:
- Pulling in
cuprate-rpc-types
everywhere seems wrong - The RPC types are JSON-like, so a
[u8; 32]
field will be something likeString
, adding these types lets us use the canonical type internally
Some of the types are defined in the crate where the request/response is defined, many are defined in cuprate-types
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah separating them is a good idea IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those types seem weird though, wouldn't this make more sense:
//! Data structures related to bans. | |
use std::time::{Duration, Instant}; | |
use crate::NetZoneAddress; | |
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | |
pub struct SetBan<A: NetZoneAddress> { | |
pub address: A, | |
pub ban: bool, | |
pub duration: Duration, | |
} | |
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | |
pub struct BanState<A: NetZoneAddress> { | |
pub address: A, | |
pub banned: bool, | |
pub unban_instant: Instant, | |
} | |
//! Data structures related to bans. | |
use std::time::{Duration, Instant}; | |
use crate::NetZoneAddress; | |
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | |
pub struct SetBan<A: NetZoneAddress> { | |
pub address: A, | |
pub ban: Option<Duration>, | |
} | |
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | |
pub struct BanState<A: NetZoneAddress> { | |
pub address: A, | |
pub banned: Option<Instant>, | |
} |
//! Transaction metadata. | ||
|
||
/// Data about a transaction in the pool. | ||
/// | ||
/// Used in [`TxpoolReadResponse::Backlog`](crate::service::interface::TxpoolReadResponse::Backlog). | ||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
pub struct TxEntry { | ||
/// The transaction's weight. | ||
pub weight: u64, | ||
/// The transaction's fee. | ||
pub fee: u64, | ||
/// How long the transaction has been in the pool. | ||
pub time_in_pool: std::time::Duration, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Types (txpool)
/// Input required to generate an output histogram. | ||
/// | ||
/// Used in RPC's `get_output_histogram`. | ||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct OutputHistogramInput { | ||
pub amounts: Vec<u64>, | ||
pub min_count: u64, | ||
pub max_count: u64, | ||
pub unlocked: bool, | ||
pub recent_cutoff: u64, | ||
} | ||
|
||
/// A single entry in an output histogram. | ||
/// | ||
/// Used in RPC's `get_output_histogram`. | ||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct OutputHistogramEntry { | ||
pub amount: u64, | ||
pub total_instances: u64, | ||
pub unlocked_instances: u64, | ||
pub recent_instances: u64, | ||
} | ||
|
||
/// Data of summed coinbase transactions. | ||
/// | ||
/// Used in RPC's `get_coinbase_tx_sum`. | ||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct CoinbaseTxSum { | ||
pub emission_amount: u128, | ||
pub fee_amount: u128, | ||
pub wide_emission_amount: u128, | ||
pub wide_fee_amount: u128, | ||
} | ||
|
||
/// Data to create a custom block template. | ||
/// | ||
/// Used in RPC's `get_miner_data`. | ||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct MinerData { | ||
pub major_version: u8, | ||
pub height: u64, | ||
pub prev_id: [u8; 32], | ||
pub seed_hash: [u8; 32], | ||
pub difficulty: u128, | ||
pub median_weight: u64, | ||
pub already_generated_coins: u64, | ||
pub tx_backlog: Vec<MinerDataTxBacklogEntry>, | ||
} | ||
|
||
/// A transaction in the txpool. | ||
/// | ||
/// Used in [`MinerData::tx_backlog`]. | ||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct MinerDataTxBacklogEntry { | ||
pub id: [u8; 32], | ||
pub weight: u64, | ||
pub fee: u64, | ||
} | ||
|
||
/// Information on a [`HardFork`]. | ||
/// | ||
/// Used in RPC's `hard_fork_info`. | ||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct HardForkInfo { | ||
pub earliest_height: u64, | ||
pub enabled: bool, | ||
pub state: u32, | ||
pub threshold: u32, | ||
pub version: u8, | ||
pub votes: u32, | ||
pub voting: u8, | ||
pub window: u32, | ||
} | ||
|
||
/// Estimated fee data. | ||
/// | ||
/// Used in RPC's `get_fee_estimate`. | ||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct FeeEstimate { | ||
pub fee: u64, | ||
pub fees: Vec<u64>, | ||
pub quantization_mask: u64, | ||
} | ||
|
||
/// Information on a (maybe alternate) chain. | ||
/// | ||
/// Used in RPC's `get_alternate_chains`. | ||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct ChainInfo { | ||
pub block_hash: [u8; 32], | ||
pub block_hashes: Vec<[u8; 32]>, | ||
pub difficulty: u128, | ||
pub height: u64, | ||
pub length: u64, | ||
pub main_chain_parent_block: [u8; 32], | ||
pub wide_difficulty: u128, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Types (everything else)
/// [`BlockchainReadRequest::Block`] | ||
fn block(env: &ConcreteEnv, block_height: BlockHeight) -> ResponseResult { | ||
Ok(BlockchainResponse::Block(todo!())) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
All the new request/responses are fully typed but they will all panic with todo!()
.
The exception is AddressBookRequest::GetBan
which I added a new function for.
Handlers can be added in a different PR.
/// | ||
/// A map of seed height to `RandomX` VMs. | ||
RxVms(HashMap<usize, Arc<RandomXVm>>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
Why does this return a HashMap
when the request is CurrentRxVm
?
I might be misremembering but I think somewhere in RPC we need access to a RandomX VM, would calling .last()
on this HashMap
always give us the latest in a Some
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this return a HashMap when the request is CurrentRxVm?
It should probably be CurrentRxVms
, it needs to return more than one so we can compute PoW concurrently.
I might be misremembering but I think somewhere in RPC we need access to a RandomX VM
You will probably just need to generate a new VM - I don't think you will need the current active one, I may be wrong though I just can't see why you would.
binaries/cuprated/src/rpc/handler.rs
Outdated
pub txpool_read: TxpoolReadHandle, | ||
|
||
/// Write handle to the transaction pool database. | ||
pub txpool_write: TxpoolWriteHandle, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Boog900 some questions about #279 (comment):
The transaction pool is going to have a specific interface for requests that mutate the pool, just to keep in mind, it wont directly use the write handle.
- What should be here in
txpool_write
? - Is "specific interface" just going to be a different
tower::Service
? - Why does the txpool need this special interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should be here in txpool_write?
Is "specific interface" just going to be a different tower::Service?
Yes, so a handle to that other service.
Why does the txpool need this special interface?
To synchronise tx-pool state so we don't end up verifying the same tx across multiple connections before adding them to the pool. There are other operations that require more synchronization than a database write handle can provide.
/// Relay a block to the network. | ||
RelayBlock(Block), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be add block, this also happens to be the one request I already have in the blockchain manager PR (handle_incoming_block
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it have the same semantics though? It adds a block to our local chain and broadcasts it?
The usecase is submit_block
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep
binaries/cuprated/src/rpc/handler.rs
Outdated
pub txpool_read: TxpoolReadHandle, | ||
|
||
/// Write handle to the transaction pool database. | ||
pub txpool_write: TxpoolWriteHandle, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should be here in txpool_write?
Is "specific interface" just going to be a different tower::Service?
Yes, so a handle to that other service.
Why does the txpool need this special interface?
To synchronise tx-pool state so we don't end up verifying the same tx across multiple connections before adding them to the pool. There are other operations that require more synchronization than a database write handle can provide.
/// [`AddressBookRequest::PeerlistSize`] | ||
pub(super) async fn peerlist_size<Z: NetworkZone>( | ||
address_book: &mut impl AddressBook<Z>, | ||
) -> Result<(u64, u64), Error> { | ||
let AddressBookResponse::PeerlistSize { white, grey } = address_book | ||
.ready() | ||
.await | ||
.expect("TODO") | ||
.call(AddressBookRequest::PeerlistSize) | ||
.await | ||
.expect("TODO") | ||
else { | ||
unreachable!(); | ||
}; | ||
|
||
Ok((usize_to_u64(white), usize_to_u64(grey))) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there are other as well, probably should be done in a separate PR
/// | ||
/// A map of seed height to `RandomX` VMs. | ||
RxVms(HashMap<usize, Arc<RandomXVm>>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this return a HashMap when the request is CurrentRxVm?
It should probably be CurrentRxVms
, it needs to return more than one so we can compute PoW concurrently.
I might be misremembering but I think somewhere in RPC we need access to a RandomX VM
You will probably just need to generate a new VM - I don't think you will need the current active one, I may be wrong though I just can't see why you would.
p2p/p2p-core/src/ban.rs
Outdated
//! Data structures related to bans. | ||
|
||
use std::time::{Duration, Instant}; | ||
|
||
use crate::NetZoneAddress; | ||
|
||
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | ||
pub struct SetBan<A: NetZoneAddress> { | ||
pub address: A, | ||
pub ban: bool, | ||
pub duration: Duration, | ||
} | ||
|
||
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | ||
pub struct BanState<A: NetZoneAddress> { | ||
pub address: A, | ||
pub banned: bool, | ||
pub unban_instant: Instant, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah separating them is a good idea IMO
p2p/p2p-core/src/ban.rs
Outdated
//! Data structures related to bans. | ||
|
||
use std::time::{Duration, Instant}; | ||
|
||
use crate::NetZoneAddress; | ||
|
||
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | ||
pub struct SetBan<A: NetZoneAddress> { | ||
pub address: A, | ||
pub ban: bool, | ||
pub duration: Duration, | ||
} | ||
|
||
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | ||
pub struct BanState<A: NetZoneAddress> { | ||
pub address: A, | ||
pub banned: bool, | ||
pub unban_instant: Instant, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those types seem weird though, wouldn't this make more sense:
//! Data structures related to bans. | |
use std::time::{Duration, Instant}; | |
use crate::NetZoneAddress; | |
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | |
pub struct SetBan<A: NetZoneAddress> { | |
pub address: A, | |
pub ban: bool, | |
pub duration: Duration, | |
} | |
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | |
pub struct BanState<A: NetZoneAddress> { | |
pub address: A, | |
pub banned: bool, | |
pub unban_instant: Instant, | |
} | |
//! Data structures related to bans. | |
use std::time::{Duration, Instant}; | |
use crate::NetZoneAddress; | |
/// Data within [`crate::services::AddressBookRequest::SetBan`]. | |
pub struct SetBan<A: NetZoneAddress> { | |
pub address: A, | |
pub ban: Option<Duration>, | |
} | |
/// Data within [`crate::services::AddressBookResponse::GetBans`]. | |
pub struct BanState<A: NetZoneAddress> { | |
pub address: A, | |
pub banned: Option<Instant>, | |
} |
//! The `DatabaseReadHandle` can be shared as it is cheaply [`Clone`]able, however, | ||
//! the `DatabaseWriteHandle` cannot be cloned. There is only 1 place in Cuprate that | ||
//! writes, so it is passed there and used. | ||
//! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does need to be removed for the tx-pool handle, however the database is still only written to in 1 place, the blockchain manager.
You shouldn't need to hold a write handle to the blockchain in the RPC
types/src/blockchain.rs
Outdated
/// Get a [`Block`] by its height. | ||
Block { | ||
height: usize, | ||
}, | ||
|
||
/// Get a [`Block`] by its hash. | ||
BlockByHash([u8; 32]), | ||
|
||
/// Get the total amount of non-coinbase transactions in the chain. | ||
TotalTxCount, | ||
|
||
/// Get the current size of the database. | ||
DatabaseSize, | ||
|
||
// Get the difficulty for the next block in the chain. | ||
Difficulty(usize), | ||
|
||
/// Get an output histogram. | ||
/// | ||
/// TODO: document fields after impl. | ||
OutputHistogram(OutputHistogramInput), | ||
|
||
/// Get the coinbase amount and the fees amount for | ||
/// `N` last blocks starting at particular height. | ||
/// | ||
/// TODO: document fields after impl. | ||
CoinbaseTxSum { | ||
height: usize, | ||
count: u64, | ||
}, | ||
|
||
/// Get the necessary data to create a custom block template. | ||
MinerData, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Difficulty
is contained in the blockchain context so shouldn't need a new request (my bad for missing this).
MinerData
will not be able to be a single request. Looking at its data it would need a request to the blockchain cache and to the tx-pool, both of these requests are already there/in this PR so this could just be removed I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The blockchain manager API should change but for now is fine
What
Adds the
{function, request, response}
signatures needed bycuprated
's RPC system to the{blockchain, txpool, p2p, blockchain_context}
APIs.Closes #279.
This covers most (but not all) signatures needed for all RPC requests (JSON-RPC, binary, other).