diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33b87809..b05088a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,45 +1,115 @@ -# Contributing to Cuprate +## Contributing to Cuprate +Thank you for wanting to help out! -## Introduction +Cuprate is in the stage where things are likely to change quickly, so it's recommended +you ask questions in our public [Matrix room](https://matrix.to/#/#cuprate:monero.social). -Thank you for wanting to help out! Cuprate is in the stage where things are likely to change quickly, so it's recommend -you join our [Matrix room](https://matrix.to/#/#cuprate:monero.social). +- [1. Submitting a pull request](#1-submitting-a-pull-request) + - [1.1 Rust toolchain](#11-rust-toolchain) + - [1.2 Draft PR](#12-draft-pr) + - [1.3 Passing CI](#13-passing-ci) + - [1.4 Ready for review](#14-ready-for-review) +- [2. Crate names](#2-crate-names) +- [3. Coding guidelines](#3-coding-guidelines) +- [4. Keeping track of issues and PRs](#4-keeping-track-of-issues-and-prs) +- [5. Documentation](#5-documentation) +- [6. Books](#6-books) + - [6.1 Architecture book](#61-architecture-book) + - [6.2 Protocol book](#62-protocol-book) + - [6.3 User book](#63-user-book) -## Making a PR +## 1. Submitting a pull request +Once you have found something you would like to work on by: +- Looking at the [open issues](https://github.com/Cuprate/cuprate/issues) +- Looking at issues with the [`A-help-wanted`](https://github.com/Cuprate/cuprate/issues?q=is%3Aissue+is%3Aopen+label%3AE-help-wanted) label +- or joining Cuprate's [Matrix room](https://matrix.to/#/#cuprate:monero.social) and asking -Once you have found something you would like to work on by either looking at the open issues or joining Cuprate's [Matrix room](https://matrix.to/#/#cuprate:monero.social) -and asking it's recommended to make your interest on working on that thing known so people don't duplicate work. +it is recommended to make your interest on working on that thing known so people don't duplicate work. -When you are at a stage where you would like feedback you can open a draft PR, keep in mind that feedback may take time especially if the change is large. -Once your PR is at the stage where you feel it's ready to go, open it for review. +Before starting, consider reading/using Cuprate's: +- [`Documentation`](#5-documentation) (practical `cargo` docs) +- [`Books`](#6-books) (Cuprate's architecture and protocol) -## Passing CI -The first 3 steps to CI are formatting, typo, and documentation checking. +These may answer some questions you have, or may confirm an issue you would like to fix. -Check if your changes are formatted, typo-free, and documented correctly by running: -- `cargo fmt --all --check` -- `typos` -- `RUSTDOCFLAGS='-D warnings' cargo doc --workspace --all-features` +_Note: Cuprate is currently a work-in-progress; documentation will be changing/unfinished._ + +### 1.1 Rust toolchain +Cuprate is written in [Rust](https://rust-lang.org). + +If you are editing code, you will need Rust's toolchain and package manager, +[`cargo`](https://doc.rust-lang.org/cargo/index.html), to develop and submit PRs effectively. + +Get started with Rust here: . + +### 1.2 Draft PR +Consider opening a draft PR until you have passed all CI. + +This is also the stage where you can ask for feedback from others. Keep in mind that feedback may take time especially if the change is large. + +### 1.3 Passing CI +Each commit pushed in a PR will trigger our [lovely, yet pedantic CI](https://github.com/Cuprate/cuprate/blob/main/.github/workflows/ci.yml). + +It currently: +- Checks code formatting +- Checks documentation +- Looks for typos +- Runs [`clippy`](https://github.com/rust-lang/rust-clippy) (and fails on warnings) +- Runs all tests +- Builds all targets +- Automatically add approriate [labels](#4-keeping-track-of-issues-and-prs) to your PR + +Before pushing your code, please run the following at the root of the repository: + +| Command | Does what | +|-------------------|-----------| +| `cargo fmt --all` | Formats code +| `typos -w` | Fixes typos `typos` can be installed with `cargo` from: https://github.com/crate-ci/typos. -After that, ensure all lints, tests, and builds are successful by running: +After that, ensure all other CI passes by running: -- `cargo clippy --workspace --all-features --all-targets -- -D warnings` -- `cargo fmt --all` -- `cargo test --all-features --workspace` -- `cargo build --all-features --all-targets --workspace` +| Command | Does what | +|------------------------------------------------------------------------|-----------| +| `RUSTDOCFLAGS='-D warnings' cargo doc --workspace --all-features` | Checks documentation is OK +| `cargo clippy --workspace --all-features --all-targets -- -D warnings` | Checks clippy lints are satisfied +| `cargo test --all-features --workspace` | Runs all tests +| `cargo build --all-features --all-targets --workspace` | Builds all code -## Coding guidelines +**Note: in order for some tests to work, you will need to place a [`monerod`](https://www.getmonero.org/downloads/) binary at the root of the repository.** +### 1.4 Ready for review +Once your PR has passed all CI and is ready to go, open it for review. Others will leave their thoughts and may ask for changes to be made. + +Finally, if everything looks good, we will merge your code! Thank you for contributing! + +## 2. Crate names +All of Cuprate's crates (libraries) are prefixed with `cuprate-`. All directories containing crates however, are not. + +For example: + +| Crate Directory | Crate Name | +|--------------------|--------------------| +| `storage/database` | `cuprate-database` | +| `net/levin` | `cuprate-levin` | +| `net/wire` | `cuprate-wire` | + +## 3. Coding guidelines +This is a list of rules that are not mandated by any automation, but contributors generally follow. + +You should keep these in mind when submitting code: + +- Separate and sort imports as core, std, third-party, Cuprate crates, current crate +- Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines) - `// Comment like this.` and not `//like this` - Use `TODO` instead of `FIXME` - Avoid `unsafe` -- Sort imports as core, std, third-party, Cuprate crates, current crate. -- Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines) -- Break the above rules when it makes sense -## Keeping track of issues and PRs +And the most important rule: +- Break any and all of the above rules when it makes sense + +## 4. Keeping track of issues and PRs The Cuprate GitHub repository has a lot of issues and PRs to keep track of. Cuprate makes use of generic labels and labels grouped by a prefixes to help with this. Some labels will be [automatically added/removed](https://github.com/Cuprate/cuprate/tree/main/.github/labeler.yml) if certain file paths have been changed in a PR. @@ -65,7 +135,45 @@ This section is primarily targeted at maintainers. Most contributors aren't able [O-]: https://github.com/Cuprate/cuprate/labels?q=O [P-]: https://github.com/Cuprate/cuprate/labels?q=P -## Books +## 5. Documentation +Cuprate's crates (libraries) have inline documentation. + +These can be built and viewed using the `cargo` tool. For example, to build and view a specific crate's documentation, run the following command at the repository's root: +```bash +cargo doc --open --package $CRATE +``` +`$CRATE` can be any package listed in the [root `Cargo.toml`](https://github.com/Cuprate/cuprate/tree/main/Cargo.toml)'s workspace members list, for example, `cuprate-blockchain`. + +You can also build all documentation at once: +```bash +cargo doc +``` +and view by using a web-browser to open the `index.html` file within the build directory: `target/doc/$CRATE/index.html`, for example, `target/doc/cuprate_blockchain/index.html`. + +## 6. Books Cuprate has various documentation books whose source files live in [`books/`](https://github.com/Cuprate/cuprate/tree/main/books). Please contribute if you found a mistake! The files are mostly [markdown](https://wikipedia.org/wiki/Markdown) files and can be easily edited. See the `books/` directory for more information. + +These books are also good resources to understand how Cuprate and Monero work. + +### 6.1 Architecture book +This book documents Cuprate's architecture and implementation. + +- +- +- + +### 6.2 Protocol book +This book documents the Monero protocol. + +- +- +- + +### 6.3 User book +This book is a user-guide for using Cuprate. + +- +- +- diff --git a/Cargo.lock b/Cargo.lock index 154e697c..1363cd05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,11 +565,15 @@ version = "0.1.0" dependencies = [ "clap", "cuprate-blockchain", + "cuprate-consensus", + "cuprate-consensus-rules", "cuprate-types", "hex", "hex-literal", + "monero-serai", "rayon", "sha3", + "thiserror", "tokio", "tokio-test", "tower", @@ -594,7 +598,6 @@ dependencies = [ name = "cuprate-p2p" version = "0.1.0" dependencies = [ - "async-buffer", "bytes", "cuprate-helper", "cuprate-test-utils", @@ -609,7 +612,6 @@ dependencies = [ "monero-serai", "monero-wire", "pin-project", - "proptest", "rand", "rand_distr", "rayon", @@ -619,6 +621,7 @@ dependencies = [ "tokio-util", "tower", "tracing", + "tracing-subscriber", ] [[package]] @@ -662,9 +665,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", @@ -672,7 +675,6 @@ dependencies = [ "digest", "fiat-crypto", "group", - "platforms", "rand_core", "rustc_version", "subtle", @@ -789,17 +791,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "doxygen-rs" version = "0.4.2" @@ -1184,9 +1175,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" @@ -1268,134 +1259,14 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "idna" -version = "1.0.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", - "smallvec", - "utf8_iter", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1501,12 +1372,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "lmdb-master-sys" version = "0.2.1" @@ -1546,9 +1411,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "merlin" @@ -1564,9 +1429,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -1706,6 +1571,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1753,6 +1628,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "page_size" version = "0.6.0" @@ -1901,12 +1782,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2106,9 +1981,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -2379,6 +2254,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2438,12 +2322,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "std-shims" version = "0.1.1" @@ -2502,17 +2380,6 @@ dependencies = [ "crossbeam-queue", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "tap" version = "1.0.1" @@ -2571,15 +2438,20 @@ dependencies = [ ] [[package]] -name = "tinystr" -version = "0.7.6" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "displaydoc", - "zerovec", + "tinyvec_macros", ] +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.38.0" @@ -2743,6 +2615,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", ] [[package]] @@ -2751,7 +2635,12 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", "tracing-core", + "tracing-log", ] [[package]] @@ -2772,12 +2661,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -2786,9 +2690,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2796,16 +2700,10 @@ dependencies = [ ] [[package]] -name = "utf16_iter" -version = "1.0.5" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" @@ -3123,18 +3021,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "wyz" version = "0.5.1" @@ -3150,30 +3036,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.7.34" @@ -3194,27 +3056,6 @@ dependencies = [ "syn 2.0.66", ] -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "synstructure", -] - [[package]] name = "zeroize" version = "1.8.1" @@ -3234,25 +3075,3 @@ dependencies = [ "quote", "syn 2.0.66", ] - -[[package]] -name = "zerovec" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] diff --git a/README.md b/README.md index c38a1288..d5179730 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,58 @@ -![Cuprate](misc/logo/wordmark/CuprateWordmark.svg) +
+ +An alternative Monero node implementation. ----- - -> An up and coming Rust Monero node. +_(work-in-progress)_ [![Matrix](https://img.shields.io/badge/Matrix-Cuprate-white?logo=matrix&labelColor=grey&logoColor=white)](https://matrix.to/#/#cuprate:monero.social) [![CI](https://github.com/Cuprate/cuprate/actions/workflows/ci.yml/badge.svg)](https://github.com/Cuprate/cuprate/actions/workflows/ci.yml) -Cuprate will be an alternative Monero node written from the ground up in Rust. It -will be able to independently validate Monero consensus rules providing a layer of -security and redundancy for the Monero network. +
-Cuprate will help to protect the network from implementation bugs that could -cause a variety of issues, plus because it's written in a memory safe language Cuprate -will be less likely to suffer from memory safety issues compared to monerod. +## Contents +- [About](#about) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [Security](#security) +- [License](#license) + + + +## About +Cuprate is an effort to create an alternative [Monero](https://getmonero.org) node implementation in [Rust](http://rust-lang.org). + +It will be able to independently validate Monero consensus rules, providing a layer of security and redundancy for the Monero network. + + + +## Documentation +_Cuprate is currently a work-in-progress; documentation will be changing/unfinished._ + +Cuprate maintains various documentation books: + +| Book | Description | +|-----------------------------------------------------------------|------------------------------------------------------------| +| [Cuprate's architecture book](https://architecture.cuprate.org) | Documents Cuprate's internal architecture & implementation | +| [Cuprate's protocol book](https://monero-book.cuprate.org) | Documents the Monero protocol | +| [Cuprate's user book](https://user.cuprate.org) | Practical user-guide for using `cuprated` | + +For crate (library) documentation, see the `Documentation` section in [`CONTRIBUTING.md`](CONTRIBUTING.md). + +## Contributing +See [`CONTRIBUTING.md`](CONTRIBUTING.md). + +## Security +Cuprate has a responsible vulnerability disclosure policy, see [`SECURITY.md`](SECURITY.md). + +## License +The `binaries/` directory is licensed under AGPL-3.0, everything else is licensed under MIT. + +See [`LICENSE`](LICENSE) for more details. diff --git a/SECURITY.md b/SECURITY.md index adab19da..dbfd9aa3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,11 +1,15 @@ # Security Policy -## Supported Versions +## Reporting a vulnerability +If you have discovered a vulnerability within Cuprate, please do not open a GitHub issue or announce it publicly. -We only support the latest version available as it follows monero specifications and older client is therefore prohibited by the network. +Please contact us directly by email using our PGP keys in the [`gpg_keys/`](misc/gpg_keys/) directory or via an encrypted Matrix channel. -## Reporting a Vulnerability +Thanks for being quiet. -If you ever discover a vulnerability, please do not open a github issue. Contact us by mail directly using our pgp keys under the gpg_keys folder in the repository. We're also available on Matrix. -You can also alert us using the *Report a Vulnerability* feature of Github. Thanks for being quiet. We'll always disclose the vulnerability after patching it and encourage everyone to fetch the -security update. +## Contact +Please disclose vulnerabilities to one of the trusted maintainers below: + +| Trusted maintainer | PGP key | Email address | Matrix ID | +|--------------------|---------|---------------|-----------| +| [Boog900](https://github.com/Boog900) | [`boog900.asc`](misc/gpg_keys/boog900.asc) | `boog900@tutanota.com` | `@boog900:monero.social` \ No newline at end of file diff --git a/consensus/fast-sync/Cargo.toml b/consensus/fast-sync/Cargo.toml index bfd973cb..3a0754a3 100644 --- a/consensus/fast-sync/Cargo.toml +++ b/consensus/fast-sync/Cargo.toml @@ -11,11 +11,15 @@ path = "src/create.rs" [dependencies] clap = { workspace = true, features = ["derive", "std"] } cuprate-blockchain = { path = "../../storage/cuprate-blockchain" } +cuprate-consensus = { path = ".." } +cuprate-consensus-rules = { path = "../rules" } cuprate-types = { path = "../../types" } hex.workspace = true hex-literal.workspace = true +monero-serai.workspace = true rayon.workspace = true sha3 = "0.10.8" +thiserror.workspace = true tokio = { workspace = true, features = ["full"] } tower.workspace = true diff --git a/consensus/fast-sync/src/fast_sync.rs b/consensus/fast-sync/src/fast_sync.rs index cd7860de..a97040a6 100644 --- a/consensus/fast-sync/src/fast_sync.rs +++ b/consensus/fast-sync/src/fast_sync.rs @@ -1,5 +1,6 @@ use std::{ cmp, + collections::HashMap, future::Future, pin::Pin, task::{Context, Poll}, @@ -7,9 +8,21 @@ use std::{ #[allow(unused_imports)] use hex_literal::hex; -use tower::Service; +use monero_serai::{ + block::Block, + transaction::{Input, Transaction}, +}; +use tower::{Service, ServiceExt}; + +use cuprate_consensus::{ + context::{BlockChainContextRequest, BlockChainContextResponse}, + transactions::TransactionVerificationData, +}; +use cuprate_consensus_rules::{miner_tx::MinerTxError, ConsensusError}; +use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation}; use crate::{hash_of_hashes, BlockId, HashOfHashes}; + #[cfg(not(test))] static HASHES_OF_HASHES: &[HashOfHashes] = &include!("./data/hashes_of_hashes"); @@ -31,13 +44,6 @@ fn max_height() -> u64 { (HASHES_OF_HASHES.len() * BATCH_SIZE) as u64 } -pub enum FastSyncRequest { - ValidateHashes { - start_height: u64, - block_ids: Vec, - }, -} - #[derive(Debug, PartialEq)] pub struct ValidBlockId(BlockId); @@ -45,31 +51,79 @@ fn valid_block_ids(block_ids: &[BlockId]) -> Vec { block_ids.iter().map(|b| ValidBlockId(*b)).collect() } +#[allow(clippy::large_enum_variant)] +pub enum FastSyncRequest { + ValidateHashes { + start_height: u64, + block_ids: Vec, + }, + ValidateBlock { + block: Block, + txs: HashMap<[u8; 32], Transaction>, + token: ValidBlockId, + }, +} + +#[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq)] pub enum FastSyncResponse { ValidateHashes { validated_hashes: Vec, unknown_hashes: Vec, }, + ValidateBlock(VerifiedBlockInformation), } -#[derive(Debug, PartialEq)] +#[derive(thiserror::Error, Debug, PartialEq)] pub enum FastSyncError { - InvalidStartHeight, // start_height not a multiple of BATCH_SIZE - Mismatch, // hash does not match - NothingToDo, // no complete batch to check - OutOfRange, // start_height too high + #[error("Block does not match its expected hash")] + BlockHashMismatch, + + #[error("Start height must be a multiple of the batch size")] + InvalidStartHeight, + + #[error("Hash of hashes mismatch")] + Mismatch, + + #[error("Given range too small for fast sync (less than one batch)")] + NothingToDo, + + #[error("Start height too high for fast sync")] + OutOfRange, + + #[error("Block does not have the expected height entry")] + BlockHeightMismatch, + + #[error("Block does not contain the expected transaction list")] + TxsIncludedWithBlockIncorrect, + + #[error(transparent)] + Consensus(#[from] ConsensusError), + + #[error(transparent)] + MinerTx(#[from] MinerTxError), + + #[error("Database error: {0}")] + DbErr(String), +} + +impl From for FastSyncError { + fn from(error: tower::BoxError) -> Self { + Self::DbErr(error.to_string()) + } } -#[allow(dead_code)] pub struct FastSyncService { context_svc: C, } impl FastSyncService where - C: Service - + Clone + C: Service< + BlockChainContextRequest, + Response = BlockChainContextResponse, + Error = tower::BoxError, + > + Clone + Send + 'static, { @@ -81,8 +135,11 @@ where impl Service for FastSyncService where - C: Service - + Clone + C: Service< + BlockChainContextRequest, + Response = BlockChainContextResponse, + Error = tower::BoxError, + > + Clone + Send + 'static, C::Future: Send + 'static, @@ -97,12 +154,17 @@ where } fn call(&mut self, req: FastSyncRequest) -> Self::Future { + let context_svc = self.context_svc.clone(); + Box::pin(async move { match req { FastSyncRequest::ValidateHashes { start_height, block_ids, } => validate_hashes(start_height, &block_ids).await, + FastSyncRequest::ValidateBlock { block, txs, token } => { + validate_block(context_svc, block, txs, token).await + } } }) } @@ -149,6 +211,91 @@ async fn validate_hashes( }) } +async fn validate_block( + mut context_svc: C, + block: Block, + mut txs: HashMap<[u8; 32], Transaction>, + token: ValidBlockId, +) -> Result +where + C: Service< + BlockChainContextRequest, + Response = BlockChainContextResponse, + Error = tower::BoxError, + > + Send + + 'static, + C::Future: Send + 'static, +{ + let BlockChainContextResponse::Context(checked_context) = context_svc + .ready() + .await? + .call(BlockChainContextRequest::GetContext) + .await? + else { + panic!("Context service returned wrong response!"); + }; + + let block_chain_ctx = checked_context.unchecked_blockchain_context().clone(); + + let block_hash = block.hash(); + if block_hash != token.0 { + return Err(FastSyncError::BlockHashMismatch); + } + + let block_blob = block.serialize(); + + let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else { + return Err(FastSyncError::MinerTx(MinerTxError::InputNotOfTypeGen)); + }; + if *height != block_chain_ctx.chain_height { + return Err(FastSyncError::BlockHeightMismatch); + } + + let mut verified_txs = Vec::with_capacity(txs.len()); + for tx in &block.txs { + let tx = txs + .remove(tx) + .ok_or(FastSyncError::TxsIncludedWithBlockIncorrect)?; + + let data = TransactionVerificationData::new(tx)?; + verified_txs.push(VerifiedTransactionInformation { + tx_blob: data.tx_blob, + tx_weight: data.tx_weight, + fee: data.fee, + tx_hash: data.tx_hash, + tx: data.tx, + }); + } + + let total_fees = verified_txs.iter().map(|tx| tx.fee).sum::(); + let total_outputs = block + .miner_tx + .prefix + .outputs + .iter() + .map(|output| output.amount.unwrap_or(0)) + .sum::(); + + let generated_coins = total_outputs - total_fees; + + let weight = + block.miner_tx.weight() + verified_txs.iter().map(|tx| tx.tx_weight).sum::(); + + Ok(FastSyncResponse::ValidateBlock(VerifiedBlockInformation { + block_blob, + txs: verified_txs, + block_hash, + pow_hash: [0u8; 32], + height: *height, + generated_coins, + weight, + long_term_weight: block_chain_ctx.next_block_long_term_weight(weight), + cumulative_difficulty: block_chain_ctx.cumulative_difficulty + + block_chain_ctx.next_difficulty, + block, + })) +} + #[cfg(test)] mod tests { use super::*;