From d6a3a4314682312f1a6f1406a98ecfacb13981e3 Mon Sep 17 00:00:00 2001 From: Andrew Westberg Date: Tue, 10 Sep 2024 20:58:26 +0000 Subject: [PATCH] feat[amaru-consensus]: Add basic block validation --- .github/workflows/validate.yml | 78 +++ Cargo.lock | 589 +++++++++++++----- Cargo.toml | 1 - amaru-common/Cargo.toml | 8 +- amaru-common/src/ledger/mod.rs | 16 +- amaru-common/src/lib.rs | 3 - amaru-common/src/nonce/mod.rs | 6 +- amaru-common/src/pool/mod.rs | 17 +- amaru-common/src/tests.rs | 11 - amaru-common/src/validator/mod.rs | 2 +- amaru-consensus/Cargo.toml | 28 +- amaru-consensus/src/block/mod.rs | 413 +++++++++++- amaru-consensus/src/lib.rs | 7 - amaru-consensus/src/tests.rs | 7 - .../data/mainnet_blockheader_10817298.cbor | Bin 0 -> 859 bytes amaru-crypto/Cargo.toml | 13 - amaru-crypto/src/lib.rs | 14 - amaru-node/Cargo.toml | 7 +- amaru-node/src/main.rs | 11 +- version.toml | 2 - 20 files changed, 967 insertions(+), 266 deletions(-) create mode 100644 .github/workflows/validate.yml delete mode 100644 amaru-common/src/tests.rs delete mode 100644 amaru-consensus/src/tests.rs create mode 100644 amaru-consensus/tests/data/mainnet_blockheader_10817298.cbor delete mode 100644 amaru-crypto/Cargo.toml delete mode 100644 amaru-crypto/src/lib.rs delete mode 100644 version.toml diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..69d17ee --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,78 @@ +# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md + +on: + push: { } + +name: Validate + +jobs: + check: + name: Check + strategy: + fail-fast: false + matrix: + os: [ windows-latest, ubuntu-latest, macOS-latest ] + rust: [ stable ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + + - name: Run cargo check + run: cargo check + + test: + name: Test Suite + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Run cargo test + run: cargo test + + test-windows: + name: Test Suite Windows + runs-on: windows-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Run cargo test + run: cargo test + + lints: + name: Lints + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: rustfmt, clippy + + - name: Run cargo fmt + run: cargo fmt --all -- --check + + - name: Run cargo clippy + run: cargo clippy -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index aa6afc0..a7e43e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -30,8 +42,10 @@ dependencies = [ name = "amaru-common" version = "0.0.1" dependencies = [ - "pallas-crypto", - "pallas-traverse", + "hex", + "mockall", + "pallas-crypto 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-traverse 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", ] @@ -40,80 +54,38 @@ name = "amaru-consensus" version = "0.0.1" dependencies = [ "amaru-common", - "log", - "pallas-codec", - "pallas-primitives", - "pallas-traverse", + "ctor", + "hex", + "malachite", + "malachite-base", + "mockall", + "pallas-codec 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-crypto 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-math", + "pallas-primitives 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-traverse 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "tracing", + "tracing-subscriber", ] -[[package]] -name = "amaru-crypto" -version = "0.0.1" - [[package]] name = "amaru-node" version = "0.0.1" dependencies = [ "amaru-common", "amaru-consensus", - "env_logger 0.11.5", - "log", - "pretty_env_logger", "thiserror", "tokio", "tracing", "tracing-subscriber", ] -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "backtrace" version = "0.3.73" @@ -141,6 +113,21 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.7.1" @@ -163,10 +150,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "colorchoice" -version = "1.0.2" +name = "cpufeatures" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] [[package]] name = "crc" @@ -189,6 +179,56 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "git+https://github.com/iquerejeta/curve25519-dalek?branch=ietf03_vrf_compat_ell2#70a36f41cfc3fbb7357ec3062201b911787decba" +dependencies = [ + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "either" version = "1.13.0" @@ -196,39 +236,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "env_filter" -version = "0.1.2" +name = "fragile" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] -name = "env_logger" -version = "0.10.2" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", + "typenum", + "version_check", ] [[package]] -name = "env_logger" -version = "0.11.5" +name = "getrandom" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -244,16 +275,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -262,28 +296,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "is-terminal" -version = "0.4.13" +name = "itertools" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ - "hermit-abi 0.4.0", - "libc", - "windows-sys 0.52.0", + "either", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.13.0" @@ -311,12 +331,63 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "malachite" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5616515d632967cd329b6f6db96be9a03ea0b3a49cdbc45b0016803dad8a77b7" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46059721011b0458b7bd6d9179be5d0b60294281c23320c207adceaecc54d13b" +dependencies = [ + "hashbrown", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-nz" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1503b27e825cabd1c3d0ff1e95a39fb2ec9eab6fd3da6cfa41aec7091d273e78" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a475503a70a3679dbe3b9b230a23622516742528ba614a7b2490f180ea9cb514" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + [[package]] name = "memchr" version = "2.7.4" @@ -359,10 +430,36 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", - "wasi", - "windows-sys 0.52.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "mockall" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] @@ -390,6 +487,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "overload" version = "0.1.1" @@ -398,25 +501,51 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pallas-addresses" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38fac39e0da3b0fc4c859635c72e97584f01f3a0f4f1508b0851c02d6d52f15" +checksum = "84460293bb3323066e9ce608702750c14f02bc36d41c469e44b3eef5ec0fdbf6" dependencies = [ "base58", "bech32", "crc", "cryptoxide", "hex", - "pallas-codec", - "pallas-crypto", + "pallas-codec 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-crypto 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror", +] + +[[package]] +name = "pallas-addresses" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418#4281b281c72b9215acced43bbe4056a218bd3418" +dependencies = [ + "base58", + "bech32", + "crc", + "cryptoxide", + "hex", + "pallas-codec 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-crypto 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", "thiserror", ] [[package]] name = "pallas-codec" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8a4b87dbc8bcb8aeb865f7cca5e1eb29744330e23b307169fc30537648b264" +checksum = "747279d1bc612986035619a3eaded8f9f4ceae29668aa7a5feae83681a0e93f4" +dependencies = [ + "hex", + "minicbor", + "serde", + "thiserror", +] + +[[package]] +name = "pallas-codec" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418#4281b281c72b9215acced43bbe4056a218bd3418" dependencies = [ "hex", "minicbor", @@ -426,46 +555,103 @@ dependencies = [ [[package]] name = "pallas-crypto" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b98c3f204299d47d9b581ab425043789caff1f491c078ee3d3f109d6556f725" +checksum = "1b6f8b08e32c7dbb50302222701ae15ef9ac1a7cc39225ce29c253f6ddab2aa7" +dependencies = [ + "cryptoxide", + "hex", + "pallas-codec 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.4", + "serde", + "thiserror", +] + +[[package]] +name = "pallas-crypto" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418#4281b281c72b9215acced43bbe4056a218bd3418" dependencies = [ "cryptoxide", "hex", - "pallas-codec", - "rand_core", + "pallas-codec 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "rand_core 0.6.4", "serde", "thiserror", + "vrf_dalek", +] + +[[package]] +name = "pallas-math" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418#4281b281c72b9215acced43bbe4056a218bd3418" +dependencies = [ + "malachite", + "malachite-base", + "once_cell", + "regex", + "thiserror", ] [[package]] name = "pallas-primitives" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f64835dd9cbdd75a38961a190b983f02746c872340daf1a921eada8c525a4b6" +checksum = "24929d461308626183d5bf15290e6315f4cc67fa38a1a66469425919683cceb2" dependencies = [ "base58", "bech32", "hex", "log", - "pallas-codec", - "pallas-crypto", + "pallas-codec 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-crypto 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_json", +] + +[[package]] +name = "pallas-primitives" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418#4281b281c72b9215acced43bbe4056a218bd3418" +dependencies = [ + "base58", + "bech32", + "hex", + "log", + "pallas-codec 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-crypto 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", "serde", "serde_json", ] [[package]] name = "pallas-traverse" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad516b05ba7d838ee84f9998d7b2b4ff7acc178cb052bcfd5fea9edc2ef6023f" +checksum = "73ca94c2278a160c226d6f5bb1756ea5f355421158aaa697445f59f09477a6a4" dependencies = [ "hex", - "itertools", - "pallas-addresses", - "pallas-codec", - "pallas-crypto", - "pallas-primitives", + "itertools 0.13.0", + "pallas-addresses 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-codec 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-crypto 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-primitives 0.30.2 (registry+https://github.com/rust-lang/crates.io-index)", + "paste", + "serde", + "thiserror", +] + +[[package]] +name = "pallas-traverse" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418#4281b281c72b9215acced43bbe4056a218bd3418" +dependencies = [ + "hex", + "itertools 0.13.0", + "pallas-addresses 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-codec 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-crypto 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", + "pallas-primitives 0.30.2 (git+https://github.com/txpipe/pallas?rev=4281b281c72b9215acced43bbe4056a218bd3418)", "paste", "serde", "thiserror", @@ -484,13 +670,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] -name = "pretty_env_logger" -version = "0.5.0" +name = "predicates" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ - "env_logger 0.10.2", - "log", + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", ] [[package]] @@ -511,6 +713,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -590,6 +801,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -618,9 +842,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -644,13 +874,10 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.4.1" +name = "termtree" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" @@ -695,7 +922,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -766,6 +993,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -773,16 +1006,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] -name = "valuable" +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vrf_dalek" version = "0.1.0" +source = "git+https://github.com/input-output-hk/vrf?rev=a3185620b72e6a9647285941b961021186f16693#a3185620b72e6a9647285941b961021186f16693" +dependencies = [ + "curve25519-dalek 3.2.0", + "curve25519-dalek 3.2.1", + "rand_core 0.5.1", + "sha2", + "thiserror", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" @@ -806,15 +1057,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -830,15 +1072,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -902,3 +1135,29 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" diff --git a/Cargo.toml b/Cargo.toml index 7ec09f9..23c516c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,5 @@ resolver = "2" members = [ "amaru-common", "amaru-consensus", - "amaru-crypto", "amaru-node", ] diff --git a/amaru-common/Cargo.toml b/amaru-common/Cargo.toml index 24acc1a..def20fe 100644 --- a/amaru-common/Cargo.toml +++ b/amaru-common/Cargo.toml @@ -12,6 +12,8 @@ authors = [ ] [dependencies] -pallas-crypto = "0.30.1" -pallas-traverse = "0.30.1" -thiserror = "1" \ No newline at end of file +hex = "0.4" +mockall = "0.13" +pallas-crypto = "0.30.2" +pallas-traverse = "0.30.2" +thiserror = "1" diff --git a/amaru-common/src/ledger/mod.rs b/amaru-common/src/ledger/mod.rs index 6c2d745..8191fd5 100644 --- a/amaru-common/src/ledger/mod.rs +++ b/amaru-common/src/ledger/mod.rs @@ -1,4 +1,5 @@ use crate::pool::PoolId; +use mockall::automock; use pallas_traverse::update::RationalNumber; use thiserror::Error; @@ -16,6 +17,17 @@ pub type Sigma = RationalNumber; /// Performs a lookup of a pool_id to its sigma value. This usually represents a different set of /// sigma snapshot data depending on whether we need to look up the pool_id in the current epoch /// or in the future. -pub trait PoolIdToSigma { +#[automock] +pub trait PoolInfo: Send + Sync { fn sigma(&self, pool_id: &PoolId) -> Result; -} \ No newline at end of file + fn vrf_vkey_hash(&self, pool_id: &PoolId) -> Result<[u8; 32], Error>; +} + +/// Performs a lookup of the first slot number in the epoch given an absolute slot number. +/// This requires knowledge of the genesis values and the shelley transition epoch value when +/// we left the byron era. +#[automock] +pub trait SlotCalculator: Send + Sync { + fn first_slot_in_epoch(&self, absolute_slot: u64) -> u64; + fn epoch_for_slot(&self, absolute_slot: u64) -> u64; +} diff --git a/amaru-common/src/lib.rs b/amaru-common/src/lib.rs index 23271cd..8011481 100644 --- a/amaru-common/src/lib.rs +++ b/amaru-common/src/lib.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - pub mod ledger; pub mod nonce; pub mod pool; diff --git a/amaru-common/src/nonce/mod.rs b/amaru-common/src/nonce/mod.rs index 8cbcbc0..d732b4b 100644 --- a/amaru-common/src/nonce/mod.rs +++ b/amaru-common/src/nonce/mod.rs @@ -6,6 +6,6 @@ use pallas_crypto::hash::Hash; // Sometimes called seedL in the haskell code // pub const UC_NONCE: Hash<32> = Hash::new([ - 0x12, 0xdd, 0x0a, 0x6a, 0x7d, 0x0e, 0x22, 0x2a, 0x97, 0x92, 0x6d, 0xa0, 0x3a, 0xdb, 0x5a, 0x77, 0x68, 0xd3, 0x1c, - 0xc7, 0xc5, 0xc2, 0xbd, 0x68, 0x28, 0xe1, 0x4a, 0x7d, 0x25, 0xfa, 0x3a, 0x60, -]); \ No newline at end of file + 0x12, 0xdd, 0x0a, 0x6a, 0x7d, 0x0e, 0x22, 0x2a, 0x97, 0x92, 0x6d, 0xa0, 0x3a, 0xdb, 0x5a, 0x77, + 0x68, 0xd3, 0x1c, 0xc7, 0xc5, 0xc2, 0xbd, 0x68, 0x28, 0xe1, 0x4a, 0x7d, 0x25, 0xfa, 0x3a, 0x60, +]); diff --git a/amaru-common/src/pool/mod.rs b/amaru-common/src/pool/mod.rs index bf670d6..20ed2b2 100644 --- a/amaru-common/src/pool/mod.rs +++ b/amaru-common/src/pool/mod.rs @@ -1,10 +1,23 @@ use pallas_crypto::hash::{Hash, Hasher}; -use pallas_traverse::update::RationalNumber; pub type PoolId = Hash<28>; -/// The node's vkey is hashed with blake2b224 to create the pool id +/// The node's cold vkey is hashed with blake2b224 to create the pool id pub fn issuer_vkey_to_pool_id(issuer_vkey: &[u8]) -> PoolId { Hasher::<224>::hash(issuer_vkey) } +#[cfg(test)] +mod tests { + #[test] + fn issuer_vkey_to_pool_id() { + let issuer_vkey = + hex::decode("cad3c900ca6baee9e65bf61073d900bfbca458eeca6d0b9f9931f5b1017a8cd6") + .unwrap(); + let pool_id = crate::pool::issuer_vkey_to_pool_id(&issuer_vkey); + assert_eq!( + pool_id.to_string(), + "00beef0a9be2f6d897ed24a613cf547bb20cd282a04edfc53d477114" + ); + } +} diff --git a/amaru-common/src/tests.rs b/amaru-common/src/tests.rs deleted file mode 100644 index b725c47..0000000 --- a/amaru-common/src/tests.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::pool::{issuer_vkey_to_pool_id, PoolId}; - -#[test] -fn test_issuer_vkey_to_pool_id() { - // cad3c900ca6baee9e65bf61073d900bfbca458eeca6d0b9f9931f5b1017a8cd6 - let issuer_vkey = [0xca, 0xd3, 0xc9, 0x00, 0xca, 0x6b, 0xae, 0xe9, 0xe6, 0x5b, 0xf6, 0x10, 0x73, 0xd9, 0x00, 0xbf, 0xbc, 0xa4, 0x58, 0xee, 0xca, 0x6d, 0x0b, 0x9f, 0x99, 0x31, 0xf5, 0xb1, 0x01, 0x7a, 0x8c, 0xd6]; - let pool_id = issuer_vkey_to_pool_id(&issuer_vkey); - // 00beef0a9be2f6d897ed24a613cf547bb20cd282a04edfc53d477114 - let expected_pool_id = PoolId::new([0x00, 0xbe, 0xef, 0x0a, 0x9b, 0xe2, 0xf6, 0xd8, 0x97, 0xed, 0x24, 0xa6, 0x13, 0xcf, 0x54, 0x7b, 0xb2, 0x0c, 0xd2, 0x82, 0xa0, 0x4e, 0xdf, 0xc5, 0x3d, 0x47, 0x71, 0x14]); - assert_eq!(pool_id, expected_pool_id); -} diff --git a/amaru-common/src/validator/mod.rs b/amaru-common/src/validator/mod.rs index 896d6d9..f2e35bc 100644 --- a/amaru-common/src/validator/mod.rs +++ b/amaru-common/src/validator/mod.rs @@ -4,4 +4,4 @@ /// bug in the code. pub trait Validator: Send + Sync { fn validate(&self) -> bool; -} \ No newline at end of file +} diff --git a/amaru-consensus/Cargo.toml b/amaru-consensus/Cargo.toml index d17078a..a30340b 100644 --- a/amaru-consensus/Cargo.toml +++ b/amaru-consensus/Cargo.toml @@ -13,7 +13,27 @@ authors = [ [dependencies] amaru-common = { path = "../amaru-common" } -pallas-codec = "0.30.1" -pallas-primitives = "0.30.1" -pallas-traverse = "0.30.1" -log = "0.4.22" \ No newline at end of file +ctor = "0.2" +hex = "0.4" +malachite = "0.4" +malachite-base = "0.4" +mockall = "0.13" +#FIXME: remove pointing to pallas forks before merging +pallas-codec = { git = "https://github.com/txpipe/pallas", rev = "4281b281c72b9215acced43bbe4056a218bd3418" } +pallas-crypto = { git = "https://github.com/txpipe/pallas", rev = "4281b281c72b9215acced43bbe4056a218bd3418" } +pallas-math = { git = "https://github.com/txpipe/pallas", rev = "4281b281c72b9215acced43bbe4056a218bd3418" } +pallas-primitives = { git = "https://github.com/txpipe/pallas", rev = "4281b281c72b9215acced43bbe4056a218bd3418" } +pallas-traverse = { git = "https://github.com/txpipe/pallas", rev = "4281b281c72b9215acced43bbe4056a218bd3418" } +#pallas-codec = "0.30.2" +#pallas-crypto = "0.30.2" +#pallas-math = "0.30.2" +#pallas-primitives = "0.30.2" +#pallas-traverse = "0.30.2" + +# logging +tracing = "0.1" +tracing-subscriber = "0.3" + +#gmp has problems with thread safety and cannot easily implement Send + Sync without changes to the library +#[target.'cfg(not(target_os = "windows"))'.dependencies] +#pallas-math = { git = "https://github.com/txpipe/pallas", rev = "4281b281c72b9215acced43bbe4056a218bd3418", features = ["gmp"] } diff --git a/amaru-consensus/src/block/mod.rs b/amaru-consensus/src/block/mod.rs index 454c4c7..8d04737 100644 --- a/amaru-consensus/src/block/mod.rs +++ b/amaru-consensus/src/block/mod.rs @@ -1,17 +1,57 @@ -use amaru_common::ledger::{PoolIdToSigma, Sigma}; -use amaru_common::pool::issuer_vkey_to_pool_id; +use amaru_common::ledger::{PoolInfo, SlotCalculator}; +use amaru_common::pool::{issuer_vkey_to_pool_id, PoolId}; use amaru_common::validator::Validator; -use log::warn; +use malachite::platform::Limb; +use malachite::{Integer, Natural, Rational}; +use malachite_base::num::arithmetic::traits::{Ceiling, Pow}; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_crypto::vrf::{ + VrfProof, VrfPublicKey, VRF_PROOF_HASH_SIZE, VRF_PROOF_SIZE, VRF_PUBLIC_KEY_SIZE, +}; +use pallas_math::math::{ExpOrdering, FixedDecimal, FixedPrecision}; use pallas_traverse::MultiEraHeader; +use std::ops::Deref; +use std::sync::LazyLock; +use tracing::{error, span, trace, warn}; + +// The certified natural max value represents 2^256 in babbage and beyond +static CERTIFIED_NATURAL_MAX: LazyLock = LazyLock::new(|| { + FixedDecimal::from_str( + "1157920892373161954235709850086879078532699846656405640394575840079131296399360000000000000000000000000000000000", + 34, + ) + .expect("Infallible") +}); struct BlockValidator<'b> { - multi_era_header: MultiEraHeader<'b>, - pool_id_to_sigma: &'b dyn PoolIdToSigma, + multi_era_header: &'b MultiEraHeader<'b>, + pool_info: &'b dyn PoolInfo, + slot_calculator: &'b dyn SlotCalculator, + epoch_nonce: &'b Hash<32>, + decentralization: &'b Rational, + // c is the ln(1-active_slots_coeff). Usually ln(1-0.05) + c: &'b FixedDecimal, } impl<'b> BlockValidator<'b> { - fn new(multi_era_header: MultiEraHeader<'b>, pool_id_to_sigma: &'b dyn PoolIdToSigma) -> Self { - Self { multi_era_header, pool_id_to_sigma } + // TODO: This function is currently only used in tests. Remove warning suppression in the future + #[allow(unused)] + fn new( + multi_era_header: &'b MultiEraHeader<'b>, + pool_info: &'b dyn PoolInfo, + slot_calculator: &'b dyn SlotCalculator, + epoch_nonce: &'b Hash<32>, + decentralization: &'b Rational, + c: &'b FixedDecimal, + ) -> Self { + Self { + multi_era_header, + pool_info, + slot_calculator, + epoch_nonce, + decentralization, + c, + } } fn validate_epoch_boundary(&self) -> bool { @@ -25,26 +65,240 @@ impl<'b> BlockValidator<'b> { } fn validate_shelley_compatible(&self) -> bool { - let Some(issuer_vkey) = self.multi_era_header.issuer_vkey(); - let pool_id = issuer_vkey_to_pool_id(issuer_vkey); - let sigma = match self.pool_id_to_sigma.sigma(&pool_id) { - Ok(sigma) => sigma, + // we ignore shelley compatible headers and assume they are valid + true + + // TODO: In the future, we will need to validate this data if we build an archive amaru node. + // let issuer_vkey = self.multi_era_header.issuer_vkey().expect("Infallible"); + // let pool_id: PoolId = issuer_vkey_to_pool_id(issuer_vkey); + // let sigma: Sigma = match self.pool_id_to_sigma.sigma(&pool_id) { + // Ok(sigma) => sigma, + // Err(error) => { + // warn!("{:?} - {:?}", error, pool_id); + // return false; + // } + // }; + // let absolute_slot = self.multi_era_header.slot(); + // let leader_vrf_output = self.multi_era_header.leader_vrf_output().expect("Infallible"); + // let vrf_vkey = self.multi_era_header.vrf_vkey().expect("Infallible"); + // if self.is_overlay_slot(absolute_slot) { + // // This is an overlay block produced by the BFT node + // todo!("Overlay block validation"); + // } else { + // // This is a block produced by the stake pool + // // We must calculate the seed ourselves to verify the VRF output + // todo!("Stake pool block validation"); + // } + } + + fn validate_babbage_compatible(&self) -> bool { + let span = span!(tracing::Level::TRACE, "validate_babbage_compatible"); + let _enter = span.enter(); + + // Grab all the values we need to validate the block + let issuer_vkey = self.multi_era_header.issuer_vkey().expect("Infallible"); + let pool_id: PoolId = issuer_vkey_to_pool_id(issuer_vkey); + let vrf_vkey: &[u8; VRF_PUBLIC_KEY_SIZE] = self + .multi_era_header + .vrf_vkey() + .expect("Infallible") + .try_into() + .expect("Infallible"); + if !self.ledger_matches_block_vrf_key_hash(&pool_id, vrf_vkey) { + // Fail fast if the vrf key hash in the block does not match the ledger + return false; + } + let sigma: FixedDecimal = match self.pool_info.sigma(&pool_id) { + Ok(sigma) => { + FixedDecimal::from(sigma.numerator) / FixedDecimal::from(sigma.denominator) + } Err(error) => { warn!("{:?} - {:?}", error, pool_id); return false; } }; - let slot = self.multi_era_header.slot(); - let Ok(leader_vrf_output) = self.multi_era_header.leader_vrf_output(); - let Ok(vrf_vkey) = self.multi_era_header.vrf_vkey(); + let absolute_slot = self.multi_era_header.slot(); + let leader_vrf_output = self + .multi_era_header + .leader_vrf_output() + .expect("Infallible"); + let (block_vrf_proof_hash, block_vrf_proof, kes_signature) = { + let babbage_header = self.multi_era_header.as_babbage().expect("Infallible"); + ( + &babbage_header.header_body.vrf_result.0, + &babbage_header.header_body.vrf_result.1, + &babbage_header.body_signature, + ) + }; + let block_vrf_proof_hash: [u8; VRF_PROOF_HASH_SIZE] = block_vrf_proof_hash + .as_slice() + .try_into() + .expect("Infallible"); + let block_vrf_proof: [u8; VRF_PROOF_SIZE] = + block_vrf_proof.as_slice().try_into().expect("Infallible"); - true + trace!("pool_id: {}", pool_id); + trace!("block vrf_vkey: {}", hex::encode(vrf_vkey)); + trace!("sigma: {}", sigma); + trace!("absolute_slot: {}", absolute_slot); + trace!("leader_vrf_output: {}", hex::encode(&leader_vrf_output)); + trace!( + "block_vrf_proof_hash: {}", + hex::encode(block_vrf_proof_hash.as_slice()) + ); + trace!( + "block_vrf_proof: {}", + hex::encode(block_vrf_proof.as_slice()) + ); + trace!("kes_signature: {}", hex::encode(kes_signature.as_slice())); + + // Calculate the VRF input seed so we can verify the VRF output against it. + let vrf_input_seed = self.mk_vrf_input(absolute_slot, self.epoch_nonce.as_ref()); + trace!("vrf_input_seed: {}", vrf_input_seed); + + // Verify the VRF proof + let vrf_proof = VrfProof::from(&block_vrf_proof); + let vrf_vkey = VrfPublicKey::from(vrf_vkey); + match vrf_proof.verify(&vrf_vkey, vrf_input_seed.as_ref()) { + Ok(proof_hash) => { + if proof_hash.as_slice() != block_vrf_proof_hash.as_slice() { + error!("VRF proof hash mismatch"); + false + } else { + // The proof was valid. Make sure that our leader_vrf_output matches what was in the block + trace!("certified_proof_hash: {}", hex::encode(proof_hash)); + let mut hasher = Hasher::<256>::new(); + hasher.input(&[0x4C_u8]); /* "L" */ + hasher.input(proof_hash.as_slice()); + let calculated_leader_vrf_output = hasher.finalize(); + if calculated_leader_vrf_output.as_slice() != leader_vrf_output.as_slice() { + error!( + "Leader VRF output hash mismatch. was: {}, expected: {}", + hex::encode(calculated_leader_vrf_output), + hex::encode(&leader_vrf_output) + ); + false + } else { + // The leader VRF output hash matches what was in the block + // Now we need to check if the pool had enough sigma stake to produce this block + if self.pool_meets_delegation_threshold( + &sigma, + absolute_slot, + leader_vrf_output, + ) { + // TODO: Validate the KES signature + true + } else { + false + } + } + } + } + Err(error) => { + error!("Could not verify block vrf: {}", error); + false + } + } } - fn validate_babbage_compatible(&self) -> bool { - let cbor = self.multi_era_header.cbor(); + /// Verify that the pool meets the delegation threshold + fn pool_meets_delegation_threshold( + &self, + sigma: &FixedDecimal, + absolute_slot: u64, + leader_vrf_output: Vec, + ) -> bool { + let limbs = leader_vrf_output + .chunks(size_of::()) + .map(|chunk| Limb::from_be_bytes(chunk.try_into().expect("Infallible"))) + .collect(); + let certified_leader_vrf = FixedDecimal::from( + Natural::from_owned_limbs_desc(limbs) * Natural::from(10u64).pow(34), + ); + let denominator = CERTIFIED_NATURAL_MAX.deref() - &certified_leader_vrf; + let recip_q = CERTIFIED_NATURAL_MAX.deref() / &denominator; + let x = -(sigma * self.c); + + trace!("certified_leader_vrf: {}", certified_leader_vrf); + trace!("denominator: {}", denominator); + trace!("recip_q: {}", recip_q); + trace!("c: {}", self.c); + trace!("x: {}", x); + + let ordering = x.exp_cmp(1000, 3, &recip_q); + match ordering.estimation { + ExpOrdering::LT => { + trace!( + "Slot: {} - IS Leader: {} < {}", + absolute_slot, + recip_q, + ordering.approx + ); + true + } + _ => { + trace!( + "Slot: {} - NOT Leader: {} >= {}", + absolute_slot, + recip_q, + ordering.approx + ); + false + } + } + } + + /// Validate that the VRF key hash in the block matches the VRF key hash in the ledger + fn ledger_matches_block_vrf_key_hash(&self, pool_id: &PoolId, vrf_vkey: &[u8]) -> bool { + let vrf_vkey_hash = Hasher::<256>::hash(vrf_vkey); + trace!("block vrf_vkey_hash: {}", hex::encode(vrf_vkey_hash)); + let ledger_vrf_vkey_hash = match self.pool_info.vrf_vkey_hash(pool_id) { + Ok(ledger_vrf_vkey_hash) => ledger_vrf_vkey_hash, + Err(error) => { + warn!("{:?} - {:?}", error, pool_id); + return false; + } + }; + if vrf_vkey_hash.as_slice() != ledger_vrf_vkey_hash { + error!( + "VRF vkey hash in block ({}) does not match registered ledger vrf vkey hash ({})", + hex::encode(vrf_vkey_hash), + hex::encode(ledger_vrf_vkey_hash) + ); + return false; + } true } + + fn mk_vrf_input(&self, absolute_slot: u64, eta0: &[u8]) -> Hash<32> { + trace!("mk_vrf_input() absolute_slot {}", absolute_slot); + let mut hasher = Hasher::<256>::new(); + hasher.input(&absolute_slot.to_be_bytes()); + hasher.input(eta0); + hasher.finalize() + } + + #[allow(unused)] + fn is_overlay_slot(&self, absolute_slot: u64) -> bool { + let diff_slot: Rational = + Rational::from(absolute_slot - self.slot_calculator.first_slot_in_epoch(absolute_slot)); + let diff_slot_inc: Rational = &diff_slot + Rational::from(1); + let left: Integer = (self.decentralization * &diff_slot).ceiling(); + let right: Integer = (self.decentralization * &diff_slot_inc).ceiling(); + + trace!("is_overlay_slot: d: {:?}", self.decentralization); + trace!("is_overlay_slot: diff_slot: {:?}", &diff_slot); + trace!("is_overlay_slot: diff_slot_inc: {:?}", &diff_slot_inc); + trace!("is_overlay_slot: left: {:?}", &left); + trace!("is_overlay_slot: right: {:?}", &right); + trace!( + "is_overlay_slot: result: {:?} - {:?}", + absolute_slot, + left < right + ); + + left < right + } } impl Validator for BlockValidator<'_> { @@ -56,4 +310,127 @@ impl Validator for BlockValidator<'_> { MultiEraHeader::BabbageCompatible(_) => self.validate_babbage_compatible(), } } -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use crate::block::BlockValidator; + use amaru_common::ledger::{MockPoolInfo, MockSlotCalculator, Sigma}; + use amaru_common::pool::PoolId; + use amaru_common::validator::Validator; + use ctor::ctor; + use malachite::Rational; + use mockall::predicate::eq; + use pallas_crypto::hash::Hash; + use pallas_math::math::{FixedDecimal, FixedPrecision}; + use pallas_traverse::MultiEraHeader; + use std::fs::File; + use std::io::Read; + + #[ctor] + fn init() { + // set rust log level to TRACE + // std::env::set_var("RUST_LOG", "amaru_consensus=trace"); + + // initialize tracing crate + tracing_subscriber::fmt::init(); + } + + #[test] + fn test_validate_conway_block() { + let mut pool_info = MockPoolInfo::new(); + // bcsh pool in epoch 508 made block 10817298 + let bcsh_pool_id = PoolId::from( + hex::decode("00beef0a9be2f6d897ed24a613cf547bb20cd282a04edfc53d477114") + .unwrap() + .as_slice(), + ); + pool_info + .expect_sigma() + .with(eq(bcsh_pool_id)) + .returning(|_| { + Ok(Sigma { + numerator: 25626202470912, + denominator: 22586623335121436, + }) + }); + pool_info + .expect_vrf_vkey_hash() + .with(eq(bcsh_pool_id)) + .returning(|_| { + Ok(<[u8; 32]>::try_from( + hex::decode("c0d1f9b040d2f6fd7fc8775d24753d6db4b697429f11404a6178a0a4a005867b") + .unwrap(), + ) + .unwrap()) + }); + + let mut slot_calculator = MockSlotCalculator::new(); + slot_calculator + .expect_first_slot_in_epoch() + .with(eq(134402628)) + .returning(|_| 134092800); + + let epoch_nonce: Hash<32> = + "c7937fc47fecbe687891b3decd71e904d1e129598aa3852481d295eea3ea3ada" + .parse() + .unwrap(); + let decentralization: Rational = Rational::from(0u64); + let active_slots_coeff: FixedDecimal = + FixedDecimal::from(5u64) / FixedDecimal::from(100u64); + + let c = (FixedDecimal::from(1u64) - active_slots_coeff).ln(); + + let conway_block_tag: u8 = 6; + let mut cbor = Vec::new(); + File::open("tests/data/mainnet_blockheader_10817298.cbor") + .unwrap() + .read_to_end(&mut cbor) + .unwrap(); + + let multi_era_header = MultiEraHeader::decode(conway_block_tag, None, &cbor).unwrap(); + assert_eq!(multi_era_header.slot(), 134402628u64); + + let block_validator = BlockValidator::new( + &multi_era_header, + &pool_info, + &slot_calculator, + &epoch_nonce, + &decentralization, + &c, + ); + assert_eq!(block_validator.validate(), true); + + // lower our stake and expect that we are not a leader + let mut pool_info = MockPoolInfo::new(); + pool_info + .expect_sigma() + .with(eq(bcsh_pool_id)) + .returning(|_| { + Ok(Sigma { + numerator: 6026202470912, + denominator: 22586623335121436, + }) + }); + pool_info + .expect_vrf_vkey_hash() + .with(eq(bcsh_pool_id)) + .returning(|_| { + Ok(<[u8; 32]>::try_from( + hex::decode("c0d1f9b040d2f6fd7fc8775d24753d6db4b697429f11404a6178a0a4a005867b") + .unwrap(), + ) + .unwrap()) + }); + + let block_validator = BlockValidator::new( + &multi_era_header, + &pool_info, + &slot_calculator, + &epoch_nonce, + &decentralization, + &c, + ); + assert_eq!(block_validator.validate(), false); + } +} diff --git a/amaru-consensus/src/lib.rs b/amaru-consensus/src/lib.rs index 11ec6da..fc9210d 100644 --- a/amaru-consensus/src/lib.rs +++ b/amaru-consensus/src/lib.rs @@ -1,8 +1 @@ mod block; - -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests; diff --git a/amaru-consensus/src/tests.rs b/amaru-consensus/src/tests.rs deleted file mode 100644 index 53ed08a..0000000 --- a/amaru-consensus/src/tests.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[test] -fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); -} diff --git a/amaru-consensus/tests/data/mainnet_blockheader_10817298.cbor b/amaru-consensus/tests/data/mainnet_blockheader_10817298.cbor new file mode 100644 index 0000000000000000000000000000000000000000..e4fdec7d623d71fde4600f91dd7b42d2933eabb2 GIT binary patch literal 859 zcmV-h1El5AX0Wnw+SZ=e4z z-60N*(bQOr`uMLU3+<`X$AVZu*+jU=5CHrUg6skrOsVXp5dDOiT_V5Xgdvc#okosc z%h+Rx(-3qJx~s98SWxn!pO{@Y%8s^i!i0aToWxv{ zbBaR?PD6u!e=`D$847+?6)O&q6-bsEX4P)%=PhAxo@o6O>6Ki~h7rO6rL-cfj`eEy zOtx3GxMkM*+oe2=SP?Oh%1HwtFWZTf+*ntrW4nzboKrS^)k_0<#7Wr6DNv z%<08-o$$JiasHV_YtrL0XC6fn2?)?SF2l2hnbK=*zC~UW7863$#`n8EO)WLsySfg%(P6$X9>6ft^`-@y z$esFd+-p4x!rB*{Vy>0M`NyrC1BbI)^0ZqrT^RDkLG=1J{=Nmk4d>fg=R;`D@ftL(rnFY)Gb^ zo-7Lcu`@c(<_FP^v!gf|!^zdVa?0L_lX(D(_60I8)Kmw!kwIfHNLZ2FnmBbxiY7vhAk~4k lDX8@HE;^tLysGBN;<)qv13QOJ9p!y(BlcVs^R(vJKkrqBQY literal 0 HcmV?d00001 diff --git a/amaru-crypto/Cargo.toml b/amaru-crypto/Cargo.toml deleted file mode 100644 index b82e222..0000000 --- a/amaru-crypto/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "amaru-crypto" -description = "Amaru crate providing higher-level Cardano-specific crypto algorithms." -version = "0.0.1" -edition = "2021" -repository = "https://github.com/pragma-org/amaru" -homepage = "https://github.com/pragma-org/amaru" -license = "Apache-2.0" -readme = "README.md" -authors = [ - "Andrew Westberg ", -] -[dependencies] diff --git a/amaru-crypto/src/lib.rs b/amaru-crypto/src/lib.rs deleted file mode 100644 index b93cf3f..0000000 --- a/amaru-crypto/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/amaru-node/Cargo.toml b/amaru-node/Cargo.toml index 1d9d4e2..14a37a6 100644 --- a/amaru-node/Cargo.toml +++ b/amaru-node/Cargo.toml @@ -17,9 +17,4 @@ amaru-consensus = { path = "../amaru-consensus" } tokio = { version = "1", features = ["rt", "rt-multi-thread", "net", "io-util", "time", "sync", "macros"] } thiserror = "1.0" tracing = "0.1" -tracing-subscriber = "0.3" - -# logging -log = "0.4" -env_logger = "0.11" -pretty_env_logger = "0.5" +tracing-subscriber = "0.3" \ No newline at end of file diff --git a/amaru-node/src/main.rs b/amaru-node/src/main.rs index 9c2c22f..70490d2 100644 --- a/amaru-node/src/main.rs +++ b/amaru-node/src/main.rs @@ -3,6 +3,11 @@ use tracing::info; #[tokio::main] async fn main() { + init_logging(); + info!("Hello, amaru-node!"); +} + +fn init_logging() { match var("RUST_LOG") { Ok(_) => {} Err(_) => { @@ -10,7 +15,6 @@ async fn main() { set_var("RUST_LOG", "info"); } } - pretty_env_logger::init_timed(); let tracing_filter = match var("RUST_LOG") { Ok(level) => match level.to_lowercase().as_str() { @@ -28,7 +32,6 @@ async fn main() { tracing_subscriber::FmtSubscriber::builder() .with_max_level(tracing_filter) .finish(), - ).unwrap(); - - info!("Hello, amaru-node!"); + ) + .unwrap(); } diff --git a/version.toml b/version.toml deleted file mode 100644 index 1ead7a7..0000000 --- a/version.toml +++ /dev/null @@ -1,2 +0,0 @@ -[package] -version = "0.0.1" \ No newline at end of file