Merge branch 'network-init' into block-downloader

This commit is contained in:
Boog900 2024-06-01 00:36:48 +01:00
commit 334a62115b
No known key found for this signature in database
GPG key ID: 42AB1287CB0041C2
84 changed files with 460 additions and 181 deletions

45
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View file

@ -0,0 +1,45 @@
---
name: 🐞 Bug report
about: Create a bug report
title: ''
labels: ["C-bug"]
assignees: ''
---
<!--
Notes:
- All these fields are optional, add as much or as little info as you like
- Please search to see if an issue already exists for the bug you encountered
-->
## Environment
Example:
- OS: Windows 11
- CPU: AMD Ryzen 5 5600X
- Memory: 16GB
- Storage: SSD 500GB
- Cuprate: v1.0.0
## Bug
What is the bug?
### Expected behavior
What correct beahvior was expected to happen?
## Steps to reproduce
Example:
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
## Log
If possible, add any related logs to help explain the bug.
Note: please remove any sensitive information from the logs (e.g. IP address).
## Screenshots
If possible, add screenshots to help explain the bug.
Note: please remove any sensitive information from the screenshot.

18
.github/ISSUE_TEMPLATE/discussion.md vendored Normal file
View file

@ -0,0 +1,18 @@
---
name: ⏳ Discussion
about: Start a discussion on a topic
title: ''
labels: ["C-discussion"]
assignees: ''
---
<!--
Note: Please search to see if an issue already exists for this discussion.
-->
## What
What would you would like to discuss?
## Why
Why would you would like to discuss this?

21
.github/ISSUE_TEMPLATE/feature.md vendored Normal file
View file

@ -0,0 +1,21 @@
---
name: ✨ Feature request
about: Request a feature
title: ''
labels: ["C-request"]
assignees: ''
---
<!--
Note: Please search to see if an issue already exists for this request, or if the feature already exists.
-->
## Feature
What is the feature you're requesting?
## Why
Why should your feature be added?
## Additional context
Add any other context or screenshots about the feature request.

24
.github/ISSUE_TEMPLATE/proposal.md vendored Normal file
View file

@ -0,0 +1,24 @@
---
name: 📜 Proposal
about: Propose an idea and request for comments
title: ''
labels: ["C-proposal"]
assignees: ''
---
<!--
Note: Please search to see if an issue already exists for this proposal.
-->
## What
Describe your proposal.
## Where
Describe where your proposal will cause changes to.
## Why
Describe why the proposal is needed.
## How
Describe how the proposal could be implemented.

15
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View file

@ -0,0 +1,15 @@
---
name: ❓ Question
about: Ask a question
title: ''
labels: ["C-question"]
assignees: ''
---
<!--
Note: Please search to see if an issue already exists for this question.
-->
## Question
What question would you like to ask?

38
.github/labeler.yml vendored
View file

@ -30,13 +30,31 @@ A-consensus: # This is the tag name
# will cause the `github-actions` bot
# to add the `A-consensus` tag.
# Cuprate's books.
A-books:
- changed-files:
- any-glob-to-any-file: books/**
A-book-architecture:
- changed-files:
- any-glob-to-any-file: books/architecture/**
A-book-protocol:
- changed-files:
- any-glob-to-any-file: books/protocol/**
# Crate (sub-)directories.
A-binaries:
- changed-files:
- any-glob-to-any-file: binaries/**
A-cryptonight:
- changed-files:
- any-glob-to-any-file: cryptonight/**
A-database:
A-storage:
- changed-files:
- any-glob-to-any-file: database/**
- any-glob-to-any-file: storage/**
A-helper:
- changed-files:
@ -62,10 +80,24 @@ A-types:
- changed-files:
- any-glob-to-any-file: types/**
A-rpc:
- changed-files:
- any-glob-to-any-file: rpc/**
A-zmq:
- changed-files:
- any-glob-to-any-file: zmq/**
# CI files.
A-ci:
- changed-files:
- any-glob-to-any-file: .github/**
# Misc
A-benches:
- changed-files:
- any-glob-to-any-file: benches/** # Benchmarks
A-dependency:
- changed-files:
- any-glob-to-any-file: '**/Cargo.toml' # Any Cargo file in the entire repo
@ -74,7 +106,9 @@ A-dependency:
A-workspace:
- changed-files:
- any-glob-to-any-file: '*' # Any root file change
- any-glob-to-any-file: misc/**
A-docs:
- changed-files:
- any-glob-to-any-file: '**/*.md' # Any file in the entire repo ending in `.md`
# `A-books` label is used for book documentation.

View file

@ -133,7 +133,7 @@ jobs:
- name: Test
run: |
cargo test --all-features --workspace
cargo test --package cuprate-database --no-default-features --features redb --features service
cargo test --package cuprate-blockchain --no-default-features --features redb --features service
# TODO: upload binaries with `actions/upload-artifact@v3`
- name: Build

View file

@ -49,14 +49,13 @@ This section is primarily targeted at maintainers. Most contributors aren't able
| Labels | Description | Example |
|--------------|-------------|---------|
| [A-] | The **area** of the project an issue relates to. | `A-database`, `A-rpc`, `A-docs`
| [A-] | The **area** of the project an issue relates to. | `A-storage`, `A-rpc`, `A-docs`
| [C-] | The **category** of an issue. | `C-cleanup`, `C-optimization`
| [D-] | Issues for **diagnostics**. | `D-confusing`, `D-verbose`
| [E-] | The **experience** level necessary to fix an issue. | `E-easy`, `E-hard`
| [I-] | The **importance** of the issue. | `I-crash`, `I-memory`
| [O-] | The **operating system** or platform that the issue is specific to. | `O-windows`, `O-macos`, `O-linux`
| [P-] | The issue **priority**. These labels can be assigned by anyone that understand the issue and is able to prioritize it, and remove the [I-prioritize] label. | `P-high`, `P-low`
| [wontfix] | Indicates an issue will not be fixed. | |
[A-]: https://github.com/Cuprate/cuprate/labels?q=A
[C-]: https://github.com/Cuprate/cuprate/labels?q=C
@ -65,4 +64,3 @@ This section is primarily targeted at maintainers. Most contributors aren't able
[I-]: https://github.com/Cuprate/cuprate/labels?q=I
[O-]: https://github.com/Cuprate/cuprate/labels?q=O
[P-]: https://github.com/Cuprate/cuprate/labels?q=P
[wontfix]: https://github.com/Cuprate/cuprate/labels?q=wontfix

76
Cargo.lock generated
View file

