diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 0dc88fe..9aa4451 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Cache - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: | ~/.cargo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cee92a..8c2271d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: components: clippy - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: target key: ${{ matrix.os }} diff --git a/.github/workflows/deny.yml b/.github/workflows/deny.yml index 3b596ec..1301bb1 100644 --- a/.github/workflows/deny.yml +++ b/.github/workflows/deny.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Cache - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: | ~/.cargo diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33b8780..b05088a 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 656ba45..32b7592 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,9 +118,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -317,9 +317,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstyle", "clap_lex", @@ -589,11 +589,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", @@ -808,6 +812,17 @@ 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" @@ -1179,12 +1194,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http", "http-body", "pin-project-lite", @@ -1192,9 +1207,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "hyper" @@ -1277,13 +1292,133 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +dependencies = [ + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1389,6 +1524,12 @@ 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" @@ -1620,9 +1761,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1995,9 +2136,9 @@ dependencies = [ [[package]] name = "redb" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7508e692a49b6b2290b56540384ccae9b1fb4d77065640b165835b56ffe3bb" +checksum = "a6dd20d3cdeb9c7d2366a0b16b93b35b75aec15309fbeb7ce477138c9f68c8c0" dependencies = [ "libc", ] @@ -2044,9 +2185,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ring" @@ -2093,9 +2234,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "once_cell", "ring", @@ -2345,6 +2486,12 @@ 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" @@ -2403,6 +2550,17 @@ 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" @@ -2461,20 +2619,15 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[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" @@ -2684,27 +2837,12 @@ 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" @@ -2713,15 +2851,27 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +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" + [[package]] name = "valuable" version = "0.1.0" @@ -3044,6 +3194,18 @@ 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" @@ -3059,6 +3221,30 @@ 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" @@ -3079,6 +3265,27 @@ 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" @@ -3098,3 +3305,25 @@ 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 c38a128..d517973 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 adab19d..dbfd9aa 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 de9d082..32fce11 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/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 cd7860d..a97040a 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::*; diff --git a/deny.toml b/deny.toml index ab4ce53..85e7da2 100644 --- a/deny.toml +++ b/deny.toml @@ -120,6 +120,7 @@ allow = [ # Font licenses. "Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html + "Unicode-3.0", # https://spdx.org/licenses/Unicode-3.0.html # "LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 # "OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html ] diff --git a/rpc/json-rpc/src/id.rs b/rpc/json-rpc/src/id.rs index 542d9ee..7ee710d 100644 --- a/rpc/json-rpc/src/id.rs +++ b/rpc/json-rpc/src/id.rs @@ -182,20 +182,20 @@ macro_rules! impl_u { $( impl From<$u> for Id { fn from(u: $u) -> Self { - Self::Num(u as u64) + Self::Num(u64::from(u)) } } impl From<&$u> for Id { fn from(u: &$u) -> Self { - Self::Num(*u as u64) + Self::Num(u64::from(*u)) } } impl From> for Id { fn from(u: Option<$u>) -> Self { match u { - Some(u) => Self::Num(u as u64), + Some(u) => Self::Num(u64::from(u)), None => Self::Null, } } diff --git a/rpc/json-rpc/src/tests.rs b/rpc/json-rpc/src/tests.rs index 5eeb778..ff8f049 100644 --- a/rpc/json-rpc/src/tests.rs +++ b/rpc/json-rpc/src/tests.rs @@ -1,6 +1,5 @@ //! Tests and utilities. -#![cfg(test)] #![allow( clippy::unreadable_literal, clippy::manual_string_new,