@ -505,6 +505,39 @@ dependencies = [
"thiserror",
]
[[package]]
name = "cuprate-blockchain"
version = "0.0.0"
dependencies = [
"bitflags 2.5.0",
"bytemuck",
"bytes",
"cfg-if",
"crossbeam",
"cuprate-helper",
"cuprate-test-utils",
"cuprate-types",
"curve25519-dalek",
"futures",
"heed",
"hex",
"hex-literal",
"monero-pruning",
"monero-serai",
"page_size",
"paste",
"pretty_assertions",
"rayon",
"redb",
"serde",
"tempfile",
"thiserror",
"thread_local",
"tokio",
"tokio-util",
"tower",
]
[[package]]
name = "cuprate-consensus"
version = "0.1.0"
@ -538,39 +571,6 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "cuprate-database"
version = "0.0.0"
dependencies = [
"bitflags 2.5.0",
"bytemuck",
"bytes",
"cfg-if",
"crossbeam",
"cuprate-helper",
"cuprate-test-utils",
"cuprate-types",
"curve25519-dalek",
"futures",
"heed",
"hex",
"hex-literal",
"monero-pruning",
"monero-serai",
"page_size",
"paste",
"pretty_assertions",
"rayon",
"redb",
"serde",
"tempfile",
"thiserror",
"thread_local",
"tokio",
"tokio-util",
"tower",
]
[[package]]
name = "cuprate-helper"
version = "0.1.0"
@ -640,6 +640,10 @@ dependencies = [
"tokio-util",
]
[[package]]
name = "cuprate-txpool"
version = "0.0.0"
[[package]]
name = "cuprate-types"
version = "0.0.0"
@ -698,7 +702,7 @@ dependencies = [
]
[[package]]
name = "dandelion_tower"
name = "dandelion-tower"
version = "0.1.0"
dependencies = [
"futures",
@ -725,6 +729,10 @@ dependencies = [
"parking_lot_core",
]
[[package]]
name = "database"
version = "0.0.0"
[[package]]
name = "diff"
version = "0.1.13"

View file

@ -5,7 +5,6 @@ members = [
"consensus",
"consensus/rules",
"cryptonight",
"database",
"helper",
"net/epee-encoding",
"net/fixed-bytes",
@ -14,7 +13,11 @@ members = [
"p2p/cuprate-p2p",
"p2p/dandelion",
"p2p/monero-p2p",
"p2p/async-buffer",
"p2p/address-book",
"storage/cuprate-blockchain",
"storage/cuprate-txpool",
"storage/database",
"pruning",
"test-utils",
"types",

1
benches/README.md Normal file
View file

@ -0,0 +1 @@
# TODO

1
binaries/README.md Normal file
View file

@ -0,0 +1 @@
# TODO

1
books/README.md Normal file
View file

@ -0,0 +1 @@
# TODO

View file

@ -0,0 +1 @@
# TODO

1
books/protocol/README.md Normal file
View file

@ -0,0 +1 @@
# TODO

View file

@ -68,7 +68,7 @@ pub const CUPRATE_DIR: &str = {
/// - [`cuprate_cache_dir()`]
/// - [`cuprate_config_dir()`]
/// - [`cuprate_data_dir()`]
/// - [`cuprate_database_dir()`]
/// - [`cuprate_blockchain_dir()`]
///
/// FIXME: Use `LazyLock` when stabilized.
/// <https://github.com/rust-lang/rust/issues/109736>.
@ -166,18 +166,18 @@ impl_path_oncelock_and_fn! {
data_dir,
"",
/// Cuprate's database directory.
/// Cuprate's blockchain directory.
///
/// This is the PATH used for any Cuprate database files.
/// This is the PATH used for any Cuprate blockchain files.
///
/// | OS | PATH |
/// |---------|--------------------------------------------------------------|
/// | Windows | `C:\Users\Alice\AppData\Roaming\Cuprate\database\` |
/// | macOS | `/Users/Alice/Library/Application Support/Cuprate/database/` |
/// | Linux | `/home/alice/.local/share/cuprate/database/` |
cuprate_database_dir,
/// | OS | PATH |
/// |---------|----------------------------------------------------------------|
/// | Windows | `C:\Users\Alice\AppData\Roaming\Cuprate\blockchain\` |
/// | macOS | `/Users/Alice/Library/Application Support/Cuprate/blockchain/` |
/// | Linux | `/home/alice/.local/share/cuprate/blockchain/` |
cuprate_blockchain_dir,
data_dir,
"database",
"blockchain",
}
//---------------------------------------------------------------------------------------------------- Tests
@ -195,7 +195,7 @@ mod test {
assert!(cuprate_cache_dir().is_absolute());
assert!(cuprate_config_dir().is_absolute());
assert!(cuprate_data_dir().is_absolute());
assert!(cuprate_database_dir().is_absolute());
assert!(cuprate_blockchain_dir().is_absolute());
if cfg!(target_os = "windows") {
let dir = cuprate_cache_dir();
@ -210,9 +210,9 @@ mod test {
println!("cuprate_data_dir: {dir:?}");
assert!(dir.ends_with(r"AppData\Roaming\Cuprate"));
let dir = cuprate_database_dir();
println!("cuprate_database_dir: {dir:?}");
assert!(dir.ends_with(r"AppData\Roaming\Cuprate\database"));
let dir = cuprate_blockchain_dir();
println!("cuprate_blockchain_dir: {dir:?}");
assert!(dir.ends_with(r"AppData\Roaming\Cuprate\blockchain"));
} else if cfg!(target_os = "macos") {
let dir = cuprate_cache_dir();
println!("cuprate_cache_dir: {dir:?}");
@ -226,9 +226,9 @@ mod test {
println!("cuprate_data_dir: {dir:?}");
assert!(dir.ends_with("Library/Application Support/Cuprate"));
let dir = cuprate_database_dir();
println!("cuprate_database_dir: {dir:?}");
assert!(dir.ends_with("Library/Application Support/Cuprate/database"));
let dir = cuprate_blockchain_dir();
println!("cuprate_blockchain_dir: {dir:?}");
assert!(dir.ends_with("Library/Application Support/Cuprate/blockchain"));
} else {
// Assumes Linux.
let dir = cuprate_cache_dir();
@ -243,9 +243,9 @@ mod test {
println!("cuprate_data_dir: {dir:?}");
assert!(dir.ends_with(".local/share/cuprate"));
let dir = cuprate_database_dir();
println!("cuprate_database_dir: {dir:?}");
assert!(dir.ends_with(".local/share/cuprate/database"));
let dir = cuprate_blockchain_dir();
println!("cuprate_blockchain_dir: {dir:?}");
assert!(dir.ends_with(".local/share/cuprate/blockchain"));
}
}
}

View file

@ -409,6 +409,9 @@ impl<Z: NetworkZone> Service<AddressBookRequest<Z>> for AddressBook<Z> {
AddressBookRequest::GetWhitePeers(len) => {
Ok(AddressBookResponse::Peers(self.get_white_peers(len)))
}
AddressBookRequest::IsPeerBanned(addr) => Ok(AddressBookResponse::IsPeerBanned(
self.is_peer_banned(&addr),
)),
};
ready(response)

View file

@ -151,6 +151,7 @@ pub enum BroadcastRequest<N: NetworkZone> {
},
}
#[derive(Clone)]
pub struct BroadcastSvc<N: NetworkZone> {
new_block_watch: watch::Sender<NewBlockInfo>,
tx_broadcast_channel_outbound: broadcast::Sender<BroadcastTxInfo<N>>,

View file

@ -12,14 +12,14 @@
//!
use std::sync::Arc;
use dashmap::{DashMap, DashSet};
use dashmap::DashMap;
use tokio::sync::mpsc;
use tracing::{Instrument, Span};
use monero_p2p::{
client::{Client, InternalPeerID},
handles::ConnectionHandle,
ConnectionDirection, NetworkZone,
NetworkZone,
};
pub(crate) mod disconnect_monitor;
@ -33,11 +33,6 @@ pub use drop_guard_client::ClientPoolDropGuard;
pub struct ClientPool<N: NetworkZone> {
/// The connected [`Client`]s.
clients: DashMap<InternalPeerID<N::Addr>, Client<N>>,
/// A set of outbound clients, as these allow accesses/mutation from different threads,
/// a peer ID in here does not mean the peer is necessarily in `clients` as it could have been removed
/// by another thread. However, if the peer is in both here and `clients` it is definitely
/// an outbound peer.
outbound_clients: DashSet<InternalPeerID<N::Addr>>,
/// A channel to send new peer ids down to monitor for disconnect.
new_connection_tx: mpsc::UnboundedSender<(ConnectionHandle, InternalPeerID<N::Addr>)>,
}
@ -49,7 +44,6 @@ impl<N: NetworkZone> ClientPool<N> {
let pool = Arc::new(ClientPool {
clients: DashMap::new(),
outbound_clients: DashSet::new(),
new_connection_tx: tx,
});
@ -76,10 +70,6 @@ impl<N: NetworkZone> ClientPool<N> {
return;
}
if client.info.direction == ConnectionDirection::OutBound {
self.outbound_clients.insert(id);
}
let res = self.clients.insert(id, client);
assert!(res.is_none());
@ -108,8 +98,6 @@ impl<N: NetworkZone> ClientPool<N> {
///
/// [`None`] is returned if the client did not exist in the pool.
fn remove_client(&self, peer: &InternalPeerID<N::Addr>) -> Option<Client<N>> {
self.outbound_clients.remove(peer);
self.clients.remove(peer).map(|(_, client)| client)
}

View file

@ -24,8 +24,8 @@ pub async fn disconnect_monitor<N: NetworkZone>(
mut new_connection_rx: mpsc::UnboundedReceiver<(ConnectionHandle, InternalPeerID<N::Addr>)>,
client_pool: Arc<ClientPool<N>>,
) {
// We need to hold a weak reference otherwise the client pool and this would hold a reference to each
// other causing the pool to be leaked.
// We need to hold a weak reference otherwise the client pool and this would hold a reference to
// each other causing the pool to be leaked.
let weak_client_pool = Arc::downgrade(&client_pool);
drop(client_pool);

View file

@ -14,7 +14,8 @@ use tracing::{instrument, Instrument, Span};
use monero_p2p::{
client::{Client, DoHandshakeRequest, HandshakeError, InternalPeerID},
ConnectionDirection, NetworkZone,
services::{AddressBookRequest, AddressBookResponse},
AddressBook, ConnectionDirection, NetworkZone,
};
use crate::{
@ -25,9 +26,10 @@ use crate::{
/// The inbound server.
#[instrument(level = "warn", skip_all)]
pub async fn inbound_server<N, HS>(
pub async fn inbound_server<N, HS, A>(
client_pool: Arc<ClientPool<N>>,
mut handshaker: HS,
mut address_book: A,
config: P2PConfig<N>,
) -> Result<(), tower::BoxError>
where
@ -36,15 +38,13 @@ where
+ Send
+ 'static,
HS::Future: Send + 'static,
A: AddressBook<N>,
{
let Some(server_config) = config.server_config else {
tracing::warn!("No inbound server config provided, not listening for inbound connections.");
return Ok(());
};
// TODO: take in the address book and check if incoming peers are banned before adding them to our
// connections.
tracing::info!("Starting inbound connection server");
let listener = N::incoming_connection_listener(server_config, config.p2p_port)
@ -60,6 +60,21 @@ where
continue;
};
if let Some(addr) = &addr {
let AddressBookResponse::IsPeerBanned(banned) = address_book
.ready()
.await?
.call(AddressBookRequest::IsPeerBanned(*addr))
.await?
else {
panic!("Address book returned incorrect response!");
};
if banned {
continue;
}
}
let addr = match addr {
Some(addr) => InternalPeerID::KnownAddr(addr),
None => InternalPeerID::Unknown(rand::random()),

View file

@ -8,11 +8,22 @@
#![allow(dead_code)]
use std::sync::Arc;
use tokio::sync::{mpsc, watch};
use tower::buffer::Buffer;
use futures::FutureExt;
use tokio::{
sync::{mpsc, watch},
task::JoinSet,
};
use tokio_stream::wrappers::WatchStream;
use tower::{buffer::Buffer, util::BoxCloneService, ServiceExt};
use tracing::{instrument, Instrument, Span};
use monero_p2p::{CoreSyncSvc, NetworkZone, PeerRequestHandler};
use monero_p2p::{
client::Connector,
client::InternalPeerID,
services::{AddressBookRequest, AddressBookResponse},
CoreSyncSvc, NetworkZone, PeerRequestHandler,
};
pub mod block_downloader;
mod broadcast;
@ -23,9 +34,10 @@ mod constants;
mod inbound_server;
mod sync_states;
use crate::connection_maintainer::MakeConnectionRequest;
pub use broadcast::{BroadcastRequest, BroadcastSvc};
use client_pool::ClientPoolDropGuard;
pub use config::P2PConfig;
use monero_p2p::client::Connector;
use connection_maintainer::MakeConnectionRequest;
use monero_p2p::services::PeerSyncRequest;
/// Initializes the P2P [`NetworkInterface`] for a specific [`NetworkZone`].
@ -58,7 +70,7 @@ where
config.max_inbound_connections + config.outbound_connections,
);
// Use the default config. Changing the defaults affects tx fluff times, which could effect D++ so for now don't allow changing
// Use the default config. Changing the defaults affects tx fluff times, which could affect D++ so for now don't allow changing
// this.
let (broadcast_svc, outbound_mkr, inbound_mkr) =
broadcast::init_broadcast_channels(broadcast::BroadcastConfig::default());
@ -66,7 +78,6 @@ where
let mut basic_node_data = config.basic_node_data();
if !N::CHECK_NODE_ID {
// TODO: make sure this is the value monerod sets for anon networks.
basic_node_data.peer_id = 1;
}
@ -101,14 +112,28 @@ where
outbound_connector,
);
tokio::spawn(
let mut background_tasks = JoinSet::new();
background_tasks.spawn(
outbound_connection_maintainer
.run()
.instrument(Span::current()),
);
tokio::spawn(
inbound_server::inbound_server(client_pool.clone(), inbound_handshaker, config)
.instrument(Span::current()),
background_tasks.spawn(
inbound_server::inbound_server(
client_pool.clone(),
inbound_handshaker,
address_book.clone(),
config,
)
.map(|res| {
if let Err(e) = res {
tracing::error!("Error in inbound connection listener: {e}")
}
tracing::info!("Inbound connection listener shutdown")
})
.instrument(Span::current()),
);
Ok(NetworkInterface {
@ -117,20 +142,52 @@ where
top_block_watch,
make_connection_tx,
sync_states_svc,
address_book: address_book.boxed_clone(),
_background_tasks: Arc::new(background_tasks),
})
}
/// The interface to Monero's P2P network on a certain [`NetworkZone`].
#[derive(Clone)]
pub struct NetworkInterface<N: NetworkZone> {
/// A pool of free connected peers.
pub pool: Arc<client_pool::ClientPool<N>>,
/// A [`Service`](tower::Service) that allows broadcasting to all connected peers.
broadcast_svc: broadcast::BroadcastSvc<N>,
pool: Arc<client_pool::ClientPool<N>>,
/// A [`Service`] that allows broadcasting to all connected peers.
broadcast_svc: BroadcastSvc<N>,
/// A [`watch`] channel that contains the highest seen cumulative difficulty and other info
/// on that claimed chain.
top_block_watch: watch::Receiver<sync_states::NewSyncInfo>,
/// A channel to request extra connections.
#[allow(dead_code)] // will be used eventually
make_connection_tx: mpsc::Sender<MakeConnectionRequest>,
/// The address book service.
address_book: BoxCloneService<AddressBookRequest<N>, AddressBookResponse<N>, tower::BoxError>,
pub sync_states_svc: Buffer<sync_states::PeerSyncSvc<N>, PeerSyncRequest<N>>,
/// Background tasks that will be aborted when this interface is dropped.
_background_tasks: Arc<JoinSet<()>>,
}
impl<N: NetworkZone> NetworkInterface<N> {
/// Returns a service which allows broadcasting messages to all the connected peers in a specific [`NetworkZone`].
pub fn broadcast_svc(&self) -> BroadcastSvc<N> {
self.broadcast_svc.clone()
}
/// Returns a stream which yields the highest seen sync state from a connected peer.
pub fn top_sync_stream(&self) -> WatchStream<sync_states::NewSyncInfo> {
WatchStream::from_changes(self.top_block_watch.clone())
}
/// Returns the address book service.
pub fn address_book(
&self,
) -> BoxCloneService<AddressBookRequest<N>, AddressBookResponse<N>, tower::BoxError> {
self.address_book.clone()
}
/// Pulls a client from the client pool, returning it in a guard that will return it there when it's
/// dropped.
pub fn borrow_client(&self, peer: &InternalPeerID<N::Addr>) -> Option<ClientPoolDropGuard<N>> {
self.pool.borrow_client(peer)
}
}

View file

@ -25,14 +25,14 @@ use monero_wire::CoreSyncData;
use crate::{client_pool::disconnect_monitor::PeerDisconnectFut, constants::SHORT_BAN};
/// The highest claimed sync info from our connected peers.
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct NewSyncInfo {
/// The peers chain height.
chain_height: u64,
pub chain_height: u64,
/// The peers top block's hash.
top_hash: [u8; 32],
pub top_hash: [u8; 32],
/// The peers cumulative difficulty.
cumulative_difficulty: u128,
pub cumulative_difficulty: u128,
}
/// A service that keeps track of our peers blockchains.

View file

@ -1,5 +1,5 @@
[package]
name = "dandelion_tower"
name = "dandelion-tower"
version = "0.1.0"
edition = "2021"
license = "MIT"

View file

@ -119,10 +119,13 @@ pub enum AddressBookRequest<Z: NetworkZone> {
TakeRandomPeer { height: Option<u64> },
/// Gets the specified number of white peers, or less if we don't have enough.
GetWhitePeers(usize),
/// Checks if the given peer is banned.
IsPeerBanned(Z::Addr),
}
pub enum AddressBookResponse<Z: NetworkZone> {
Ok,
Peer(ZoneSpecificPeerListEntryBase<Z::Addr>),
Peers(Vec<ZoneSpecificPeerListEntryBase<Z::Addr>>),
IsPeerBanned(bool),
}

1
rpc/README.md Normal file
View file

@ -0,0 +1 @@
# TODO

5
storage/README.md Normal file
View file

@ -0,0 +1,5 @@
# storage
TODO: This subdirectory used to be `database/` and is in the middle of being shifted around.
The old `database/` design document is in `cuprate-blockchain/` which will eventually be ported Cuprate's architecture book.

View file

@ -1,12 +1,12 @@
[package]
name = "cuprate-database"
name = "cuprate-blockchain"
version = "0.0.0"
edition = "2021"
description = "Cuprate's database abstraction"
description = "Cuprate's blockchain database"
license = "MIT"
authors = ["hinto-janai"]
repository = "https://github.com/Cuprate/cuprate/tree/main/database"
keywords = ["cuprate", "database"]
repository = "https://github.com/Cuprate/cuprate/tree/main/storage/cuprate-blockchain"
keywords = ["cuprate", "blockchain", "database"]
[features]
default = ["heed", "redb", "service"]
@ -25,10 +25,10 @@ cfg-if = { workspace = true }
# FIXME:
# We only need the `thread` feature if `service` is enabled.
# Figure out how to enable features of an already pulled in dependency conditionally.
cuprate-helper = { path = "../helper", features = ["fs", "thread", "map"] }
cuprate-types = { path = "../types", features = ["service"] }
cuprate-helper = { path = "../../helper", features = ["fs", "thread", "map"] }
cuprate-types = { path = "../../types", features = ["service"] }
curve25519-dalek = { workspace = true }
monero-pruning = { path = "../pruning" }
monero-pruning = { path = "../../pruning" }
monero-serai = { workspace = true, features = ["std"] }
paste = { workspace = true }
page_size = { version = "0.6.0" } # Needed for database resizes, they must be a multiple of the OS page size.
@ -50,10 +50,10 @@ serde = { workspace = true, optional = true }
[dev-dependencies]
bytemuck = { version = "1.14.3", features = ["must_cast", "derive", "min_const_generics", "extern_crate_alloc"] }
cuprate-helper = { path = "../helper", features = ["thread"] }
cuprate-test-utils = { path = "../test-utils" }
cuprate-helper = { path = "../../helper", features = ["thread"] }
cuprate-test-utils = { path = "../../test-utils" }
page_size = { version = "0.6.0" }
tempfile = { version = "3.10.0" }
pretty_assertions = { workspace = true }
hex = { workspace = true }
hex-literal = { workspace = true }
hex-literal = { workspace = true }

View file

@ -1,5 +1,7 @@
# Database
Cuprate's database implementation.
FIXME: This documentation must be updated and moved to the architecture book.
Cuprate's blockchain implementation.
- [1. Documentation](#1-documentation)
- [2. File structure](#2-file-structure)
@ -595,4 +597,4 @@ struct PreRctOutputId { amount: 1, amount_index: 1 }
This means `cuprated`'s database will be slightly larger than `monerod`'s.
The current method `cuprate_database` uses will be "good enough" until usage shows that it must be optimized as multimap tables are tricky to implement across all backends.
The current method `cuprate_database` uses will be "good enough" until usage shows that it must be optimized as multimap tables are tricky to implement across all backends.

View file

@ -27,7 +27,7 @@ use crate::{
//---------------------------------------------------------------------------------------------------- Consts
/// Panic message when there's a table missing.
const PANIC_MSG_MISSING_TABLE: &str =
"cuprate_database::Env should uphold the invariant that all tables are already created";
"cuprate_blockchain::Env should uphold the invariant that all tables are already created";
//---------------------------------------------------------------------------------------------------- ConcreteEnv
/// A strongly typed, concrete database environment, backed by `heed`.

View file

@ -1,4 +1,4 @@
//! Conversion from `heed::Error` -> `cuprate_database`'s errors.
//! Conversion from `heed::Error` -> `cuprate_blockchain`'s errors.
//---------------------------------------------------------------------------------------------------- Use
use crate::constants::DATABASE_CORRUPT_MSG;
@ -85,7 +85,7 @@ impl From<heed::Error> for crate::RuntimeError {
E2::Corrupted | E2::PageNotFound => panic!("{mdb_error:#?}\n{DATABASE_CORRUPT_MSG}"),
// These errors should not occur, and if they do,
// the best thing `cuprate_database` can do for
// the best thing `cuprate_blockchain` can do for
// safety is to panic right here.
E2::Panic
| E2::PageFull

View file

@ -1,4 +1,4 @@
//! `cuprate_database::Storable` <-> `heed` serde trait compatibility layer.
//! `cuprate_blockchain::Storable` <-> `heed` serde trait compatibility layer.
//---------------------------------------------------------------------------------------------------- Use
use std::{borrow::Cow, marker::PhantomData};
@ -9,7 +9,7 @@ use crate::storable::Storable;
//---------------------------------------------------------------------------------------------------- StorableHeed
/// The glue struct that implements `heed`'s (de)serialization
/// traits on any type that implements `cuprate_database::Storable`.
/// traits on any type that implements `cuprate_blockchain::Storable`.
///
/// Never actually gets constructed, just used for trait bound translations.
pub(super) struct StorableHeed<T>(PhantomData<T>)

View file

@ -22,7 +22,7 @@ pub struct ConcreteEnv {
/// (and in current use).
config: Config,
/// A cached, redb version of `cuprate_database::config::SyncMode`.
/// A cached, redb version of `cuprate_blockchain::config::SyncMode`.
/// `redb` needs the sync mode to be set _per_ TX, so we
/// will continue to use this value every `Env::tx_rw`.
durability: redb::Durability,

View file

@ -1,4 +1,4 @@
//! Conversion from `redb`'s errors -> `cuprate_database`'s errors.
//! Conversion from `redb`'s errors -> `cuprate_blockchain`'s errors.
//!
//! HACK: There's a lot of `_ =>` usage here because
//! `redb`'s errors are `#[non_exhaustive]`...

View file

@ -1,4 +1,4 @@
//! `cuprate_database::Storable` <-> `redb` serde trait compatibility layer.
//! `cuprate_blockchain::Storable` <-> `redb` serde trait compatibility layer.
//---------------------------------------------------------------------------------------------------- Use
use std::{cmp::Ordering, fmt::Debug, marker::PhantomData};
@ -9,7 +9,7 @@ use crate::{key::Key, storable::Storable};
//---------------------------------------------------------------------------------------------------- StorableRedb
/// The glue structs that implements `redb`'s (de)serialization
/// traits on any type that implements `cuprate_database::Key`.
/// traits on any type that implements `cuprate_blockchain::Key`.
///
/// Never actually get constructed, just used for trait bound translations.
#[derive(Debug)]

View file

@ -1,4 +1,4 @@
//! Tests for `cuprate_database`'s backends.
//! Tests for `cuprate_blockchain`'s backends.
//!
//! These tests are fully trait-based, meaning there
//! is no reference to `backend/`-specific types.

View file

@ -10,7 +10,7 @@ use std::{
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_helper::fs::cuprate_database_dir;
use cuprate_helper::fs::cuprate_blockchain_dir;
use crate::{
config::{ReaderThreads, SyncMode},

View file

@ -9,7 +9,7 @@ use std::{
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_helper::fs::cuprate_database_dir;
use cuprate_helper::fs::cuprate_blockchain_dir;
use crate::{
config::{ReaderThreads, SyncMode},
@ -55,7 +55,7 @@ impl ConfigBuilder {
///
/// # Default values
/// If [`ConfigBuilder::db_directory`] was not called,
/// the default [`cuprate_database_dir`] will be used.
/// the default [`cuprate_blockchain_dir`] will be used.
///
/// For all other values, [`Default::default`] is used.
pub fn build(self) -> Config {
@ -63,7 +63,7 @@ impl ConfigBuilder {
// in `helper::fs`. No need to do them here.
let db_directory = self
.db_directory
.unwrap_or_else(|| Cow::Borrowed(cuprate_database_dir()));
.unwrap_or_else(|| Cow::Borrowed(cuprate_blockchain_dir()));
// Add the database filename to the directory.
let db_file = {
@ -137,7 +137,7 @@ impl ConfigBuilder {
impl Default for ConfigBuilder {
fn default() -> Self {
Self {
db_directory: Some(Cow::Borrowed(cuprate_database_dir())),
db_directory: Some(Cow::Borrowed(cuprate_blockchain_dir())),
sync_mode: Some(SyncMode::default()),
reader_threads: Some(ReaderThreads::default()),
resize_algorithm: Some(ResizeAlgorithm::default()),
@ -163,7 +163,7 @@ pub struct Config {
/// The directory used to store all database files.
///
/// By default, if no value is provided in the [`Config`]
/// constructor functions, this will be [`cuprate_database_dir`].
/// constructor functions, this will be [`cuprate_blockchain_dir`].
///
// SOMEDAY: we should also support `/etc/cuprated.conf`.
// This could be represented with an `enum DbPath { Default, Custom, Etc, }`
@ -190,20 +190,20 @@ pub struct Config {
impl Config {
/// Create a new [`Config`] with sane default settings.
///
/// The [`Config::db_directory`] will be [`cuprate_database_dir`].
/// The [`Config::db_directory`] will be [`cuprate_blockchain_dir`].
///
/// All other values will be [`Default::default`].
///
/// Same as [`Config::default`].
///
/// ```rust
/// use cuprate_database::{config::*, resize::*, DATABASE_DATA_FILENAME};
/// use cuprate_blockchain::{config::*, resize::*, DATABASE_DATA_FILENAME};
/// use cuprate_helper::fs::*;
///
/// let config = Config::new();
///
/// assert_eq!(config.db_directory(), cuprate_database_dir());
/// assert!(config.db_file().starts_with(cuprate_database_dir()));
/// assert_eq!(config.db_directory(), cuprate_blockchain_dir());
/// assert!(config.db_file().starts_with(cuprate_blockchain_dir()));
/// assert!(config.db_file().ends_with(DATABASE_DATA_FILENAME));
/// assert_eq!(config.sync_mode, SyncMode::default());
/// assert_eq!(config.reader_threads, ReaderThreads::default());
@ -228,7 +228,7 @@ impl Default for Config {
/// Same as [`Config::new`].
///
/// ```rust
/// # use cuprate_database::config::*;
/// # use cuprate_blockchain::config::*;
/// assert_eq!(Config::default(), Config::new());
/// ```
fn default() -> Self {

View file

@ -12,7 +12,7 @@
//!
//! # Example
//! ```rust
//! use cuprate_database::{
//! use cuprate_blockchain::{
//! Env,
//! config::{ConfigBuilder, ReaderThreads, SyncMode}
//! };
@ -31,7 +31,7 @@
//! .build();
//!
//! // Start a database `service` using this configuration.
//! let (reader_handle, _) = cuprate_database::service::init(config.clone())?;
//! let (reader_handle, _) = cuprate_blockchain::service::init(config.clone())?;
//! // It's using the config we provided.
//! assert_eq!(reader_handle.env().config(), &config);
//! # Ok(()) }

View file

@ -49,7 +49,7 @@ pub enum ReaderThreads {
/// as such, it is equal to [`ReaderThreads::OnePerThread`].
///
/// ```rust
/// # use cuprate_database::config::*;
/// # use cuprate_blockchain::config::*;
/// let reader_threads = ReaderThreads::from(0_usize);
/// assert!(matches!(reader_threads, ReaderThreads::OnePerThread));
/// ```
@ -81,7 +81,7 @@ pub enum ReaderThreads {
/// non-zero, but not 1 thread, the minimum value 1 will be returned.
///
/// ```rust
/// # use cuprate_database::config::*;
/// # use cuprate_blockchain::config::*;
/// assert_eq!(ReaderThreads::Percent(0.000000001).as_threads().get(), 1);
/// ```
Percent(f32),
@ -97,7 +97,7 @@ impl ReaderThreads {
///
/// # Example
/// ```rust
/// use cuprate_database::config::ReaderThreads as Rt;
/// use cuprate_blockchain::config::ReaderThreads as Rt;
///
/// let total_threads: std::num::NonZeroUsize =
/// cuprate_helper::thread::threads();

View file

@ -1,4 +1,4 @@
//! General constants used throughout `cuprate-database`.
//! General constants used throughout `cuprate-blockchain`.
//---------------------------------------------------------------------------------------------------- Import
use cfg_if::cfg_if;
@ -8,7 +8,7 @@ use cfg_if::cfg_if;
///
/// Returned by [`crate::ops::property::db_version`].
///
/// This is incremented by 1 when `cuprate_database`'s
/// This is incremented by 1 when `cuprate_blockchain`'s
/// structure/schema/tables change.
///
/// This is akin to `VERSION` in `monerod`:

View file

@ -66,7 +66,7 @@ pub enum InitError {
/// 2. (De)serialization
/// 3. Shutdown errors
///
/// as `cuprate_database` upholds the invariant that:
/// as `cuprate_blockchain` upholds the invariant that:
///
/// 1. All tables exist
/// 2. (De)serialization never fails

View file

@ -23,7 +23,7 @@ pub trait Key: Storable + Sized {
/// not a comparison of the key's value.
///
/// ```rust
/// # use cuprate_database::*;
/// # use cuprate_blockchain::*;
/// assert_eq!(
/// <u64 as Key>::compare([0].as_slice(), [1].as_slice()),
/// std::cmp::Ordering::Less,

View file

@ -1,6 +1,6 @@
//! Cuprate's database abstraction.
//!
//! This documentation is mostly for practical usage of `cuprate_database`.
//! This documentation is mostly for practical usage of `cuprate_blockchain`.
//!
//! For a high-level overview,
//! see [`database/README.md`](https://github.com/Cuprate/cuprate/blob/main/database/README.md).
@ -13,7 +13,7 @@
//!
//! Each layer builds on-top of the previous.
//!
//! As a user of `cuprate_database`, consider using the higher-level [`service`] module,
//! As a user of `cuprate_blockchain`, consider using the higher-level [`service`] module,
//! or at the very least the [`ops`] module instead of interacting with the database traits directly.
//!
//! With that said, many database traits and internals (like [`DatabaseRo::get`]) are exposed.
@ -82,7 +82,7 @@
//! <!-- FIXME: tracing should be behind a feature flag -->
//!
//! # Invariants when not using `service`
//! `cuprate_database` can be used without the `service` feature enabled but
//! `cuprate_blockchain` can be used without the `service` feature enabled but
//! there are some things that must be kept in mind when doing so.
//!
//! Failing to uphold these invariants may cause panics.
@ -92,7 +92,7 @@
//! 1. `LMDB` has [maximum key/value byte size](http://www.lmdb.tech/doc/group__internal.html#gac929399f5d93cef85f874b9e9b1d09e0) which must not be exceeded
//!
//! # Examples
//! The below is an example of using `cuprate_database`'s
//! The below is an example of using `cuprate_blockchain`'s
//! lowest API, i.e. using the database directly.
//!
//! For examples of the higher-level APIs, see:
@ -100,7 +100,7 @@
//! - [`service`]
//!
//! ```rust
//! use cuprate_database::{
//! use cuprate_blockchain::{
//! ConcreteEnv,
//! config::ConfigBuilder,
//! Env, EnvInner,

View file

@ -56,7 +56,7 @@
//!
//! use cuprate_test_utils::data::block_v16_tx0;
//!
//! use cuprate_database::{
//! use cuprate_blockchain::{
//! ConcreteEnv,
//! config::ConfigBuilder,
//! Env, EnvInner,

View file

@ -13,7 +13,7 @@ use crate::{error::RuntimeError, ops::macros::doc_error};
///
/// # Example
/// ```rust
/// # use cuprate_database::{*, tables::*, ops::block::*, ops::tx::*};
/// # use cuprate_blockchain::{*, tables::*, ops::block::*, ops::tx::*};
/// // SOMEDAY
/// ```
#[inline]
@ -29,7 +29,7 @@ pub const fn get_blockchain_pruning_seed() -> Result<PruningSeed, RuntimeError>
///
/// # Example
/// ```rust
/// # use cuprate_database::{*, tables::*, ops::block::*, ops::tx::*};
/// # use cuprate_blockchain::{*, tables::*, ops::block::*, ops::tx::*};
/// // SOMEDAY
/// ```
#[inline]

View file

@ -50,7 +50,7 @@ impl ResizeAlgorithm {
/// Returns [`Self::Monero`].
///
/// ```rust
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// assert!(matches!(ResizeAlgorithm::new(), ResizeAlgorithm::Monero));
/// ```
#[inline]
@ -75,7 +75,7 @@ impl Default for ResizeAlgorithm {
/// Calls [`Self::new`].
///
/// ```rust
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// assert_eq!(ResizeAlgorithm::new(), ResizeAlgorithm::default());
/// ```
#[inline]
@ -113,7 +113,7 @@ pub fn page_size() -> NonZeroUsize {
/// [^2]: `1_073_745_920`
///
/// ```rust
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// // The value this function will increment by
/// // (assuming page multiple of 4096).
/// const N: usize = 1_073_741_824;
@ -129,7 +129,7 @@ pub fn page_size() -> NonZeroUsize {
/// This function will panic if adding onto `current_size_bytes` overflows [`usize::MAX`].
///
/// ```rust,should_panic
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// // Ridiculous large numbers panic.
/// monero(usize::MAX);
/// ```
@ -166,7 +166,7 @@ pub fn monero(current_size_bytes: usize) -> NonZeroUsize {
/// and then round up to nearest OS page size.
///
/// ```rust
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// let page_size: usize = page_size().get();
///
/// // Anything below the page size will round up to the page size.
@ -185,7 +185,7 @@ pub fn monero(current_size_bytes: usize) -> NonZeroUsize {
/// This function will panic if adding onto `current_size_bytes` overflows [`usize::MAX`].
///
/// ```rust,should_panic
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// // Ridiculous large numbers panic.
/// fixed_bytes(1, usize::MAX);
/// ```
@ -221,7 +221,7 @@ pub fn fixed_bytes(current_size_bytes: usize, add_bytes: usize) -> NonZeroUsize
/// (rounded up to the OS page size).
///
/// ```rust
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// let page_size: usize = page_size().get();
///
/// // Anything below the page size will round up to the page size.
@ -247,7 +247,7 @@ pub fn fixed_bytes(current_size_bytes: usize, add_bytes: usize) -> NonZeroUsize
/// is closer to [`usize::MAX`] than the OS page size.
///
/// ```rust,should_panic
/// # use cuprate_database::resize::*;
/// # use cuprate_blockchain::resize::*;
/// // Ridiculous large numbers panic.
/// percent(usize::MAX, 1.001);
/// ```

View file

@ -1,4 +1,4 @@
//! General free functions used (related to `cuprate_database::service`).
//! General free functions used (related to `cuprate_blockchain::service`).
//---------------------------------------------------------------------------------------------------- Import
use std::sync::Arc;

View file

@ -66,7 +66,7 @@
//! use cuprate_types::service::{ReadRequest, WriteRequest, Response};
//! use cuprate_test_utils::data::block_v16_tx0;
//!
//! use cuprate_database::{ConcreteEnv, config::ConfigBuilder, Env};
//! use cuprate_blockchain::{ConcreteEnv, config::ConfigBuilder, Env};
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -77,7 +77,7 @@
//! .build();
//!
//! // Initialize the database thread-pool.
//! let (mut read_handle, mut write_handle) = cuprate_database::service::init(config)?;
//! let (mut read_handle, mut write_handle) = cuprate_blockchain::service::init(config)?;
//!
//! // Prepare a request to write block.
//! let mut block = block_v16_tx0().clone();

View file

@ -29,7 +29,7 @@ use bytes::Bytes;
/// See [`StorableVec`] & [`StorableBytes`] for storing slices of `T: Storable`.
///
/// ```rust
/// # use cuprate_database::*;
/// # use cuprate_blockchain::*;
/// # use std::borrow::*;
/// let number: u64 = 0;
///
@ -77,7 +77,7 @@ pub trait Storable: Debug {
///
/// # Examples
/// ```rust
/// # use cuprate_database::*;
/// # use cuprate_blockchain::*;
/// assert_eq!(<()>::BYTE_LENGTH, Some(0));
/// assert_eq!(u8::BYTE_LENGTH, Some(1));
/// assert_eq!(u16::BYTE_LENGTH, Some(2));
@ -99,7 +99,7 @@ pub trait Storable: Debug {
///
/// # Blanket implementation
/// The blanket implementation that covers all types used
/// by `cuprate_database` will simply bitwise copy `bytes`
/// by `cuprate_blockchain` will simply bitwise copy `bytes`
/// into `Self`.
///
/// The bytes do not have be correctly aligned.
@ -136,7 +136,7 @@ where
///
/// # Example
/// ```rust
/// # use cuprate_database::*;
/// # use cuprate_blockchain::*;
/// //---------------------------------------------------- u8
/// let vec: StorableVec<u8> = StorableVec(vec![0,1]);
///
@ -202,7 +202,7 @@ impl<T> Borrow<[T]> for StorableVec<T> {
/// A [`Storable`] version of [`Bytes`].
///
/// ```rust
/// # use cuprate_database::*;
/// # use cuprate_blockchain::*;
/// # use bytes::Bytes;
/// let bytes: StorableBytes = StorableBytes(Bytes::from_static(&[0,1]));
///

View file

@ -1,7 +1,7 @@
//! Database tables.
//!
//! # Table marker structs
//! This module contains all the table definitions used by `cuprate_database`.
//! This module contains all the table definitions used by `cuprate_blockchain`.
//!
//! The zero-sized structs here represents the table type;
//! they all are essentially marker types that implement [`Table`].
@ -331,7 +331,7 @@ macro_rules! tables {
///
/// ## Table Name
/// ```rust
/// # use cuprate_database::{*,tables::*};
/// # use cuprate_blockchain::{*,tables::*};
#[doc = concat!(
"assert_eq!(",
stringify!([<$table:camel>]),

View file

@ -1,4 +1,4 @@
//! Utilities for `cuprate_database` testing.
//! Utilities for `cuprate_blockchain` testing.
//!
//! These types/fn's are only:
//! - enabled on #[cfg(test)]

View file

@ -105,7 +105,7 @@ pub type UnlockTime = u64;
///
/// ```rust
/// # use std::borrow::*;
/// # use cuprate_database::{*, types::*};
/// # use cuprate_blockchain::{*, types::*};
/// // Assert Storable is correct.
/// let a = PreRctOutputId {
/// amount: 1,
@ -118,7 +118,7 @@ pub type UnlockTime = u64;
///
/// # Size & Alignment
/// ```rust
/// # use cuprate_database::types::*;
/// # use cuprate_blockchain::types::*;
/// # use std::mem::*;
/// assert_eq!(size_of::<PreRctOutputId>(), 16);
/// assert_eq!(align_of::<PreRctOutputId>(), 8);
@ -148,7 +148,7 @@ pub struct PreRctOutputId {
///
/// ```rust
/// # use std::borrow::*;
/// # use cuprate_database::{*, types::*};
/// # use cuprate_blockchain::{*, types::*};
/// // Assert Storable is correct.
/// let a = BlockInfo {
/// timestamp: 1,
@ -167,7 +167,7 @@ pub struct PreRctOutputId {
///
/// # Size & Alignment
/// ```rust
/// # use cuprate_database::types::*;
/// # use cuprate_blockchain::types::*;
/// # use std::mem::*;
/// assert_eq!(size_of::<BlockInfo>(), 88);
/// assert_eq!(align_of::<BlockInfo>(), 8);
@ -207,7 +207,7 @@ bitflags::bitflags! {
///
/// ```rust
/// # use std::borrow::*;
/// # use cuprate_database::{*, types::*};
/// # use cuprate_blockchain::{*, types::*};
/// // Assert Storable is correct.
/// let a = OutputFlags::NON_ZERO_UNLOCK_TIME;
/// let b = Storable::as_bytes(&a);
@ -217,7 +217,7 @@ bitflags::bitflags! {
///
/// # Size & Alignment
/// ```rust
/// # use cuprate_database::types::*;
/// # use cuprate_blockchain::types::*;
/// # use std::mem::*;
/// assert_eq!(size_of::<OutputFlags>(), 4);
/// assert_eq!(align_of::<OutputFlags>(), 4);
@ -236,7 +236,7 @@ bitflags::bitflags! {
///
/// ```rust
/// # use std::borrow::*;
/// # use cuprate_database::{*, types::*};
/// # use cuprate_blockchain::{*, types::*};
/// // Assert Storable is correct.
/// let a = Output {
/// key: [1; 32],
@ -251,7 +251,7 @@ bitflags::bitflags! {
///
/// # Size & Alignment
/// ```rust
/// # use cuprate_database::types::*;
/// # use cuprate_blockchain::types::*;
/// # use std::mem::*;
/// assert_eq!(size_of::<Output>(), 48);
/// assert_eq!(align_of::<Output>(), 8);
@ -277,7 +277,7 @@ pub struct Output {
///
/// ```rust
/// # use std::borrow::*;
/// # use cuprate_database::{*, types::*};
/// # use cuprate_blockchain::{*, types::*};
/// // Assert Storable is correct.
/// let a = RctOutput {
/// key: [1; 32],
@ -293,7 +293,7 @@ pub struct Output {
///
/// # Size & Alignment
/// ```rust
/// # use cuprate_database::types::*;
/// # use cuprate_blockchain::types::*;
/// # use std::mem::*;
/// assert_eq!(size_of::<RctOutput>(), 80);
/// assert_eq!(align_of::<RctOutput>(), 8);

View file

@ -0,0 +1,15 @@
[package]
name = "cuprate-txpool"
version = "0.0.0"
edition = "2021"
description = "Cuprate's transaction pool database"
license = "MIT"
authors = ["hinto-janai"]
repository = "https://github.com/Cuprate/cuprate/tree/main/storage/cuprate-txpool"
keywords = ["cuprate", "txpool", "transaction", "pool", "database"]
[features]
[dependencies]
[dev-dependencies]

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,15 @@
[package]
name = "database"
version = "0.0.0"
edition = "2021"
description = "Cuprate's database abstraction"
license = "MIT"
authors = ["hinto-janai"]
repository = "https://github.com/Cuprate/cuprate/tree/main/storage/database"
keywords = ["cuprate", "database"]
[features]
[dependencies]
[dev-dependencies]

View file

@ -0,0 +1 @@

1
zmq/README.md Normal file
View file

@ -0,0 +1 @@
# TODO