From 8fa2107eb6f09c49c4a2d90f9b41c31eada1d79b Mon Sep 17 00:00:00 2001 From: Andrew Westberg Date: Thu, 22 Aug 2024 14:07:34 +0000 Subject: [PATCH] Refactor and use pallas nonce --- .github/workflows/ci.yml | 164 +-- .gitignore | 1 + Cargo.lock | 1146 +++++++---------- Cargo.toml | 47 +- build.rs | 53 - contrib/libsodium | 1 - src/lib.rs | 1014 +++++++-------- src/main.rs | 13 +- src/nodeclient/blockstore/mod.rs | 48 + src/nodeclient/blockstore/redb.rs | 584 +++++++++ src/nodeclient/{ => blockstore}/sqlite.rs | 246 +++- src/nodeclient/leaderlog/ledgerstate.rs | 6 +- src/nodeclient/leaderlog/libsodium.rs | 75 -- .../{leaderlog.rs => leaderlog/mod.rs} | 654 +++++----- src/nodeclient/math.rs | 441 ------- src/nodeclient/mod.rs | 7 + src/nodeclient/{ping.rs => ping/mod.rs} | 70 + src/nodeclient/sign/mod.rs | 231 ++++ src/nodeclient/signing.rs | 200 --- .../{snapshot.rs => snapshot/mod.rs} | 17 +- src/nodeclient/{sync.rs => sync/mod.rs} | 101 +- src/nodeclient/{ => sync}/pooltool.rs | 91 +- src/nodeclient/validate.rs | 76 -- src/nodeclient/validate/mod.rs | 82 ++ src/test.rs | 261 ---- 25 files changed, 2707 insertions(+), 2922 deletions(-) delete mode 160000 contrib/libsodium create mode 100644 src/nodeclient/blockstore/mod.rs create mode 100644 src/nodeclient/blockstore/redb.rs rename src/nodeclient/{ => blockstore}/sqlite.rs (63%) delete mode 100644 src/nodeclient/leaderlog/libsodium.rs rename src/nodeclient/{leaderlog.rs => leaderlog/mod.rs} (61%) delete mode 100644 src/nodeclient/math.rs create mode 100644 src/nodeclient/mod.rs rename src/nodeclient/{ping.rs => ping/mod.rs} (65%) create mode 100644 src/nodeclient/sign/mod.rs delete mode 100644 src/nodeclient/signing.rs rename src/nodeclient/{snapshot.rs => snapshot/mod.rs} (91%) rename src/nodeclient/{sync.rs => sync/mod.rs} (91%) rename src/nodeclient/{ => sync}/pooltool.rs (76%) delete mode 100644 src/nodeclient/validate.rs create mode 100644 src/nodeclient/validate/mod.rs delete mode 100644 src/test.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0ce37b..f602797 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,121 +1,99 @@ -on: [push, pull_request] +# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md -jobs: +on: + push: { } + +name: Validate +jobs: build: - name: ${{ matrix.job.os }} (${{ matrix.job.target }}) - runs-on: ${{ matrix.job.os }} - defaults: - run: - shell: bash + name: Build strategy: fail-fast: false matrix: job: - { os: ubuntu-22.04, label: ubuntu22, target: x86_64-unknown-linux-gnu } - { os: ubuntu-22.04, label: ubuntu22, target: x86_64-unknown-linux-musl, use-cross: true } - - { os: macos-latest, label: macos, target: x86_64-apple-darwin } + - { os: macos-latest, label: macos, target: aarch64-apple-darwin } + - { os: windows-latest, label: windows, target: x86_64-pc-windows-msvc } + rust: [ stable ] + + runs-on: ${{ matrix.job.os }} + steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: true # recurse submodules - - name: Configure Environment - run: | - case ${{ matrix.job.target }} in - *-pc-windows-gnu) - - case ${{ matrix.job.target }} in - i686*) - MSYSTEM=MINGW32 - MSYSTEM_PATH=c:/msys64/mingw32 - ;; - x86_64*) - MSYSTEM=MINGW64 - MSYSTEM_PATH=c:/msys64/mingw64 - ;; - esac - - echo "c:/msys64" >> $GITHUB_PATH # enable msys2 entrypoint commands (probably not needed) - echo "c:/msys64/usr/bin" >> $GITHUB_PATH # place msys2 environment on system path - echo "${MSYSTEM_PATH}/bin" >> $GITHUB_PATH # place MinGW environment on system path - echo "CHERE_INVOKING=yes" >> $GITHUB_ENV # maintain directory context - echo "MSYSTEM=${MSYSTEM}" >> $GITHUB_ENV # set msystem version for msys2 shell - ;; - esac - - name: Install Prerequisites - run: | - case ${{ matrix.job.os }} in - windows64*) - pacman -S --noconfirm --needed mingw-w64-x86_64-gcc base-devel autoconf - ;; - windows32*) - pacman -S --noconfirm --needed mingw-w64-i686-gcc base-devel autoconf - ;; - ubuntu*) - sudo apt-get -y update - sudo apt-get -y install automake build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev libsystemd-dev zlib1g-dev make g++ tmux git jq wget libncursesw5 libtool autoconf musl-tools gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu - ;; - macos*) - brew install automake - brew install libtool - ;; - esac - - name: Install Rust Toolchain - uses: actions-rs/toolchain@v1 + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable with: - toolchain: ${{ matrix.job.toolchain || 'stable' }} - target: ${{ matrix.job.target }} - override: true - default: true + toolchain: ${{ matrix.rust }} components: rustfmt, clippy - - name: Build Libsodium - if: contains(matrix.job.features, 'libsodium-sys') - run: autoreconf -vfi && ./configure && make && make install - working-directory: contrib/libsodium - - name: Format - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - name: Lint (Clippy) - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --release --features "${{ join(matrix.job.features, ',') }}" --target ${{ matrix.job.target }} -- -D warnings - - name: Test - if: "${{ ! contains(matrix.job.target, 'aarch64') }}" - uses: actions-rs/cargo@v1 - with: - use-cross: ${{ matrix.job.use-cross }} - command: test - args: --release --features "${{ join(matrix.job.features, ',') }}" --target ${{ matrix.job.target }} - - name: Build - uses: actions-rs/cargo@v1 - with: - use-cross: ${{ matrix.job.use-cross }} - command: build - args: --release --features "${{ join(matrix.job.features, ',') }}" --target ${{ matrix.job.target }} --locked + + - name: Install cross + if: ${{ matrix.job.use-cross }} + run: cargo install cross + + - name: Run cargo check + if: ${{ matrix.job.target == 'x86_64-unknown-linux-gnu' }} + run: cargo check + + - name: Run cargo fmt + if: ${{ matrix.job.target == 'x86_64-unknown-linux-gnu' }} + run: cargo fmt --all -- --check + + - name: Run cargo clippy (cross) + if: ${{ matrix.job.use-cross }} + run: cross clippy --release --target ${{ matrix.job.target }} -- -D warnings + + - name: Run cargo clippy + if: ${{ !matrix.job.use-cross }} + run: cargo clippy --release --target ${{ matrix.job.target }} -- -D warnings + + - name: Run cargo test (cross) + if: ${{ matrix.job.use-cross }} + run: cross test --release --target ${{ matrix.job.target }} + + - name: Run cargo test + if: ${{ !matrix.job.use-cross }} + run: cargo test --release --target ${{ matrix.job.target }} + + - name: Build Release (cross) + if: ${{ matrix.job.use-cross }} + run: cross build --release --target ${{ matrix.job.target }} --locked + + - name: Build Release + if: ${{ !matrix.job.use-cross }} + run: cargo build --release --target ${{ matrix.job.target }} --locked + - name: Package id: package + shell: bash run: | PROJECT_NAME=$(sed -n 's/^name = "\(.*\)"/\1/p' Cargo.toml) PROJECT_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1) - PKG_SUFFIX=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_SUFFIX=".zip" ;; esac; + if [[ "${{ matrix.job.target }}" == *-pc-windows-* ]]; then + PKG_SUFFIX=".zip" + else + PKG_SUFFIX=".tar.gz" + fi PKG_NAME=${PROJECT_NAME}-${PROJECT_VERSION}-${{ matrix.job.label }}-${{ matrix.job.target }}${PKG_SUFFIX} - - case ${{ matrix.job.target }} in - *-pc-windows-*) 7z -y a "${PKG_NAME}" ./target/${{matrix.job.target}}/release/cncli.exe | tail -2 ;; - *) tar -C target/${{matrix.job.target}}/release -czf "${PKG_NAME}" cncli ;; - esac; + + if [[ "${{ matrix.job.target }}" == *-pc-windows-* ]]; then + 7z -y a "${PKG_NAME}" ./target/${{matrix.job.target}}/release/cncli.exe | tail -2 + else + tar -C target/${{matrix.job.target}}/release -czf "${PKG_NAME}" cncli + fi echo ::set-output name=PKG_NAME::${PKG_NAME} echo ::set-output name=PKG_PATH::${PKG_NAME} + - name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ steps.package.outputs.PKG_NAME }} path: ${{ steps.package.outputs.PKG_PATH }} + - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/v') diff --git a/.gitignore b/.gitignore index cef4277..cad44ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target .idea *.db* +*.redb* *.skey *.vkey pooltool.json diff --git a/Cargo.lock b/Cargo.lock index cfb1b7a..8a7c94a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" @@ -62,67 +62,6 @@ dependencies = [ "winapi", ] -[[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 = "arrayref" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "async-channel" version = "1.9.0" @@ -161,14 +100,14 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", + "fastrand", + "futures-lite", "slab", ] @@ -180,59 +119,30 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.3.1", "async-executor", - "async-io 2.3.3", - "async-lock 3.4.0", + "async-io", + "async-lock", "blocking", - "futures-lite 2.3.0", + "futures-lite", "once_cell", ] [[package]] name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ - "async-lock 3.4.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.7.2", - "rustix 0.38.34", + "polling", + "rustix", "slab", "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", + "windows-sys 0.59.0", ] [[package]] @@ -248,19 +158,19 @@ dependencies = [ [[package]] name = "async-std" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io", + "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 1.13.0", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -297,32 +207,23 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "autotools" -version = "0.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf" -dependencies = [ - "cc", -] +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -350,16 +251,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] -name = "bigdecimal" -version = "0.4.5" +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "autocfg", - "libm", - "num-bigint", - "num-integer", - "num-traits", + "serde", ] [[package]] @@ -375,14 +272,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] -name = "blake2b_simd" -version = "1.0.2" +name = "block-buffer" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", + "generic-array", ] [[package]] @@ -394,7 +289,7 @@ dependencies = [ "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", ] @@ -421,18 +316,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.7" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -452,14 +348,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] name = "chrono-tz" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" dependencies = [ "chrono", "chrono-tz-build", @@ -468,12 +364,11 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" dependencies = [ "parse-zoneinfo", - "phf", "phf_codegen", ] @@ -494,33 +389,29 @@ dependencies = [ [[package]] name = "cncli" -version = "6.3.1" +version = "6.4.0" dependencies = [ "async-std", - "autotools", "bech32 0.11.0", - "bigdecimal", - "blake2b_simd", + "bincode", "built", "byteorder", "chrono", "chrono-tz", - "env_logger 0.11.5", "futures", "hex", - "itertools", - "libc", + "itertools 0.13.0", "log", - "minicbor 0.24.2", - "num-bigint", - "num-rational", - "pallas-crypto 0.30.0 (git+https://github.com/txpipe/pallas?rev=d3084d6e3209ee0f507a413b1a2491e07abf3756)", + "malachite", + "malachite-base", + "minicbor 0.25.1", + "pallas-crypto", + "pallas-math", "pallas-network", "pallas-traverse", - "pkg-config", - "pretty_env_logger", "rand", "rayon", + "redb", "regex", "reqwest", "rusqlite", @@ -528,20 +419,15 @@ dependencies = [ "serde-aux", "serde_cbor", "serde_json", - "socket2 0.5.7", + "socket2", "structopt", "thiserror", "tokio", "tracing", "tracing-subscriber", + "uuid", ] -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -552,16 +438,19 @@ dependencies = [ ] [[package]] -name = "constant_time_eq" -version = "0.3.0" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "cpufeatures" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] [[package]] name = "crc" @@ -619,46 +508,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" [[package]] -name = "either" -version = "1.13.0" +name = "curve25519-dalek" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle", + "zeroize", +] [[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +name = "curve25519-dalek" +version = "3.2.0" +source = "git+https://github.com/txpipe/curve25519-dalek?branch=ietf03_vrf_compat_ell2#70a36f41cfc3fbb7357ec3062201b911787decba" dependencies = [ - "log", - "regex", + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle", + "zeroize", ] [[package]] -name = "env_logger" -version = "0.10.2" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", + "generic-array", ] [[package]] -name = "env_logger" -version = "0.11.5" +name = "either" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "errno" @@ -711,24 +598,15 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "flate2" -version = "1.0.31" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -797,28 +675,13 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.1.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -833,7 +696,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -866,6 +729,27 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -874,14 +758,14 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "git2" @@ -898,9 +782,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ "futures-channel", "futures-core", @@ -1004,15 +888,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "hyper" @@ -1035,9 +913,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http", @@ -1053,9 +931,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -1064,18 +942,17 @@ dependencies = [ "http-body", "hyper", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", - "tower", "tower-service", "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1104,49 +981,21 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] -name = "is-terminal" -version = "0.4.12" +name = "itertools" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ - "hermit-abi 0.3.9", - "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" @@ -1173,9 +1022,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -1197,9 +1046,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libgit2-sys" @@ -1232,9 +1081,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "libc", @@ -1242,12 +1091,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1263,6 +1106,51 @@ dependencies = [ "value-bag", ] +[[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" @@ -1287,11 +1175,11 @@ dependencies = [ [[package]] name = "minicbor" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8e213c36148d828083ae01948eed271d03f95f7e72571fa242d78184029af2" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" dependencies = [ - "minicbor-derive 0.15.0", + "minicbor-derive 0.15.3", ] [[package]] @@ -1307,33 +1195,33 @@ dependencies = [ [[package]] name = "minicbor-derive" -version = "0.15.0" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6bdc119b1a405df86a8cde673295114179dbd0ebe18877c26ba89fb080365c2" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1347,36 +1235,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1388,18 +1246,27 @@ dependencies = [ [[package]] name = "object" -version = "0.36.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "overload" @@ -1409,36 +1276,23 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pallas-addresses" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1659716de2b4e26cbef0fd7ff14aaf97491cea38b61b6659368aaf2b73be4b35" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ "base58", "bech32 0.9.1", "crc", "cryptoxide", "hex", - "pallas-codec 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallas-crypto 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror", -] - -[[package]] -name = "pallas-codec" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eaf6270dbf928c57e348fe317e3c29524274f57e1b46c5aa5f7c0e91709180c" -dependencies = [ - "hex", - "minicbor 0.20.0", - "serde", + "pallas-codec", + "pallas-crypto", "thiserror", ] [[package]] name = "pallas-codec" -version = "0.30.0" -source = "git+https://github.com/txpipe/pallas?rev=d3084d6e3209ee0f507a413b1a2491e07abf3756#d3084d6e3209ee0f507a413b1a2491e07abf3756" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ "hex", "minicbor 0.20.0", @@ -1448,44 +1302,42 @@ dependencies = [ [[package]] name = "pallas-crypto" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8563598b51b66b5f29ddce468b25c56984ef3d0ba6184b5106c9642f8cb5bb11" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ "cryptoxide", "hex", - "pallas-codec 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core", + "pallas-codec", + "rand_core 0.6.4", "serde", "thiserror", + "vrf_dalek", ] [[package]] -name = "pallas-crypto" -version = "0.30.0" -source = "git+https://github.com/txpipe/pallas?rev=d3084d6e3209ee0f507a413b1a2491e07abf3756#d3084d6e3209ee0f507a413b1a2491e07abf3756" +name = "pallas-math" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ - "cryptoxide", - "hex", - "pallas-codec 0.30.0 (git+https://github.com/txpipe/pallas?rev=d3084d6e3209ee0f507a413b1a2491e07abf3756)", - "rand_core", - "serde", + "malachite", + "malachite-base", + "once_cell", + "regex", "thiserror", ] [[package]] name = "pallas-network" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a14b752833e2a18ba9238e5bbf87e185c9dd85e8055e7d24e2a88b9030bf9" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ "byteorder", "hex", - "itertools", - "pallas-codec 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallas-crypto 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.13.0", + "pallas-codec", + "pallas-crypto", "rand", - "socket2 0.5.7", + "socket2", "thiserror", "tokio", "tracing", @@ -1493,31 +1345,29 @@ dependencies = [ [[package]] name = "pallas-primitives" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9458b82f0203aaadfb4bf996a7dbc5407caf8e31f035f5268be1f10e174578" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ "base58", "bech32 0.9.1", "hex", "log", - "pallas-codec 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallas-crypto 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-codec", + "pallas-crypto", "serde", "serde_json", ] [[package]] name = "pallas-traverse" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2781b2bf2a69371c874abcb6b24e5105875c8017b9a9713f9a987fa9c6d12a" +version = "0.30.2" +source = "git+https://github.com/txpipe/pallas?rev=de88df1986dc1487452075dd29cec0996a46e3c3#de88df1986dc1487452075dd29cec0996a46e3c3" dependencies = [ "hex", - "itertools", + "itertools 0.13.0", "pallas-addresses", - "pallas-codec 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallas-crypto 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pallas-codec", + "pallas-crypto", "pallas-primitives", "paste", "serde", @@ -1526,9 +1376,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parse-zoneinfo" @@ -1589,26 +1439,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1623,51 +1453,41 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.0", + "fastrand", "futures-io", ] [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "2.8.0" +version = "3.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" dependencies = [ - "autocfg", - "bitflags 1.3.2", "cfg-if", "concurrent-queue", - "libc", - "log", + "hermit-abi 0.4.0", "pin-project-lite", - "windows-sys 0.48.0", + "rustix", + "tracing", + "windows-sys 0.59.0", ] [[package]] -name = "polling" -version = "3.7.2" +name = "portable-atomic" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.4.0", - "pin-project-lite", - "rustix 0.38.34", - "tracing", - "windows-sys 0.52.0", -] +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "ppv-lite86" @@ -1678,16 +1498,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "pretty_env_logger" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" -dependencies = [ - "env_logger 0.10.2", - "log", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1723,9 +1533,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" dependencies = [ "bytes", "pin-project-lite", @@ -1733,6 +1543,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", + "socket2", "thiserror", "tokio", "tracing", @@ -1740,9 +1551,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand", @@ -1757,21 +1568,22 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" dependencies = [ "libc", "once_cell", - "socket2 0.5.7", - "windows-sys 0.52.0", + "socket2", + "tracing", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1784,7 +1596,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1794,7 +1606,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -1803,7 +1624,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1826,11 +1647,20 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redb" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4760ad04a88ef77075ba86ba9ea79b919e6bab29c1764c5747237cd6eaedcaa" +dependencies = [ + "libc", +] + [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -1840,9 +1670,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1851,15 +1681,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ "async-compression", "base64", @@ -1897,7 +1727,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -1908,7 +1738,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -1937,42 +1767,28 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustix" -version = "0.37.27" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "once_cell", "ring", @@ -1984,25 +1800,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -2017,9 +1832,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -2047,20 +1862,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -2080,6 +1895,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" @@ -2089,6 +1917,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "siphasher" version = "0.3.11" @@ -2110,16 +1944,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.7" @@ -2185,9 +2009,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -2199,14 +2023,8 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "winapi-util", + "futures-core", ] [[package]] @@ -2220,22 +2038,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -2265,16 +2083,16 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -2287,7 +2105,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -2303,9 +2121,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -2314,32 +2132,11 @@ dependencies = [ "tokio", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -2360,7 +2157,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -2404,6 +2201,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -2412,30 +2215,30 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "untrusted" @@ -2455,10 +2258,13 @@ dependencies = [ ] [[package]] -name = "utf8parse" -version = "0.2.2" +name = "uuid" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom 0.2.15", +] [[package]] name = "valuable" @@ -2491,10 +2297,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +name = "vrf_dalek" +version = "0.1.0" +source = "git+https://github.com/txpipe/vrf?rev=044b45a1a919ba9d9c2471fc5c4d441f13086676#044b45a1a919ba9d9c2471fc5c4d441f13086676" +dependencies = [ + "curve25519-dalek 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 3.2.0 (git+https://github.com/txpipe/curve25519-dalek?branch=ietf03_vrf_compat_ell2)", + "rand_core 0.5.1", + "sha2", + "thiserror", +] [[package]] name = "want" @@ -2505,6 +2317,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2513,34 +2331,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -2550,9 +2369,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2560,28 +2379,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -2589,9 +2408,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] @@ -2612,15 +2431,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" @@ -2633,49 +2443,55 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-targets 0.48.5", + "windows-result", + "windows-strings", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-targets 0.52.6", + "windows-result", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2684,46 +2500,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2736,64 +2534,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "zerocopy" version = "0.7.35" @@ -2812,7 +2576,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 16cd656..1cc5ca8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,33 +1,33 @@ [package] name = "cncli" -version = "6.3.1" +version = "6.4.0" authors = ["Andrew Westberg "] -edition = "2018" +edition = "2021" build = "build.rs" -links = "libsodium" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-std = "1.12" +async-std = "1.13" bech32 = "0.11" -bigdecimal = "0.4" -num-bigint = "0.4" -num-rational = "0.4" -blake2b_simd = "1.0" +bincode = "1.3.3" byteorder = "1.5" -#pallas-network = { git = "https://github.com/AndrewWestberg/pallas", rev="35f693c57eec5f70c4f8e2f6a24445b14c6104b9" } -#pallas-traverse = { git = "https://github.com/AndrewWestberg/pallas", rev="35f693c57eec5f70c4f8e2f6a24445b14c6104b9" } -pallas-crypto = { git = "https://github.com/txpipe/pallas", rev = "d3084d6e3209ee0f507a413b1a2491e07abf3756" } -pallas-network = "0.30" -pallas-traverse = "0.30" -#pallas-crypto = "0.30.0" +pallas-crypto = { git = "https://github.com/txpipe/pallas", rev = "de88df1986dc1487452075dd29cec0996a46e3c3" } +pallas-math = { git = "https://github.com/txpipe/pallas", rev = "de88df1986dc1487452075dd29cec0996a46e3c3" } +pallas-network = { git = "https://github.com/txpipe/pallas", rev = "de88df1986dc1487452075dd29cec0996a46e3c3" } +pallas-traverse = { git = "https://github.com/txpipe/pallas", rev = "de88df1986dc1487452075dd29cec0996a46e3c3" } +#pallas-crypto = "0.30" +#pallas-math = "0.30" +#pallas-network = "0.30" +#pallas-traverse = "0.30" chrono = "0.4" -chrono-tz = "0.9" +chrono-tz = "0.10" futures = "0.3" hex = "0.4" -libc = "0.2" -minicbor = { version = "0.24", features = ["std"] } +malachite-base = "0.4.16" +malachite = "0.4.16" +minicbor = { version = "0.25", features = ["std"] } +redb = "2.1.1" regex = "1.10" reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls-webpki-roots", "rustls-tls", "json", "gzip", "deflate"] } rusqlite = { version = "0.32", features = ["bundled"] } @@ -44,17 +44,8 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "net", "io-util", thiserror = "1.0" tracing = "0.1" tracing-subscriber = "0.3" - - -# logging -log = "0.4" -env_logger = "0.11" -pretty_env_logger = "0.5" +uuid = { version = "1", features = ["v7"] } +log = "0.4.22" [build-dependencies] -autotools = "0.2" -pkg-config = "0.3" built = { version = "0.7", features = ["git2"] } - -[features] -libsodium-sys = [] diff --git a/build.rs b/build.rs index 6eb7ff6..6749700 100644 --- a/build.rs +++ b/build.rs @@ -1,58 +1,5 @@ -use std::env; -use std::path::Path; -use std::process::Command; - -macro_rules! ok (($expression:expr) => ($expression.unwrap())); -macro_rules! log { - ($fmt:expr) => (println!(concat!("cncli/build.rs:{}: ", $fmt), line!())); - ($fmt:expr, $($arg:tt)*) => (println!(concat!("cncli/build.rs:{}: ", $fmt), - line!(), $($arg)*)); -} - fn main() { built::write_built_file().expect("Failed to acquire build-time information"); - #[cfg(not(feature = "libsodium-sys"))] - { - // Use set libsodium env path - if env::var("SODIUM_LIB_DIR").is_ok() { - let libdir = env::var("SODIUM_LIB_DIR").unwrap(); - println!("cargo:rustc-link-search=native={}", Path::new(&libdir).display()); - println!("cargo:rustc-link-lib=static=sodium"); - } else { - // Build and link IOHK libsodium - run("git", |command| { - command - .arg("submodule") - .arg("update") - .arg("--init") - .arg("--recursive") - .arg("--force") - }); - // Build libsodium automatically (as part of rust build) - let libsodium = autotools::Config::new("contrib/libsodium/").reconf("-vfi").build(); - println!("cargo:rustc-link-search=native={}", libsodium.join("lib").display()); - println!("cargo:rustc-link-lib=static=sodium"); - } - } - // Link with libsodium system library - #[cfg(feature = "libsodium-sys")] - { - pkg_config::Config::new().probe("libsodium").unwrap(); - } - println!("cargo:rerun-if-changed=build.rs"); } - -fn run(name: &str, mut configure: F) -where - F: FnMut(&mut Command) -> &mut Command, -{ - let mut command = Command::new(name); - let configured = configure(&mut command); - log!("Executing {:?}", configured); - if !ok!(configured.status()).success() { - panic!("failed to execute {:?}", configured); - } - log!("Command {:?} finished successfully", configured); -} diff --git a/contrib/libsodium b/contrib/libsodium deleted file mode 160000 index dbb48cc..0000000 --- a/contrib/libsodium +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dbb48cce5429cb6585c9034f002568964f1ce567 diff --git a/src/lib.rs b/src/lib.rs index f70fa12..c239605 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,544 +1,516 @@ -pub mod nodeclient { - use std::fs::File; - use std::io::{stdout, BufReader}; - use std::path::{Path, PathBuf}; - use std::str::FromStr; - use std::string::ParseError; - use std::thread; - use std::thread::JoinHandle; +use std::io::stdout; +use std::path::PathBuf; +use std::str::FromStr; +use std::string::ParseError; +use std::thread; +use std::thread::JoinHandle; - use serde::Deserialize; - use structopt::StructOpt; +use structopt::StructOpt; - use crate::nodeclient::leaderlog::handle_error; +use crate::nodeclient::leaderlog::handle_error; +use crate::nodeclient::sync::pooltool; +use crate::nodeclient::sync::pooltool::PooltoolConfig; +use crate::nodeclient::{leaderlog, ping, sign, snapshot, sync, validate}; - pub mod leaderlog; - pub mod math; - pub mod ping; - pub mod pooltool; - pub mod signing; - pub mod snapshot; - pub mod sqlite; - pub mod sync; - mod validate; +pub(crate) mod nodeclient; - pub static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); +pub static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); - #[derive(Debug)] - pub enum LedgerSet { - Mark, - Set, - Go, - } +#[derive(Debug)] +pub enum LedgerSet { + Mark, + Set, + Go, +} - impl FromStr for LedgerSet { - type Err = ParseError; - fn from_str(ledger_set: &str) -> Result { - match ledger_set { - "next" => Ok(LedgerSet::Mark), - "current" => Ok(LedgerSet::Set), - "prev" => Ok(LedgerSet::Go), - _ => Ok(LedgerSet::Set), - } +impl FromStr for LedgerSet { + type Err = ParseError; + fn from_str(ledger_set: &str) -> Result { + match ledger_set { + "next" => Ok(LedgerSet::Mark), + "current" => Ok(LedgerSet::Set), + "prev" => Ok(LedgerSet::Go), + _ => Ok(LedgerSet::Set), } } +} - #[derive(Debug, StructOpt)] - pub enum Command { - Ping { - #[structopt(short, long, help = "cardano-node hostname to connect to")] - host: String, - #[structopt(short, long, default_value = "3001", help = "cardano-node port")] - port: u16, - #[structopt(long, default_value = "764824073", help = "network magic.")] - network_magic: u64, - #[structopt(short, long, default_value = "2", help = "connect timeout in seconds")] - timeout_seconds: u64, - }, - Validate { - #[structopt(long, help = "full or partial block hash to validate")] - hash: String, - #[structopt( - parse(from_os_str), - short, - long, - default_value = "./cncli.db", - help = "sqlite database file" - )] - db: PathBuf, - }, - Sync { - #[structopt( - parse(from_os_str), - short, - long, - default_value = "./cncli.db", - help = "sqlite database file" - )] - db: PathBuf, - #[structopt(short, long, help = "cardano-node hostname to connect to")] - host: String, - #[structopt(short, long, default_value = "3001", help = "cardano-node port")] - port: u16, - #[structopt(long, default_value = "764824073", help = "network magic.")] - network_magic: u64, - #[structopt(long, help = "Exit at 100% sync'd.")] - no_service: bool, - #[structopt( - short, - long, - default_value = "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81", - help = "shelley genesis hash value" - )] - shelley_genesis_hash: String, - }, - Leaderlog { - #[structopt( - parse(from_os_str), - short, - long, - default_value = "./cncli.db", - help = "sqlite database file" - )] - db: PathBuf, - #[structopt(parse(from_os_str), long, help = "byron genesis json file")] - byron_genesis: PathBuf, - #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] - shelley_genesis: PathBuf, - #[structopt(long, help = "pool active stake snapshot value in lovelace")] - pool_stake: u64, - #[structopt(long, help = "total active stake snapshot value in lovelace")] - active_stake: u64, - #[structopt(long = "d", default_value = "0", help = "decentralization parameter")] - d: f64, - #[structopt(long, help = "hex string of the extra entropy value")] - extra_entropy: Option, - #[structopt( - long, - default_value = "current", - help = "Which ledger data to use. prev - previous epoch, current - current epoch, next - future epoch" - )] - ledger_set: LedgerSet, - #[structopt(long, help = "lower-case hex pool id")] - pool_id: String, - #[structopt(parse(from_os_str), long, help = "pool's vrf.skey file")] - pool_vrf_skey: PathBuf, - #[structopt( - long = "tz", - default_value = "America/Los_Angeles", - help = "TimeZone string from the IANA database - https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" - )] - timezone: String, - #[structopt( - short, - long, - default_value = "praos", - help = "Consensus algorithm - Alonzo and earlier uses tpraos, Babbage uses praos, Conway uses cpraos" - )] - consensus: String, - #[structopt( - long, - env = "SHELLEY_TRANS_EPOCH", - default_value = "-1", - help = "Epoch number where we transition from Byron to Shelley. -1 means guess based on genesis files" - )] - shelley_transition_epoch: i64, - #[structopt( - long, - help = "Provide a nonce value in lower-case hex instead of calculating from the db" - )] - nonce: Option, - #[structopt( - long, - help = "Provide a specific epoch number to calculate for and ignore --ledger-set option" - )] - epoch: Option, - }, - Sendtip { - #[structopt( - parse(from_os_str), - long, - default_value = "./pooltool.json", - help = "pooltool config file for sending tips" - )] - config: PathBuf, - #[structopt( - parse(from_os_str), - long, - help = "path to cardano-node executable for gathering version info" - )] - cardano_node: PathBuf, - }, - Sendslots { - #[structopt( - parse(from_os_str), - long, - default_value = "./pooltool.json", - help = "pooltool config file for sending slots" - )] - config: PathBuf, - #[structopt( - parse(from_os_str), - short, - long, - default_value = "./cncli.db", - help = "sqlite database file" - )] - db: PathBuf, - #[structopt(parse(from_os_str), long, help = "byron genesis json file")] - byron_genesis: PathBuf, - #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] - shelley_genesis: PathBuf, - #[structopt( - long, - env = "SHELLEY_TRANS_EPOCH", - default_value = "-1", - help = "Epoch number where we transition from Byron to Shelley. -1 means guess based on genesis files" - )] - shelley_transition_epoch: i64, - #[structopt(long, env = "OVERRIDE_TIME", hide_env_values = true, hidden = true)] - override_time: Option, - }, - Status { - #[structopt( - parse(from_os_str), - short, - long, - default_value = "./cncli.db", - help = "sqlite database file" - )] - db: PathBuf, - #[structopt(parse(from_os_str), long, help = "byron genesis json file")] - byron_genesis: PathBuf, - #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] - shelley_genesis: PathBuf, - #[structopt( - long, - env = "SHELLEY_TRANS_EPOCH", - default_value = "-1", - help = "Epoch number where we transition from Byron to Shelley. -1 means guess based on genesis files" - )] - shelley_transition_epoch: i64, - }, - Nonce { - #[structopt( - parse(from_os_str), - short, - long, - default_value = "./cncli.db", - help = "sqlite database file" - )] - db: PathBuf, - #[structopt(parse(from_os_str), long, help = "byron genesis json file")] - byron_genesis: PathBuf, - #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] - shelley_genesis: PathBuf, - #[structopt(long, help = "hex string of the extra entropy value")] - extra_entropy: Option, - #[structopt( - long, - default_value = "current", - help = "Which ledger data to use. prev - previous epoch, current - current epoch, next - future epoch" - )] - ledger_set: LedgerSet, - #[structopt( - long, - env = "SHELLEY_TRANS_EPOCH", - default_value = "-1", - help = "Epoch number where we transition from Byron to Shelley. -1 means guess based on genesis files" - )] - shelley_transition_epoch: i64, - #[structopt( - short, - long, - default_value = "praos", - help = "Consensus algorithm - Alonzo and earlier uses tpraos, Babbage uses praos, Conway uses cpraos" - )] - consensus: String, - #[structopt( - long, - help = "Provide a specific epoch number to calculate for and ignore --ledger-set option" - )] - epoch: Option, - }, - Challenge { - #[structopt(long, help = "validating domain e.g. pooltool.io")] - domain: String, - }, - Sign { - #[structopt(parse(from_os_str), long, help = "pool's vrf.skey file")] - pool_vrf_skey: PathBuf, - #[structopt(long, help = "validating domain e.g. pooltool.io")] - domain: String, - #[structopt(long, help = "nonce value in lower-case hex")] - nonce: String, - }, - Verify { - #[structopt(parse(from_os_str), long, help = "pool's vrf.vkey file")] - pool_vrf_vkey: PathBuf, - #[structopt( - long, - help = "pool's vrf hash in hex retrieved from 'cardano-cli query pool-params...'" - )] - pool_vrf_vkey_hash: String, - #[structopt(long, help = "validating domain e.g. pooltool.io")] - domain: String, - #[structopt(long, help = "nonce value in lower-case hex")] - nonce: String, - #[structopt(long, help = "signature to verify in hex")] - signature: String, - }, - Snapshot { - #[structopt(parse(from_os_str), long, help = "cardano-node socket path")] - socket_path: PathBuf, - #[structopt(long, default_value = "764824073", help = "network magic.")] - network_magic: u64, - #[structopt(long, default_value = "mark", help = "Snapshot name to retrieve (mark, set, go)")] - name: String, - #[structopt( - long, - default_value = "1", - help = "The network identifier, (1 for mainnet, 0 for testnet)" - )] - network_id: u8, - #[structopt( - long, - default_value = "stake", - help = "The prefix for stake addresses, (stake for mainnet, stake_test for testnet)" - )] - stake_prefix: String, - #[structopt(long, default_value = "mark.csv", help = "The name of the output file (CSV format)")] - output_file: String, - }, - } +#[derive(Debug, StructOpt)] +pub enum Command { + Ping { + #[structopt(short, long, help = "cardano-node hostname to connect to")] + host: String, + #[structopt(short, long, default_value = "3001", help = "cardano-node port")] + port: u16, + #[structopt(long, default_value = "764824073", help = "network magic.")] + network_magic: u64, + #[structopt(short, long, default_value = "2", help = "connect timeout in seconds")] + timeout_seconds: u64, + }, + Validate { + #[structopt(long, help = "full or partial block hash to validate")] + hash: String, + #[structopt( + parse(from_os_str), + short, + long, + default_value = "./cncli.db", + help = "sqlite database file" + )] + db: PathBuf, + }, + Sync { + #[structopt( + parse(from_os_str), + short, + long, + default_value = "./cncli.db", + help = "sqlite database file" + )] + db: PathBuf, + #[structopt(short, long, help = "cardano-node hostname to connect to")] + host: String, + #[structopt(short, long, default_value = "3001", help = "cardano-node port")] + port: u16, + #[structopt(long, default_value = "764824073", help = "network magic.")] + network_magic: u64, + #[structopt(long, help = "Exit at 100% sync'd.")] + no_service: bool, + #[structopt( + short, + long, + default_value = "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81", + help = "shelley genesis hash value" + )] + shelley_genesis_hash: String, + #[structopt(long, help = "Use the redb database instead of sqlite")] + use_redb: bool, + }, + Leaderlog { + #[structopt( + parse(from_os_str), + short, + long, + default_value = "./cncli.db", + help = "sqlite database file" + )] + db: PathBuf, + #[structopt(parse(from_os_str), long, help = "byron genesis json file")] + byron_genesis: PathBuf, + #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] + shelley_genesis: PathBuf, + #[structopt(long, help = "pool active stake snapshot value in lovelace")] + pool_stake: u64, + #[structopt(long, help = "total active stake snapshot value in lovelace")] + active_stake: u64, + #[structopt(long = "d", default_value = "0", help = "decentralization parameter")] + d: f64, + #[structopt(long, help = "hex string of the extra entropy value")] + extra_entropy: Option, + #[structopt( + long, + default_value = "current", + help = "Which ledger data to use. prev - previous epoch, current - current epoch, next - future epoch" + )] + ledger_set: LedgerSet, + #[structopt(long, help = "lower-case hex pool id")] + pool_id: String, + #[structopt(parse(from_os_str), long, help = "pool's vrf.skey file")] + pool_vrf_skey: PathBuf, + #[structopt( + long = "tz", + default_value = "America/Los_Angeles", + help = "TimeZone string from the IANA database - https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" + )] + timezone: String, + #[structopt( + short, + long, + default_value = "praos", + help = "Consensus algorithm - Alonzo and earlier uses tpraos, Babbage uses praos, Conway uses cpraos" + )] + consensus: String, + #[structopt( + long, + env = "SHELLEY_TRANS_EPOCH", + help = "Epoch number where we transition from Byron to Shelley. Omitted means guess based on genesis files" + )] + shelley_transition_epoch: Option, + #[structopt( + long, + help = "Provide a nonce value in lower-case hex instead of calculating from the db" + )] + nonce: Option, + #[structopt( + long, + help = "Provide a specific epoch number to calculate for and ignore --ledger-set option" + )] + epoch: Option, + }, + Sendtip { + #[structopt( + parse(from_os_str), + long, + default_value = "./pooltool.json", + help = "pooltool config file for sending tips" + )] + config: PathBuf, + #[structopt( + parse(from_os_str), + long, + help = "path to cardano-node executable for gathering version info" + )] + cardano_node: PathBuf, + }, + Sendslots { + #[structopt( + parse(from_os_str), + long, + default_value = "./pooltool.json", + help = "pooltool config file for sending slots" + )] + config: PathBuf, + #[structopt( + parse(from_os_str), + short, + long, + default_value = "./cncli.db", + help = "sqlite database file" + )] + db: PathBuf, + #[structopt(parse(from_os_str), long, help = "byron genesis json file")] + byron_genesis: PathBuf, + #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] + shelley_genesis: PathBuf, + #[structopt( + long, + env = "SHELLEY_TRANS_EPOCH", + help = "Epoch number where we transition from Byron to Shelley. Omitted means guess based on genesis files" + )] + shelley_transition_epoch: Option, + #[structopt(long, env = "OVERRIDE_TIME", hide_env_values = true, hidden = true)] + override_time: Option, + }, + Status { + #[structopt( + parse(from_os_str), + short, + long, + default_value = "./cncli.db", + help = "sqlite or redb database file" + )] + db: PathBuf, + #[structopt(parse(from_os_str), long, help = "byron genesis json file")] + byron_genesis: PathBuf, + #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] + shelley_genesis: PathBuf, + #[structopt( + long, + env = "SHELLEY_TRANS_EPOCH", + help = "Epoch number where we transition from Byron to Shelley. Omitted means guess based on genesis files" + )] + shelley_transition_epoch: Option, + }, + Nonce { + #[structopt( + parse(from_os_str), + short, + long, + default_value = "./cncli.db", + help = "sqlite or redb database file" + )] + db: PathBuf, + #[structopt(parse(from_os_str), long, help = "byron genesis json file")] + byron_genesis: PathBuf, + #[structopt(parse(from_os_str), long, help = "shelley genesis json file")] + shelley_genesis: PathBuf, + #[structopt(long, help = "hex string of the extra entropy value")] + extra_entropy: Option, + #[structopt( + long, + default_value = "current", + help = "Which ledger data to use. prev - previous epoch, current - current epoch, next - future epoch" + )] + ledger_set: LedgerSet, + #[structopt( + long, + env = "SHELLEY_TRANS_EPOCH", + help = "Epoch number where we transition from Byron to Shelley. Omitted means guess based on genesis files" + )] + shelley_transition_epoch: Option, + #[structopt( + short, + long, + default_value = "praos", + help = "Consensus algorithm - Alonzo and earlier uses tpraos, Babbage uses praos, Conway uses cpraos" + )] + consensus: String, + #[structopt( + long, + help = "Provide a specific epoch number to calculate for and ignore --ledger-set option" + )] + epoch: Option, + }, + Challenge { + #[structopt(long, help = "validating domain e.g. pooltool.io")] + domain: String, + }, + Sign { + #[structopt(parse(from_os_str), long, help = "pool's vrf.skey file")] + pool_vrf_skey: PathBuf, + #[structopt(long, help = "validating domain e.g. pooltool.io")] + domain: String, + #[structopt(long, help = "nonce value in lower-case hex")] + nonce: String, + }, + Verify { + #[structopt(parse(from_os_str), long, help = "pool's vrf.vkey file")] + pool_vrf_vkey: PathBuf, + #[structopt( + long, + help = "pool's vrf hash in hex retrieved from 'cardano-cli query pool-params...'" + )] + pool_vrf_vkey_hash: String, + #[structopt(long, help = "validating domain e.g. pooltool.io")] + domain: String, + #[structopt(long, help = "nonce value in lower-case hex")] + nonce: String, + #[structopt(long, help = "signature to verify in hex")] + signature: String, + }, + Snapshot { + #[structopt(parse(from_os_str), long, help = "cardano-node socket path")] + socket_path: PathBuf, + #[structopt(long, default_value = "764824073", help = "network magic.")] + network_magic: u64, + #[structopt(long, default_value = "mark", help = "Snapshot name to retrieve (mark, set, go)")] + name: String, + #[structopt( + long, + default_value = "1", + help = "The network identifier, (1 for mainnet, 0 for testnet)" + )] + network_id: u8, + #[structopt( + long, + default_value = "stake", + help = "The prefix for stake addresses, (stake for mainnet, stake_test for testnet)" + )] + stake_prefix: String, + #[structopt(long, default_value = "mark.csv", help = "The name of the output file (CSV format)")] + output_file: String, + }, +} - pub async fn start(cmd: Command) { - match cmd { - Command::Ping { - ref host, - ref port, - ref network_magic, - ref timeout_seconds, - } => { - ping::ping(&mut stdout(), host.as_str(), *port, *network_magic, *timeout_seconds).await; - } - Command::Validate { ref db, ref hash } => { - validate::validate_block(db, hash.as_str()); +pub async fn start(cmd: Command) { + match cmd { + Command::Ping { + ref host, + ref port, + ref network_magic, + ref timeout_seconds, + } => { + ping::ping(&mut stdout(), host.as_str(), *port, *network_magic, *timeout_seconds).await; + } + Command::Validate { ref db, ref hash } => { + validate::validate_block(db, hash.as_str()); + } + Command::Sync { + ref db, + ref host, + ref port, + ref network_magic, + ref no_service, + ref shelley_genesis_hash, + ref use_redb, + } => { + sync::sync( + db, + host.as_str(), + *port, + *network_magic, + shelley_genesis_hash.as_str(), + *no_service, + *use_redb, + ) + .await; + } + Command::Leaderlog { + ref db, + ref byron_genesis, + ref shelley_genesis, + ref pool_stake, + ref active_stake, + ref d, + ref extra_entropy, + ref ledger_set, + ref pool_id, + ref pool_vrf_skey, + ref timezone, + ref consensus, + ref shelley_transition_epoch, + ref nonce, + ref epoch, + } => { + if let Err(error) = leaderlog::calculate_leader_logs( + db, + byron_genesis, + shelley_genesis, + pool_stake, + active_stake, + d, + extra_entropy, + ledger_set, + pool_id, + pool_vrf_skey, + timezone, + false, + consensus, + shelley_transition_epoch, + nonce, + epoch, + ) { + handle_error(error); } - Command::Sync { - ref db, - ref host, - ref port, - ref network_magic, - ref no_service, - ref shelley_genesis_hash, - } => { - sync::sync( - db, - host.as_str(), - *port, - *network_magic, - shelley_genesis_hash.as_str(), - *no_service, - ) - .await; + } + Command::Nonce { + ref db, + ref byron_genesis, + ref shelley_genesis, + ref extra_entropy, + ref ledger_set, + ref shelley_transition_epoch, + ref consensus, + ref epoch, + } => { + if let Err(error) = leaderlog::calculate_leader_logs( + db, + byron_genesis, + shelley_genesis, + &0u64, + &0u64, + &0f64, + extra_entropy, + ledger_set, + "nonce", + &PathBuf::new(), + "America/Los_Angeles", + true, + consensus, + shelley_transition_epoch, + &None, + epoch, + ) { + handle_error(error); } - Command::Leaderlog { - ref db, - ref byron_genesis, - ref shelley_genesis, - ref pool_stake, - ref active_stake, - ref d, - ref extra_entropy, - ref ledger_set, - ref pool_id, - ref pool_vrf_skey, - ref timezone, - ref consensus, - ref shelley_transition_epoch, - ref nonce, - ref epoch, - } => { - if let Err(error) = leaderlog::calculate_leader_logs( - db, - byron_genesis, - shelley_genesis, - pool_stake, - active_stake, - d, - extra_entropy, - ledger_set, - pool_id, - pool_vrf_skey, - timezone, - false, - consensus, - shelley_transition_epoch, - nonce, - epoch, - ) { - handle_error(error); - } + } + Command::Sendtip { + ref config, + ref cardano_node, + } => { + if !config.exists() { + handle_error("config not found!"); + return; } - Command::Nonce { - ref db, - ref byron_genesis, - ref shelley_genesis, - ref extra_entropy, - ref ledger_set, - ref shelley_transition_epoch, - ref consensus, - ref epoch, - } => { - if let Err(error) = leaderlog::calculate_leader_logs( - db, - byron_genesis, - shelley_genesis, - &0u64, - &0u64, - &0f64, - extra_entropy, - ledger_set, - "nonce", - &PathBuf::new(), - "America/Los_Angeles", - true, - consensus, - shelley_transition_epoch, - &None, - epoch, - ) { - handle_error(error); - } + if !cardano_node.exists() { + handle_error("cardano-node not found!"); + return; } - Command::Sendtip { - ref config, - ref cardano_node, - } => { - if !config.exists() { - handle_error("config not found!"); - return; - } - if !cardano_node.exists() { - handle_error("cardano-node not found!"); - return; - } - - let pooltool_config: PooltoolConfig = get_pooltool_config(config); - let mut handles: Vec> = vec![]; - for pool in pooltool_config.pools.into_iter() { - let api_key = pooltool_config.api_key.clone(); - let cardano_node_path = cardano_node.clone(); - handles.push(thread::spawn(move || { - tokio::runtime::Runtime::new().unwrap().block_on(sync::sendtip( - pool.name, - pool.pool_id, - pool.host, - pool.port, - api_key, - &cardano_node_path, - )); - })); - } - for handle in handles { - handle.join().unwrap() - } + let pooltool_config: PooltoolConfig = pooltool::get_pooltool_config(config); + let mut handles: Vec> = vec![]; + for pool in pooltool_config.pools.into_iter() { + let api_key = pooltool_config.api_key.clone(); + let cardano_node_path = cardano_node.clone(); + handles.push(thread::spawn(move || { + tokio::runtime::Runtime::new().unwrap().block_on(sync::sendtip( + pool.name, + pool.pool_id, + pool.host, + pool.port, + api_key, + &cardano_node_path, + )); + })); } - Command::Sendslots { - ref config, - ref db, - ref byron_genesis, - ref shelley_genesis, - ref shelley_transition_epoch, - ref override_time, - } => { - if !config.exists() { - handle_error("config not found!"); - return; - } - let pooltool_config: PooltoolConfig = get_pooltool_config(config); - leaderlog::send_slots( - db, - byron_genesis, - shelley_genesis, - pooltool_config, - shelley_transition_epoch, - override_time, - ); - } - Command::Status { - ref db, - ref byron_genesis, - ref shelley_genesis, - ref shelley_transition_epoch, - } => { - leaderlog::status(db, byron_genesis, shelley_genesis, shelley_transition_epoch); - } - Command::Challenge { ref domain } => { - signing::create_challenge(domain); + + for handle in handles { + handle.join().unwrap() } - Command::Sign { - ref pool_vrf_skey, - ref domain, - ref nonce, - } => { - if !pool_vrf_skey.exists() { - handle_error("vrf.skey not found!"); - return; - } - signing::sign_challenge(pool_vrf_skey, domain, nonce); + } + Command::Sendslots { + ref config, + ref db, + ref byron_genesis, + ref shelley_genesis, + ref shelley_transition_epoch, + ref override_time, + } => { + if !config.exists() { + handle_error("config not found!"); + return; } - Command::Verify { - ref pool_vrf_vkey, - ref pool_vrf_vkey_hash, - ref domain, - ref nonce, - ref signature, - } => { - signing::verify_challenge(pool_vrf_vkey, pool_vrf_vkey_hash, domain, nonce, signature); + let pooltool_config: PooltoolConfig = pooltool::get_pooltool_config(config); + leaderlog::send_slots( + db, + byron_genesis, + shelley_genesis, + pooltool_config, + shelley_transition_epoch, + override_time, + ); + } + Command::Status { + ref db, + ref byron_genesis, + ref shelley_genesis, + ref shelley_transition_epoch, + } => { + leaderlog::status(db, byron_genesis, shelley_genesis, shelley_transition_epoch); + } + Command::Challenge { ref domain } => { + sign::create_challenge(domain); + } + Command::Sign { + ref pool_vrf_skey, + ref domain, + ref nonce, + } => { + if !pool_vrf_skey.exists() { + handle_error("vrf.skey not found!"); + return; } - Command::Snapshot { - ref socket_path, - ref network_magic, - ref name, - ref network_id, - ref stake_prefix, - ref output_file, - } => { - if let Err(error) = snapshot::dump( - socket_path, - *network_magic, - name.as_str(), - *network_id, - stake_prefix.as_str(), - output_file.as_str(), - ) - .await - { - handle_error(error); - } + sign::sign_challenge(pool_vrf_skey, domain, nonce); + } + Command::Verify { + ref pool_vrf_vkey, + ref pool_vrf_vkey_hash, + ref domain, + ref nonce, + ref signature, + } => { + sign::verify_challenge(pool_vrf_vkey, pool_vrf_vkey_hash, domain, nonce, signature); + } + Command::Snapshot { + ref socket_path, + ref network_magic, + ref name, + ref network_id, + ref stake_prefix, + ref output_file, + } => { + if let Err(error) = snapshot::dump( + socket_path, + *network_magic, + name.as_str(), + *network_id, + stake_prefix.as_str(), + output_file.as_str(), + ) + .await + { + handle_error(error); } } } - - fn get_pooltool_config(config: &Path) -> PooltoolConfig { - let buf = BufReader::new(File::open(config).unwrap()); - serde_json::from_reader(buf).unwrap() - } - - #[derive(Debug, Deserialize)] - pub struct PooltoolConfig { - api_key: String, - pools: Vec, - } - - #[derive(Debug, Deserialize)] - struct Pool { - name: String, - pool_id: String, - host: String, - port: u16, - } } diff --git a/src/main.rs b/src/main.rs index 9b255e6..5d01db3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use std::{panic, process}; use structopt::StructOpt; -use cncli::nodeclient::{self, Command}; +use cncli::Command; pub mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); @@ -21,7 +21,9 @@ pub mod built_info { } #[derive(Debug, StructOpt)] -#[structopt(name = "cncli", about = "A community-built cardano-node CLI", version = built_info::version())] +#[structopt( + name = "cncli", about = "A community-built cardano-node CLI", version = built_info::version() +)] struct Cli { #[structopt(subcommand)] cmd: Command, @@ -36,8 +38,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() { "error" => tracing::Level::ERROR, @@ -66,8 +66,5 @@ async fn main() { })); let args = Cli::from_args(); - nodeclient::start(args.cmd).await; + cncli::start(args.cmd).await; } - -#[cfg(test)] -mod test; diff --git a/src/nodeclient/blockstore/mod.rs b/src/nodeclient/blockstore/mod.rs new file mode 100644 index 0000000..dcb7ea7 --- /dev/null +++ b/src/nodeclient/blockstore/mod.rs @@ -0,0 +1,48 @@ +use pallas_crypto::hash::Hash; +use thiserror::Error; + +use crate::nodeclient::sync::BlockHeader; + +pub(crate) mod redb; +pub(crate) mod sqlite; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Redb error: {0}")] + Redb(#[from] redb::Error), + + #[error("Sqlite error: {0}")] + Sqlite(#[from] sqlite::Error), + + #[error("rusqlite error: {0}")] + Rusqlite(#[from] rusqlite::Error), + + #[error("Blockstore error: {0}")] + Blockstore(String), +} + +pub(crate) struct Block { + pub(crate) block_number: u64, + pub(crate) slot_number: u64, + pub(crate) hash: String, + pub(crate) prev_hash: String, + pub(crate) pool_id: String, + pub(crate) leader_vrf: String, + pub(crate) orphaned: bool, +} + +pub(crate) trait BlockStore { + fn save_block(&mut self, pending_blocks: &mut Vec, shelley_genesis_hash: &str) -> Result<(), Error>; + fn load_blocks(&mut self) -> Result)>, Error>; + fn find_block_by_hash(&mut self, hash_start: &str) -> Result, Error>; + fn get_tip_slot_number(&mut self) -> Result; + fn get_eta_v_before_slot(&mut self, slot_number: u64) -> Result, Error>; + fn get_prev_hash_before_slot(&mut self, slot_number: u64) -> Result, Error>; + fn save_slots(&mut self, epoch: u64, pool_id: &str, slot_qty: u64, slots: &str, hash: &str) -> Result<(), Error>; + + /// Get the number of slots and the hash from the block store for the epoch and pool_id + fn get_current_slots(&mut self, epoch: u64, pool_id: &str) -> Result<(u64, String), Error>; + + /// Get the previous slots list raw data String from the block store for the epoch and pool_id + fn get_previous_slots(&mut self, epoch: u64, pool_id: &str) -> Result, Error>; +} diff --git a/src/nodeclient/blockstore/redb.rs b/src/nodeclient/blockstore/redb.rs new file mode 100644 index 0000000..595fb31 --- /dev/null +++ b/src/nodeclient/blockstore/redb.rs @@ -0,0 +1,584 @@ +use crate::nodeclient::blockstore; +use crate::nodeclient::blockstore::{Block, BlockStore}; +use crate::nodeclient::sync::BlockHeader; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_crypto::nonce::generate_rolling_nonce; +use redb::{ + Builder, Database, MultimapTableDefinition, MultimapValue, ReadableMultimapTable, ReadableTable, RepairSession, + TableDefinition, TypeName, Value, +}; +use serde::{Deserialize, Serialize}; +use std::io::Read; +use std::path::Path; +use thiserror::Error; +use tracing::info; +use uuid::Uuid; + +#[derive(Error, Debug)] +pub enum Error { + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + + #[error("Redb error: {0}")] + Redb(#[from] redb::Error), + + #[error("Redb db error: {0}")] + RedbDb(#[from] redb::DatabaseError), + + #[error("Redb commit error: {0}")] + RedbCommit(#[from] redb::CommitError), + + #[error("Redb transaction error: {0}")] + RedbTransaction(#[from] redb::TransactionError), + + #[error("Redb table error: {0}")] + RedbTable(#[from] redb::TableError), + + #[error("Redb storage error: {0}")] + RedbStorage(#[from] redb::StorageError), + + #[error("FromHex error: {0}")] + FromHex(#[from] hex::FromHexError), + + #[error("Data not found")] + DataNotFound, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct ChainRecord { + block_number: u64, + slot_number: u64, + hash: Vec, + prev_hash: Vec, + pool_id: Vec, + eta_v: Vec, + node_vkey: Vec, + node_vrf_vkey: Vec, + block_vrf_0: Vec, + block_vrf_1: Vec, + eta_vrf_0: Vec, + eta_vrf_1: Vec, + leader_vrf_0: Vec, + leader_vrf_1: Vec, + block_size: u64, + block_body_hash: Vec, + pool_opcert: Vec, + unknown_0: u64, + unknown_1: u64, + unknown_2: Vec, + protocol_major_version: u64, + protocol_minor_version: u64, + orphaned: bool, +} + +impl Value for ChainRecord { + type SelfType<'a> = Self; + type AsBytes<'a> = Vec + where + Self: 'a; + + fn fixed_width() -> Option { + // dynamic sized object. not fixed width + None + } + + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + where + Self: 'a, + { + bincode::deserialize(data).unwrap() + } + + fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a> + where + Self: 'a, + Self: 'b, + { + bincode::serialize(value).unwrap() + } + + fn type_name() -> TypeName { + TypeName::new(stringify!(ChainRecord)) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct SlotsRecord { + epoch: u64, + pool_id: Vec, + slot_qty: u64, + slots: String, + hash: Vec, +} + +impl Value for SlotsRecord { + type SelfType<'a> = Self; + type AsBytes<'a> = Vec + where + Self: 'a; + + fn fixed_width() -> Option { + // dynamic sized object. not fixed width + None + } + + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + where + Self: 'a, + { + bincode::deserialize(data).unwrap() + } + + fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a> + where + Self: 'a, + Self: 'b, + { + bincode::serialize(value).unwrap() + } + + fn type_name() -> TypeName { + TypeName::new(stringify!(SlotsRecord)) + } +} + +// magic number must be set to the ASCII letters 'redb' followed by 0x1A, 0x0A, 0xA9, 0x0D, 0x0A. +// This sequence is inspired by the PNG magic number. +const MAGIC_NUMBER: &[u8; 9] = b"redb\x1A\x0A\xA9\x0D\x0A"; + +const VERSION_TABLE: TableDefinition<&str, u16> = TableDefinition::new("version"); +const CHAIN_TABLE: TableDefinition = TableDefinition::new("chain"); +const CHAIN_TABLE_SLOT_INDEX: MultimapTableDefinition = MultimapTableDefinition::new("chain_slot_index"); +const CHAIN_TABLE_HASH_INDEX: MultimapTableDefinition<&[u8], u128> = MultimapTableDefinition::new("chain_hash_index"); +const SLOTS_TABLE: TableDefinition = TableDefinition::new("slots"); +const SLOTS_TABLE_POOL_ID_EPOCH_INDEX: TableDefinition<&[u8], u128> = TableDefinition::new("slots_pool_id_epoch_index"); + +pub(crate) fn is_redb_database(db_path: &Path) -> Result { + let mut file = std::fs::File::open(db_path)?; + let mut magic_number = [0u8; 9]; + file.read_exact(&mut magic_number)?; + Ok(&magic_number == MAGIC_NUMBER) +} + +pub struct RedbBlockStore { + db: Database, +} + +impl RedbBlockStore { + const DB_VERSION: u16 = 1; + + pub fn new(db_path: &Path) -> Result { + let db = Builder::new() + .set_repair_callback(Self::repair_callback) + .create(db_path)?; + Self::migrate(&db)?; + Ok(Self { db }) + } + + pub fn repair_callback(session: &mut RepairSession) { + let progress = session.progress(); + info!("Redb Repair progress: {:?}", progress); + } + + fn migrate(db: &Database) -> Result<(), Error> { + let read_tx = db.begin_read()?; + let current_version = match read_tx.open_table(VERSION_TABLE) { + Ok(version_table) => match version_table.get("version")? { + Some(version) => version.value(), + None => 0, + }, + Err(_) => 0, + }; + + if current_version < Self::DB_VERSION { + // Do migration + let write_tx = db.begin_write()?; + { + let mut version_table = write_tx.open_table(VERSION_TABLE)?; + info!("Migrating database from version 0 to 1"); + version_table.insert("version", Self::DB_VERSION)?; + // create the chain table if it doesn't exist + write_tx.open_table(CHAIN_TABLE)?; + write_tx.open_multimap_table(CHAIN_TABLE_SLOT_INDEX)?; + write_tx.open_multimap_table(CHAIN_TABLE_HASH_INDEX)?; + // create the slots table if it doesn't exist + write_tx.open_table(SLOTS_TABLE)?; + write_tx.open_table(SLOTS_TABLE_POOL_ID_EPOCH_INDEX)?; + } + write_tx.commit()?; + } + + Ok(()) + } + + fn redb_save_block( + &mut self, + pending_blocks: &mut Vec, + shelley_genesis_hash: &str, + ) -> Result<(), Error> { + let first_pending_block_number = pending_blocks.first().unwrap().block_number; + + let write_tx = self.db.begin_write()?; + { + // get the last block eta_v (nonce) in the db + let mut chain_table = write_tx.open_table(CHAIN_TABLE)?; + let mut chain_table_slot_index = write_tx.open_multimap_table(CHAIN_TABLE_SLOT_INDEX)?; + let mut chain_table_hash_index = write_tx.open_multimap_table(CHAIN_TABLE_HASH_INDEX)?; + let mut chain_iter = chain_table.iter()?; + let mut prev_eta_v: Hash<32> = shelley_genesis_hash.parse()?; + let mut to_update: Vec<(u128, ChainRecord)> = Vec::new(); + + while let Some(chain_record) = chain_iter.next_back() { + let (key, chain_record) = chain_record?; + let chain_record: ChainRecord = chain_record.value(); + if chain_record.orphaned { + continue; + } + if chain_record.block_number >= first_pending_block_number && !chain_record.orphaned { + // set it to orphaned + to_update.push(( + key.value(), + ChainRecord { + orphaned: true, + ..chain_record.clone() + }, + )); + continue; + } + prev_eta_v = Hash::from(chain_record.eta_v.as_slice()); + // sanity check + assert_eq!( + chain_record.block_number, + first_pending_block_number - 1, + "block_number: {}, first_pending_block_number: {}", + chain_record.block_number, + first_pending_block_number + ); + break; + } + for (key, chain_record) in to_update { + chain_table.insert(key, chain_record)?; + } + + // save the pending blocks + for block in pending_blocks.drain(..) { + let key = Uuid::now_v7().as_u128(); + + // blake2b 224 of node_vkey is the pool_id + let pool_id = Hasher::<224>::hash(block.node_vkey.as_slice()); + + // calculate rolling nonce (eta_v) + let eta_v = generate_rolling_nonce(prev_eta_v, &block.eta_vrf_0); + + let chain_record = ChainRecord { + block_number: block.block_number, + slot_number: block.slot_number, + hash: block.hash.clone(), + prev_hash: block.prev_hash.clone(), + pool_id: pool_id.to_vec(), + eta_v: eta_v.to_vec(), + node_vkey: block.node_vkey.clone(), + node_vrf_vkey: block.node_vrf_vkey.clone(), + block_vrf_0: block.block_vrf_0.clone(), + block_vrf_1: block.block_vrf_1.clone(), + eta_vrf_0: block.eta_vrf_0.clone(), + eta_vrf_1: block.eta_vrf_1.clone(), + leader_vrf_0: block.leader_vrf_0.clone(), + leader_vrf_1: block.leader_vrf_1.clone(), + block_size: block.block_size, + block_body_hash: block.block_body_hash.clone(), + pool_opcert: block.pool_opcert.clone(), + unknown_0: block.unknown_0, + unknown_1: block.unknown_1, + unknown_2: block.unknown_2.clone(), + protocol_major_version: block.protocol_major_version, + protocol_minor_version: block.protocol_minor_version, + orphaned: false, + }; + chain_table.insert(key, chain_record)?; + chain_table_slot_index.insert(block.slot_number, key)?; + chain_table_hash_index.insert(block.hash.as_slice(), key)?; + + prev_eta_v = eta_v; + } + } + write_tx.commit()?; + + Ok(()) + } + + fn redb_load_blocks(&mut self) -> Result)>, Error> { + let read_tx = self.db.begin_read()?; + // get slot_number and hash from chain table ordering by slot_number descending where orphaned is false + // limit the result to 33 records + let chain_table = read_tx.open_table(CHAIN_TABLE)?; + let mut chain_iter = chain_table.iter()?; + let mut blocks: Vec<(u64, Vec)> = Vec::new(); + while let Some(record) = chain_iter.next_back() { + let (_, chain_record) = record?; + let chain_record: ChainRecord = chain_record.value(); + if chain_record.orphaned { + continue; + } + let slot_number = chain_record.slot_number; + let hash = chain_record.hash.clone(); + blocks.push((slot_number, hash)); + if blocks.len() >= 33 { + break; + } + } + + Ok(blocks) + } + + fn redb_find_block_by_hash(&mut self, hash_start: &str) -> Result, Error> { + let read_tx = self.db.begin_read()?; + let chain_table = read_tx.open_table(CHAIN_TABLE)?; + let mut chain_iter = chain_table.iter()?; + while let Some(record) = chain_iter.next_back() { + let (_, chain_record) = record?; + let chain_record: ChainRecord = chain_record.value(); + if hex::encode(&chain_record.hash).starts_with(hash_start) { + let block = Block { + block_number: chain_record.block_number, + slot_number: chain_record.slot_number, + hash: hex::encode(&chain_record.hash), + prev_hash: hex::encode(&chain_record.prev_hash), + pool_id: hex::encode(&chain_record.pool_id), + leader_vrf: hex::encode(&chain_record.leader_vrf_0), + orphaned: chain_record.orphaned, + }; + return Ok(Some(block)); + } + } + + Ok(None) + } + + fn redb_get_tip_slot_number(&mut self) -> Result { + let read_tx = self.db.begin_read()?; + let chain_table_slot_index = read_tx.open_multimap_table(CHAIN_TABLE_SLOT_INDEX)?; + let mut iter = chain_table_slot_index.iter()?; + if let Some(result) = iter.next_back() { + let (slot_number, _) = result?; + return Ok(slot_number.value()); + } + Ok(0) + } + + fn redb_get_eta_v_before_slot(&mut self, slot_number: u64) -> Result, Error> { + let read_tx = self.db.begin_read()?; + let chain_table_slot_index = read_tx.open_multimap_table(CHAIN_TABLE_SLOT_INDEX)?; + let chain_table = read_tx.open_table(CHAIN_TABLE)?; + for slot_number in (0..slot_number).rev() { + let mut chain_keys: MultimapValue = match chain_table_slot_index.get(slot_number) { + Ok(keys) => { + if !keys.is_empty() { + keys + } else { + continue; + } + } + Err(_) => continue, + }; + let eta_v: Option> = chain_keys.find_map(|key| { + let key = key.ok()?; + let chain_record: ChainRecord = chain_table.get(key.value()).ok()??.value(); + if !chain_record.orphaned { + Some(Hash::<32>::from(chain_record.eta_v.as_slice())) + } else { + None + } + }); + if let Some(eta_v) = eta_v { + return Ok(eta_v); + } + } + + Err(Error::DataNotFound) + } + + fn redb_get_prev_hash_before_slot(&mut self, slot_number: u64) -> Result, Error> { + let read_tx = self.db.begin_read()?; + let chain_table_slot_index = read_tx.open_multimap_table(CHAIN_TABLE_SLOT_INDEX)?; + let chain_table = read_tx.open_table(CHAIN_TABLE)?; + for slot_number in (0..slot_number).rev() { + let mut chain_keys: MultimapValue = match chain_table_slot_index.get(slot_number) { + Ok(keys) => { + if !keys.is_empty() { + keys + } else { + continue; + } + } + Err(_) => continue, + }; + let prev_hash: Option> = chain_keys.find_map(|key| { + let key = key.ok()?.value(); + let chain_record: ChainRecord = chain_table.get(key).ok()??.value(); + if !chain_record.orphaned { + Some(Hash::<32>::from(chain_record.prev_hash.as_slice())) + } else { + None + } + }); + if let Some(prev_hash) = prev_hash { + return Ok(prev_hash); + } + } + + Err(Error::DataNotFound) + } + + fn redb_save_slots( + &mut self, + epoch: u64, + pool_id: &str, + slot_qty: u64, + slots: &str, + hash: &str, + ) -> Result<(), Error> { + // See if record exists already + let mut hasher = Hasher::<224>::new(); + hasher.input(&epoch.to_be_bytes()); + hasher.input(hex::decode(pool_id)?.as_slice()); + let index_key = hasher.finalize(); + + let read_tx = self.db.begin_read()?; + let slots_key = { + let slots_table_pool_id_epoch_index = read_tx.open_table(SLOTS_TABLE_POOL_ID_EPOCH_INDEX)?; + slots_table_pool_id_epoch_index + .get(index_key.as_slice())? + .map(|key| key.value()) + }; + + let write_tx = self.db.begin_write()?; + { + let mut slots_table = write_tx.open_table(SLOTS_TABLE)?; + match slots_key { + Some(key) => { + // Update existing record + let mut slots_record: SlotsRecord = slots_table + .get(key)? + .map(|record| record.value()) + .ok_or(Error::DataNotFound)?; + slots_record.slot_qty = slot_qty; + slots_record.slots = slots.to_string(); + slots_record.hash = hex::decode(hash)?; + slots_table.insert(key, slots_record)?; + } + None => { + // Add new record and index + let mut slots_table_pool_id_epoch_index = write_tx.open_table(SLOTS_TABLE_POOL_ID_EPOCH_INDEX)?; + let key = Uuid::now_v7().as_u128(); + let slots_record = SlotsRecord { + epoch, + pool_id: hex::decode(pool_id)?, + slot_qty, + slots: slots.to_string(), + hash: hex::decode(hash)?, + }; + slots_table.insert(key, slots_record)?; + slots_table_pool_id_epoch_index.insert(index_key.as_slice(), key)?; + } + } + } + write_tx.commit()?; + + Ok(()) + } + + fn redb_get_current_slots(&mut self, epoch: u64, pool_id: &str) -> Result<(u64, String), Error> { + let mut hasher = Hasher::<224>::new(); + hasher.input(&epoch.to_be_bytes()); + hasher.input(hex::decode(pool_id)?.as_slice()); + let index_key = hasher.finalize(); + + let read_tx = self.db.begin_read()?; + let slots_table_pool_id_epoch_index = read_tx.open_table(SLOTS_TABLE_POOL_ID_EPOCH_INDEX)?; + let slots_key = slots_table_pool_id_epoch_index + .get(index_key.as_slice())? + .map(|key| key.value()) + .ok_or(Error::DataNotFound)?; + + let slots_table = read_tx.open_table(SLOTS_TABLE)?; + let slots_record = slots_table + .get(slots_key)? + .map(|record| record.value()) + .ok_or(Error::DataNotFound)?; + + Ok((slots_record.slot_qty, hex::encode(slots_record.hash))) + } + + fn redb_get_previous_slots(&mut self, epoch: u64, pool_id: &str) -> Result, Error> { + let mut hasher = Hasher::<224>::new(); + hasher.input(&epoch.to_be_bytes()); + hasher.input(hex::decode(pool_id)?.as_slice()); + let index_key = hasher.finalize(); + + let read_tx = self.db.begin_read()?; + let slots_table_pool_id_epoch_index = read_tx.open_table(SLOTS_TABLE_POOL_ID_EPOCH_INDEX)?; + if let Some(slots_key) = slots_table_pool_id_epoch_index + .get(index_key.as_slice())? + .map(|key| key.value()) + { + let slots_table = read_tx.open_table(SLOTS_TABLE)?; + let slots_record = slots_table + .get(slots_key)? + .map(|record| record.value()) + .ok_or(Error::DataNotFound)?; + Ok(Some(slots_record.slots)) + } else { + Ok(None) + } + } +} + +impl BlockStore for RedbBlockStore { + fn save_block( + &mut self, + pending_blocks: &mut Vec, + shelley_genesis_hash: &str, + ) -> Result<(), blockstore::Error> { + Ok(self.redb_save_block(pending_blocks, shelley_genesis_hash)?) + } + + fn load_blocks(&mut self) -> Result)>, blockstore::Error> { + Ok(self.redb_load_blocks()?) + } + + fn find_block_by_hash(&mut self, hash_start: &str) -> Result, blockstore::Error> { + Ok(self.redb_find_block_by_hash(hash_start)?) + } + + fn get_tip_slot_number(&mut self) -> Result { + Ok(self.redb_get_tip_slot_number()?) + } + + fn get_eta_v_before_slot(&mut self, slot_number: u64) -> Result, blockstore::Error> { + Ok(self.redb_get_eta_v_before_slot(slot_number)?) + } + + fn get_prev_hash_before_slot(&mut self, slot_number: u64) -> Result, blockstore::Error> { + Ok(self.redb_get_prev_hash_before_slot(slot_number)?) + } + + fn save_slots( + &mut self, + epoch: u64, + pool_id: &str, + slot_qty: u64, + slots: &str, + hash: &str, + ) -> Result<(), blockstore::Error> { + Ok(self.redb_save_slots(epoch, pool_id, slot_qty, slots, hash)?) + } + + fn get_current_slots(&mut self, epoch: u64, pool_id: &str) -> Result<(u64, String), blockstore::Error> { + Ok(self.redb_get_current_slots(epoch, pool_id)?) + } + + fn get_previous_slots(&mut self, epoch: u64, pool_id: &str) -> Result, blockstore::Error> { + Ok(self.redb_get_previous_slots(epoch, pool_id)?) + } +} diff --git a/src/nodeclient/sqlite.rs b/src/nodeclient/blockstore/sqlite.rs similarity index 63% rename from src/nodeclient/sqlite.rs rename to src/nodeclient/blockstore/sqlite.rs index af0418d..3442864 100644 --- a/src/nodeclient/sqlite.rs +++ b/src/nodeclient/blockstore/sqlite.rs @@ -1,28 +1,21 @@ -use std::io; +use crate::nodeclient::blockstore; +use crate::nodeclient::blockstore::{Block, BlockStore}; +use crate::nodeclient::sync::BlockHeader; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_crypto::nonce::generate_rolling_nonce; +use rusqlite::{named_params, Connection, OptionalExtension}; use std::path::Path; - -use blake2b_simd::Params; -use log::{debug, error, info}; -use pallas_crypto::hash::Hash; -use pallas_crypto::nonce::NonceGenerator; -use pallas_crypto::nonce::rolling_nonce::RollingNonceGenerator; -use rusqlite::{Connection, named_params}; +use std::str::FromStr; use thiserror::Error; - -use crate::nodeclient::sync::BlockHeader; +use tracing::{debug, error, info}; #[derive(Error, Debug)] pub enum Error { #[error("SQLite error: {0}")] Sqlite(#[from] rusqlite::Error), - #[error("Nonce error: {0}")] - Nonce(#[from] pallas_crypto::nonce::Error), -} - -pub trait BlockStore { - fn save_block(&mut self, pending_blocks: &mut Vec, shelley_genesis_hash: &str) -> io::Result<()>; - fn load_blocks(&mut self) -> Option)>>; + #[error("FromHex error: {0}")] + FromHex(#[from] hex::FromHexError), } pub struct SqLiteBlockStore { @@ -130,16 +123,9 @@ impl SqLiteBlockStore { info!("{} pool id records to process. Please be patient...", &count); for (i, node_vkey) in vkeys.into_iter().enumerate() { - let vkey = node_vkey.unwrap(); - let node_vkey_bytes = hex::decode(&vkey).unwrap(); - let pool_id = hex::encode( - Params::new() - .hash_length(28) - .to_state() - .update(&node_vkey_bytes) - .finalize() - .as_bytes(), - ); + let vkey = node_vkey?; + let node_vkey_bytes = hex::decode(&vkey)?; + let pool_id = hex::encode(Hasher::<224>::hash(&node_vkey_bytes)); tx.execute( "UPDATE chain SET pool_id=:pool_id WHERE node_vkey=:node_vkey", @@ -202,7 +188,9 @@ impl SqLiteBlockStore { shelley_genesis_hash.to_string() } }, - ).unwrap().as_slice() + ) + .unwrap() + .as_slice(), ); let tx = db.transaction()?; @@ -277,22 +265,16 @@ impl SqLiteBlockStore { shelley_genesis_hash.to_string() } }, - ).unwrap().as_slice() + ) + .unwrap() + .as_slice(), ); } // calculate rolling nonce (eta_v) - let mut rolling_nonce_generator = RollingNonceGenerator::new(prev_eta_v); - rolling_nonce_generator.apply_block(&block.eta_vrf_0)?; - prev_eta_v = rolling_nonce_generator.finalize()?; + let eta_v = generate_rolling_nonce(prev_eta_v, &block.eta_vrf_0); // blake2b 224 of node_vkey is the pool_id - let pool_id = Params::new() - .hash_length(28) - .to_state() - .update(&block.node_vkey) - .finalize() - .as_bytes() - .to_vec(); + let pool_id = hex::encode(Hasher::<224>::hash(&block.node_vkey)); insert_stmt.execute(named_params! { ":block_number" : block.block_number, @@ -300,7 +282,7 @@ impl SqLiteBlockStore { ":hash" : hex::encode(block.hash), ":prev_hash" : hex::encode(block.prev_hash), ":pool_id" : hex::encode(pool_id), - ":eta_v" : hex::encode(prev_eta_v), + ":eta_v" : hex::encode(eta_v), ":node_vkey" : hex::encode(block.node_vkey), ":node_vrf_vkey" : hex::encode(block.node_vrf_vkey), ":block_vrf_0": hex::encode(block.block_vrf_0), @@ -318,36 +300,180 @@ impl SqLiteBlockStore { ":protocol_major_version" : block.protocol_major_version, ":protocol_minor_version" : block.protocol_minor_version, })?; + + prev_eta_v = eta_v; } } tx.commit()?; Ok(()) } -} -impl BlockStore for SqLiteBlockStore { - fn save_block(&mut self, pending_blocks: &mut Vec, shelley_genesis_hash: &str) -> io::Result<()> { - match self.sql_save_block(pending_blocks, shelley_genesis_hash) { - Ok(_) => Ok(()), - Err(error) => Err(io::Error::new(io::ErrorKind::Other, format!("Database error!: {:?}", error))), + fn sql_load_blocks(&mut self) -> Result)>, Error> { + let db = &self.db; + let mut stmt = db + .prepare("SELECT slot_number, hash FROM (SELECT slot_number, hash, orphaned FROM chain ORDER BY slot_number DESC LIMIT 100) WHERE orphaned = 0 ORDER BY slot_number DESC LIMIT 33;")?; + let blocks = stmt.query_map([], |row| { + let slot_result: Result = row.get(0); + let hash_result: Result = row.get(1); + let slot = slot_result?; + let hash = hash_result?; + Ok((slot, hex::decode(hash).unwrap())) + })?; + Ok(blocks.map(|item| item.unwrap()).collect()) + } + + fn sql_find_block_by_hash(&mut self, hash_start: &str) -> Result, Error> { + let db = &self.db; + let like = format!("{hash_start}%"); + Ok(db.query_row( + "SELECT block_number,slot_number,hash,prev_hash,pool_id,leader_vrf_0,orphaned FROM chain WHERE hash LIKE ? ORDER BY orphaned ASC", + [&like], + |row| { + Ok(Some(Block { + block_number: row.get(0)?, + slot_number: row.get(1)?, + hash: row.get(2)?, + prev_hash: row.get(3)?, + pool_id: row.get(4)?, + leader_vrf: row.get(5)?, + orphaned: row.get(6)?, + })) + }, + )?) + } + + fn sql_get_tip_slot_number(&mut self) -> Result { + let db = &self.db; + let tip_slot_number: u64 = db.query_row("SELECT MAX(slot_number) FROM chain", [], |row| row.get(0))?; + Ok(tip_slot_number) + } + + fn sql_get_eta_v_before_slot(&mut self, slot_number: u64) -> Result, Error> { + let db = &self.db; + let eta_v_hex: String = db.query_row( + "SELECT eta_v FROM chain WHERE orphaned = 0 AND slot_number < ?1 ORDER BY slot_number DESC LIMIT 1", + [&slot_number], + |row| row.get(0), + )?; + let eta_v: Hash<32> = Hash::from_str(&eta_v_hex)?; + Ok(eta_v) + } + + fn sql_get_prev_hash_before_slot(&mut self, slot_number: u64) -> Result, Error> { + let db = &self.db; + let prev_hash_hex: String = db.query_row( + "SELECT prev_hash FROM chain WHERE orphaned = 0 AND slot_number < ?1 ORDER BY slot_number DESC LIMIT 1", + [&slot_number], + |row| row.get(0), + )?; + let prev_hash: Hash<32> = Hash::from_str(&prev_hash_hex)?; + Ok(prev_hash) + } + + fn sql_save_slots( + &mut self, + epoch: u64, + pool_id: &str, + slot_qty: u64, + slots: &str, + hash: &str, + ) -> Result<(), Error> { + let db = &mut self.db; + let tx = db.transaction()?; + { + let mut stmt = tx.prepare("INSERT INTO slots (epoch, pool_id, slot_qty, slots, hash) VALUES (:epoch, :pool_id, :slot_qty, :slots, :hash) ON CONFLICT (epoch,pool_id) DO UPDATE SET slot_qty=excluded.slot_qty, slots=excluded.slots, hash=excluded.hash")?; + stmt.execute(named_params! { + ":epoch" : epoch, + ":pool_id" : pool_id, + ":slot_qty" : slot_qty, + ":slots" : slots, + ":hash" : hash, + })?; } + tx.commit()?; + Ok(()) } - fn load_blocks(&mut self) -> Option)>> { + fn sql_get_current_slots(&mut self, epoch: u64, pool_id: &str) -> Result<(u64, String), Error> { let db = &self.db; - let mut stmt = db - .prepare("SELECT slot_number, hash FROM (SELECT slot_number, hash, orphaned FROM chain ORDER BY slot_number DESC LIMIT 100) WHERE orphaned = 0 ORDER BY slot_number DESC LIMIT 33;") - .unwrap(); - let blocks = stmt - .query_map([], |row| { - let slot_result: Result = row.get(0); - let hash_result: Result = row.get(1); - let slot = slot_result?; - let hash = hash_result?; - Ok((slot, hex::decode(hash).unwrap())) - }) - .ok()?; - Some(blocks.map(|item| item.unwrap()).collect()) + let mut stmt = db.prepare("SELECT slot_qty, hash FROM slots WHERE epoch = :epoch AND pool_id = :pool_id")?; + Ok(stmt.query_row( + named_params! { + ":epoch" : epoch, + ":pool_id" : pool_id, + }, + |row| { + let slot_qty: u64 = row.get(0)?; + let hash: String = row.get(1)?; + Ok((slot_qty, hash)) + }, + )?) + } + + fn sql_get_previous_slots(&mut self, epoch: u64, pool_id: &str) -> Result, Error> { + let db = &self.db; + let mut stmt = db.prepare("SELECT slots FROM slots WHERE epoch = :epoch AND pool_id = :pool_id")?; + Ok(stmt + .query_row( + named_params! { + ":epoch" : epoch, + ":pool_id" : pool_id, + }, + |row| { + let slots: String = row.get(0)?; + Ok(slots) + }, + ) + .optional()?) + } +} + +impl BlockStore for SqLiteBlockStore { + fn save_block( + &mut self, + pending_blocks: &mut Vec, + shelley_genesis_hash: &str, + ) -> Result<(), blockstore::Error> { + Ok(self.sql_save_block(pending_blocks, shelley_genesis_hash)?) + } + + fn load_blocks(&mut self) -> Result)>, blockstore::Error> { + Ok(self.sql_load_blocks()?) + } + + fn find_block_by_hash(&mut self, hash_start: &str) -> Result, blockstore::Error> { + Ok(self.sql_find_block_by_hash(hash_start)?) + } + + fn get_tip_slot_number(&mut self) -> Result { + Ok(self.sql_get_tip_slot_number()?) + } + + fn get_eta_v_before_slot(&mut self, slot_number: u64) -> Result, blockstore::Error> { + Ok(self.sql_get_eta_v_before_slot(slot_number)?) + } + + fn get_prev_hash_before_slot(&mut self, slot_number: u64) -> Result, blockstore::Error> { + Ok(self.sql_get_prev_hash_before_slot(slot_number)?) + } + + fn save_slots( + &mut self, + epoch: u64, + pool_id: &str, + slot_qty: u64, + slots: &str, + hash: &str, + ) -> Result<(), blockstore::Error> { + Ok(self.sql_save_slots(epoch, pool_id, slot_qty, slots, hash)?) + } + + fn get_current_slots(&mut self, epoch: u64, pool_id: &str) -> Result<(u64, String), blockstore::Error> { + Ok(self.sql_get_current_slots(epoch, pool_id)?) + } + + fn get_previous_slots(&mut self, epoch: u64, pool_id: &str) -> Result, blockstore::Error> { + Ok(self.sql_get_previous_slots(epoch, pool_id)?) } } diff --git a/src/nodeclient/leaderlog/ledgerstate.rs b/src/nodeclient/leaderlog/ledgerstate.rs index 171e927..057bd76 100644 --- a/src/nodeclient/leaderlog/ledgerstate.rs +++ b/src/nodeclient/leaderlog/ledgerstate.rs @@ -1,11 +1,9 @@ -use num_bigint::BigInt; -use num_rational::BigRational; use std::io::Error; #[derive(Debug)] pub(crate) struct LedgerInfo { pub(crate) sigma: (u64, u64), - pub(crate) decentralization: BigRational, + pub(crate) decentralization: f64, pub(crate) extra_entropy: Option, } @@ -19,7 +17,7 @@ pub(super) fn calculate_ledger_state_sigma_d_and_extra_entropy( // We're assuming d=0 at this point if we're using this new cardano-cli stake-snapshot API Ok(LedgerInfo { sigma: (*pool_stake, *active_stake), - decentralization: BigRational::new(BigInt::from((d * 100.0).round() as u64), BigInt::from(100u64)), + decentralization: *d, extra_entropy: extra_entropy.clone(), }) } diff --git a/src/nodeclient/leaderlog/libsodium.rs b/src/nodeclient/leaderlog/libsodium.rs deleted file mode 100644 index 0a7410c..0000000 --- a/src/nodeclient/leaderlog/libsodium.rs +++ /dev/null @@ -1,75 +0,0 @@ -use thiserror::Error; - -#[link(name = "sodium", kind = "static")] -extern "C" { - // int crypto_vrf_ietfdraft03_prove(unsigned char *proof, const unsigned char *sk, const unsigned char *m, unsigned long long mlen); - fn crypto_vrf_ietfdraft03_prove(proof: *mut u8, sk: *const u8, m: *const u8, mlen: u64) -> i32; - - // int crypto_vrf_ietfdraft03_proof_to_hash(unsigned char *hash, const unsigned char *proof); - fn crypto_vrf_ietfdraft03_proof_to_hash(hash: *mut u8, proof: *const u8) -> i32; - - // int crypto_vrf_ietfdraft03_verify(unsigned char *output, const unsigned char *pk, const unsigned char *proof, const unsigned char *m, unsigned long long mlen) - fn crypto_vrf_ietfdraft03_verify(output: *mut u8, pk: *const u8, proof: *const u8, m: *const u8, mlen: u64) -> i32; -} - -#[derive(Error, Debug)] -pub enum Error { - #[error("{0}")] - Libsodium(String), -} - -pub(crate) fn sodium_crypto_vrf_prove(secret_key: &[u8], seed: &[u8]) -> Result, Error> { - let mut proof: Vec = Vec::with_capacity(80); - unsafe { - let rc = crypto_vrf_ietfdraft03_prove( - proof.as_mut_ptr(), - secret_key.as_ptr(), - seed.as_ptr(), - seed.len() as u64, - ); - if rc != 0 { - Err(Error::Libsodium(format!( - "libsodium crypto_vrf_ietfdraft03_prove() failed, returned {rc}, expected 0" - ))) - } else { - proof.set_len(80); - Ok(proof) - } - } -} - -pub(crate) fn sodium_crypto_vrf_proof_to_hash(proof: &[u8]) -> Result, Error> { - let mut hash: Vec = Vec::with_capacity(64); - unsafe { - let rc = crypto_vrf_ietfdraft03_proof_to_hash(hash.as_mut_ptr(), proof.as_ptr()); - if rc != 0 { - Err(Error::Libsodium(format!( - "libsodium crypto_vrf_ietfdraft03_proof_to_hash() failed, returned {rc}, expected 0" - ))) - } else { - hash.set_len(64); - Ok(hash) - } - } -} - -pub(crate) fn sodium_crypto_vrf_verify(public_key: &[u8], signature: &[u8], seed: &[u8]) -> Result, Error> { - let mut verification: Vec = Vec::with_capacity(64); - unsafe { - let rc = crypto_vrf_ietfdraft03_verify( - verification.as_mut_ptr(), - public_key.as_ptr(), - signature.as_ptr(), - seed.as_ptr(), - seed.len() as u64, - ); - if rc != 0 { - Err(Error::Libsodium(format!( - "libsodium crypto_vrf_ietfdraft03_verify() failed, returned {rc}, expected 0" - ))) - } else { - verification.set_len(64); - Ok(verification) - } - } -} diff --git a/src/nodeclient/leaderlog.rs b/src/nodeclient/leaderlog/mod.rs similarity index 61% rename from src/nodeclient/leaderlog.rs rename to src/nodeclient/leaderlog/mod.rs index 30fe5ba..3696333 100644 --- a/src/nodeclient/leaderlog.rs +++ b/src/nodeclient/leaderlog/mod.rs @@ -4,30 +4,28 @@ use std::io::{stdout, BufReader}; use std::path::Path; use std::str::FromStr; -use bigdecimal::{BigDecimal, FromPrimitive, One, ToPrimitive}; -use blake2b_simd::Params; -use byteorder::{ByteOrder, NetworkEndian}; +use crate::nodeclient::blockstore; +use crate::nodeclient::blockstore::redb::{is_redb_database, RedbBlockStore}; +use crate::nodeclient::blockstore::sqlite::SqLiteBlockStore; +use crate::nodeclient::blockstore::BlockStore; +use crate::nodeclient::leaderlog::deserialize::cbor_hex; +use crate::nodeclient::leaderlog::ledgerstate::calculate_ledger_state_sigma_d_and_extra_entropy; +use crate::{LedgerSet, PooltoolConfig}; use chrono::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Utc}; use chrono_tz::Tz; use itertools::sorted; -use log::{debug, error, info, trace}; -use num_bigint::{BigInt, Sign}; -use num_rational::BigRational; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_crypto::nonce::generate_epoch_nonce; +use pallas_crypto::vrf::{VrfSecretKey, VRF_SECRET_KEY_SIZE}; +use pallas_math::math::{ExpOrdering, FixedDecimal, FixedPrecision, DEFAULT_PRECISION}; use rayon::prelude::*; -use rusqlite::{named_params, Connection, OptionalExtension}; use serde::{Deserialize, Serialize}; use serde_aux::prelude::deserialize_number_from_string; use thiserror::Error; - -use crate::nodeclient::leaderlog::deserialize::cbor_hex; -use crate::nodeclient::leaderlog::ledgerstate::calculate_ledger_state_sigma_d_and_extra_entropy; -use crate::nodeclient::leaderlog::libsodium::{sodium_crypto_vrf_proof_to_hash, sodium_crypto_vrf_prove}; -use crate::nodeclient::math::{ln, normalize, round, taylor_exp_cmp, TaylorCmp}; -use crate::nodeclient::{LedgerSet, PooltoolConfig}; +use tracing::{debug, error, info, span, trace, Level}; mod deserialize; -pub mod ledgerstate; -pub(crate) mod libsodium; +mod ledgerstate; #[derive(Error, Debug)] pub enum Error { @@ -37,20 +35,29 @@ pub enum Error { #[error("JSON error: {0}")] Json(#[from] serde_json::Error), - #[error("SQLite error: {0}")] - Sqlite(#[from] rusqlite::Error), - - #[error("Libsodium error: {0}")] - Libsodium(#[from] libsodium::Error), + #[error("Rusqlite error: {0}")] + Rusqlite(#[from] rusqlite::Error), #[error("FromHex error: {0}")] FromHex(#[from] hex::FromHexError), - #[error("Bigdecimal error: {0}")] - Bigdecimal(#[from] bigdecimal::ParseBigDecimalError), + #[error("PallasMath error: {0}")] + PallasMath(#[from] pallas_math::math::Error), #[error("Leaderlog error: {0}")] Leaderlog(String), + + #[error("Blockstore error: {0}")] + Blockstore(#[from] blockstore::Error), + + #[error("Redb error: {0}")] + Redb(#[from] blockstore::redb::Error), + + #[error("Sqlite error: {0}")] + Sqlite(#[from] blockstore::sqlite::Error), + + #[error("ParseFloat error: {0}")] + ParseFloat(#[from] std::num::ParseFloatError), } #[derive(Debug, Serialize)] @@ -63,7 +70,7 @@ struct LeaderLogError { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct ByronGenesis { - start_time: i64, + start_time: u64, protocol_consts: ProtocolConsts, block_version_data: BlockVersionData, } @@ -71,14 +78,14 @@ struct ByronGenesis { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct ProtocolConsts { - k: i64, + k: u64, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct BlockVersionData { #[serde(deserialize_with = "deserialize_number_from_string")] - slot_duration: i64, + slot_duration: u64, } #[derive(Debug, Deserialize)] @@ -86,8 +93,8 @@ struct BlockVersionData { struct ShelleyGenesis { active_slots_coeff: f64, network_magic: u32, - slot_length: i64, - epoch_length: i64, + slot_length: u64, + epoch_length: u64, } #[derive(Debug, Deserialize)] @@ -103,10 +110,10 @@ pub(crate) struct VrfKey { #[serde(rename_all = "camelCase")] struct LeaderLog { status: String, - epoch: i64, + epoch: u64, epoch_nonce: String, consensus: String, - epoch_slots: i64, + epoch_slots: u64, epoch_slots_ideal: f64, max_performance: f64, pool_id: String, @@ -121,9 +128,9 @@ struct LeaderLog { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct Slot { - no: i64, - slot: i64, - slot_in_epoch: i64, + no: u64, + slot: u64, + slot_in_epoch: u64, at: String, } @@ -132,8 +139,8 @@ struct Slot { struct PooltoolSendSlots { api_key: String, pool_id: String, - epoch: i64, - slot_qty: i64, + epoch: u64, + slot_qty: u64, hash: String, #[serde(skip_serializing_if = "Option::is_none")] override_time: Option, @@ -156,50 +163,7 @@ pub(crate) fn read_vrf_key(vrf_key_path: &Path) -> Result { Ok(serde_json::from_reader(buf)?) } -fn get_tip_slot_number(db: &Connection) -> Result { - db.query_row("SELECT MAX(slot_number) FROM chain", [], |row| row.get(0)) -} - -fn get_eta_v_before_slot(db: &Connection, slot_number: i64) -> Result { - db.query_row( - "SELECT eta_v FROM chain WHERE orphaned = 0 AND slot_number < ?1 ORDER BY slot_number DESC LIMIT 1", - [&slot_number], - |row| row.get(0), - ) -} - -fn get_prev_hash_before_slot(db: &Connection, slot_number: i64) -> Result { - db.query_row( - "SELECT prev_hash FROM chain WHERE orphaned = 0 AND slot_number < ?1 ORDER BY slot_number DESC LIMIT 1", - [&slot_number], - |row| row.get(0), - ) -} - -fn get_current_slots(db: &Connection, epoch: i64, pool_id: &str) -> Result<(i64, String), rusqlite::Error> { - db.query_row( - "SELECT slot_qty, hash FROM slots WHERE epoch = :epoch AND pool_id = :pool_id LIMIT 1", - named_params! { - ":epoch": epoch, - ":pool_id": pool_id, - }, - |row| Ok((row.get(0)?, row.get(1)?)), - ) -} - -fn get_prev_slots(db: &Connection, epoch: i64, pool_id: &str) -> Result, rusqlite::Error> { - db.query_row( - "SELECT slots FROM slots WHERE epoch = :epoch AND pool_id = :pool_id LIMIT 1", - named_params! { - ":epoch": epoch, - ":pool_id": pool_id, - }, - |row| row.get(0), - ) - .optional() -} - -fn guess_shelley_transition_epoch(network_magic: u32) -> i64 { +fn guess_shelley_transition_epoch(network_magic: u32) -> u64 { match network_magic { 764824073 => { // mainnet @@ -232,16 +196,13 @@ fn guess_shelley_transition_epoch(network_magic: u32) -> i64 { } } +/// Calculate the first slot of the epoch and the epoch number for the given slot fn get_first_slot_of_epoch( byron: &ByronGenesis, shelley: &ShelleyGenesis, - current_slot: i64, - shelley_trans_epoch: i64, -) -> (i64, i64) { - let shelley_transition_epoch = match shelley_trans_epoch { - -1 => guess_shelley_transition_epoch(shelley.network_magic), - _ => shelley_trans_epoch, - }; + current_slot: u64, + shelley_transition_epoch: u64, +) -> (u64, u64) { let byron_epoch_length = 10 * byron.protocol_consts.k; let byron_slots = byron_epoch_length * shelley_transition_epoch; let shelley_slots = current_slot - byron_slots; @@ -255,14 +216,12 @@ fn get_first_slot_of_epoch( fn slot_to_naivedatetime( byron: &ByronGenesis, shelley: &ShelleyGenesis, - slot: i64, - shelley_trans_epoch: i64, + slot: u64, + shelley_transition_epoch: u64, ) -> NaiveDateTime { - let shelley_transition_epoch = match shelley_trans_epoch { - -1 => guess_shelley_transition_epoch(shelley.network_magic), - _ => shelley_trans_epoch, - }; - let network_start_time = DateTime::from_timestamp(byron.start_time, 0).unwrap().naive_utc(); + let network_start_time = DateTime::from_timestamp(byron.start_time as i64, 0) + .unwrap() + .naive_utc(); let byron_epoch_length = 10 * byron.protocol_consts.k; let byron_slots = byron_epoch_length * shelley_transition_epoch; let shelley_slots = slot - byron_slots; @@ -270,33 +229,34 @@ fn slot_to_naivedatetime( let byron_secs = (byron.block_version_data.slot_duration * byron_slots) / 1000; let shelley_secs = shelley_slots * shelley.slot_length; - network_start_time + TimeDelta::try_seconds(byron_secs).unwrap() + TimeDelta::try_seconds(shelley_secs).unwrap() + network_start_time + + TimeDelta::try_seconds(byron_secs as i64).unwrap() + + TimeDelta::try_seconds(shelley_secs as i64).unwrap() } fn slot_to_timestamp( byron: &ByronGenesis, shelley: &ShelleyGenesis, - slot: i64, + slot: u64, tz: &Tz, - shelley_trans_epoch: i64, + shelley_transition_epoch: u64, ) -> String { - let slot_time = slot_to_naivedatetime(byron, shelley, slot, shelley_trans_epoch); + let slot_time = slot_to_naivedatetime(byron, shelley, slot, shelley_transition_epoch); tz.from_utc_datetime(&slot_time).to_rfc3339() } -pub fn is_overlay_slot(first_slot_of_epoch: &i64, current_slot: &i64, d: &BigRational) -> bool { - trace!("d: {:?}", &d); - // let diff_slot = Rational::from((current_slot - first_slot_of_epoch).abs()); - let diff_slot: BigRational = BigRational::from_i64((current_slot - first_slot_of_epoch).abs()).unwrap(); - trace!("diff_slot: {:?}", &diff_slot); - //let diff_slot_inc: Rational = Rational::from(&diff_slot + 1); - let diff_slot_inc: BigRational = &diff_slot + BigRational::one(); - trace!("diff_slot_inc: {:?}", &diff_slot_inc); - let left = (d * diff_slot).ceil(); - trace!("left: {:?}", &left); - let right = (d * diff_slot_inc).ceil(); - trace!("right: {:?}", &right); - trace!("is_overlay_slot: {:?} - {:?}", current_slot, left < right); +pub fn is_overlay_slot(first_slot_of_epoch: &u64, current_slot: &u64, d: &f64) -> bool { + let d = FixedDecimal::from((*d * 1000.0).round() as u64) / FixedDecimal::from(1000u64); + trace!("d: {}", &d); + let diff_slot: FixedDecimal = FixedDecimal::from(current_slot - first_slot_of_epoch); + trace!("diff_slot: {}", &diff_slot); + let diff_slot_inc: FixedDecimal = &diff_slot + &FixedDecimal::from(1u64); + trace!("diff_slot_inc: {}", &diff_slot_inc); + let left = (&d * &diff_slot).ceil(); + trace!("left: {}", &left); + let right = (&d * &diff_slot_inc).ceil(); + trace!("right: {}", &right); + trace!("is_overlay_slot: {} - {}", current_slot, left < right); left < right } @@ -310,20 +270,12 @@ const UC_NONCE: [u8; 32] = [ 0xc7, 0xc5, 0xc2, 0xbd, 0x68, 0x28, 0xe1, 0x4a, 0x7d, 0x25, 0xfa, 0x3a, 0x60, ]; -fn mk_seed(slot: i64, eta0: &[u8]) -> Vec { +fn mk_seed(slot: u64, eta0: &[u8]) -> Vec { trace!("mk_seed() start slot {}", slot); - let mut concat = [0u8; 8 + 32]; - NetworkEndian::write_i64(&mut concat, slot); - concat[8..].copy_from_slice(eta0); - trace!("concat: {}", hex::encode(concat)); - - let slot_to_seed = Params::new() - .hash_length(32) - .to_state() - .update(&concat) - .finalize() - .as_bytes() - .to_owned(); + let mut hasher = Hasher::<256>::new(); + hasher.input(&slot.to_be_bytes()); + hasher.input(eta0); + let slot_to_seed = hasher.finalize(); UC_NONCE .iter() @@ -332,49 +284,28 @@ fn mk_seed(slot: i64, eta0: &[u8]) -> Vec { .collect() } -fn mk_input_vrf(slot: i64, eta0: &[u8]) -> Vec { +fn mk_input_vrf(slot: u64, eta0: &[u8]) -> Vec { trace!("mk_seed() start slot {}", slot); - let mut concat = [0u8; 8 + 32]; - NetworkEndian::write_i64(&mut concat, slot); - concat[8..].copy_from_slice(eta0); - trace!("concat: {}", hex::encode(concat)); - - Params::new() - .hash_length(32) - .to_state() - .update(&concat) - .finalize() - .as_bytes() - .to_owned() -} - -fn vrf_eval_certified(seed: &[u8], pool_vrf_skey: &[u8]) -> Result { - let certified_proof: Vec = sodium_crypto_vrf_prove(pool_vrf_skey, seed)?; - let certified_proof_hash: Vec = sodium_crypto_vrf_proof_to_hash(&certified_proof)?; - trace!("certified_proof_hash: {}", hex::encode(&certified_proof_hash)); - Ok(BigInt::from_bytes_be(Sign::Plus, &certified_proof_hash)) -} - -fn vrf_leader_value(raw_vrf: BigInt) -> Result { - let mut concat = vec![0x4C_u8]; // "L" - let mut raw_vrf_bytes = raw_vrf.to_biguint().unwrap().to_bytes_be(); - let pad_nulls = 64 - raw_vrf_bytes.len(); - if pad_nulls > 0 { - let mut null_vec = vec![0_u8; pad_nulls]; - concat.append(&mut null_vec); - } - concat.append(&mut raw_vrf_bytes); - trace!("concat: {}", hex::encode(&concat)); + let mut hasher = Hasher::<256>::new(); + hasher.input(&slot.to_be_bytes()); + hasher.input(eta0); + hasher.finalize().to_vec() +} - Ok(BigInt::from_bytes_be( - Sign::Plus, - Params::new() - .hash_length(32) - .to_state() - .update(&concat) - .finalize() - .as_bytes(), - )) +fn vrf_eval_certified(seed: &[u8], pool_vrf_skey: &[u8]) -> Result, Error> { + let vrf_skey: [u8; VRF_SECRET_KEY_SIZE] = pool_vrf_skey[..VRF_SECRET_KEY_SIZE].try_into().expect("Infallible"); + let vrf_skey: VrfSecretKey = VrfSecretKey::from(&vrf_skey); + let certified_proof = vrf_skey.prove(seed); + let certified_proof_hash = certified_proof.to_hash(); + trace!("certified_proof_hash: {}", hex::encode(certified_proof_hash)); + Ok(certified_proof_hash) +} + +fn vrf_leader_value(raw_vrf: &[u8]) -> Result { + let mut hasher = Hasher::<256>::new(); + hasher.input(vec![0x4C_u8].as_slice()); // "L" + hasher.input(raw_vrf); + Ok(FixedDecimal::from(hasher.finalize().as_slice())) } // Determine if our pool is a slot leader for this given slot @@ -383,34 +314,34 @@ fn vrf_leader_value(raw_vrf: BigInt) -> Result { // @param eta0 The epoch nonce value // @param pool_vrf_skey The vrf signing key for the pool // @param cert_nat_max The value 2^256 -// @param c 1-activeSlotsCoeff - usually 0.95 +// @param c ln(1-activeSlotsCoeff) - usually ln(1-0.05) fn is_slot_leader_praos( - slot: i64, - sigma: &BigDecimal, + slot: u64, + sigma: &FixedDecimal, eta0: &[u8], pool_vrf_skey: &[u8], - cert_nat_max: &BigDecimal, - c: &BigDecimal, + cert_nat_max: &FixedDecimal, + c: &FixedDecimal, ) -> Result { - trace!("is_slot_leader: {}", slot); let seed: Vec = mk_input_vrf(slot, eta0); + let cert_nat: Hash<64> = vrf_eval_certified(&seed, pool_vrf_skey)?; + let cert_leader_vrf: FixedDecimal = vrf_leader_value(cert_nat.as_slice())?; + let denominator = cert_nat_max - &cert_leader_vrf; + let recip_q: FixedDecimal = cert_nat_max / &denominator; + let x: FixedDecimal = -(sigma * c); + let ordering = x.exp_cmp(1000, 3, &recip_q); + + let span = span!(Level::TRACE, "is_slot_leader_praos"); + let _enter = span.enter(); + trace!("is_slot_leader_praos: {}", slot); trace!("seed: {}", hex::encode(&seed)); - let cert_nat: BigInt = vrf_eval_certified(&seed, pool_vrf_skey)?; trace!("cert_nat: {}", &cert_nat); - let cert_leader_vrf: BigInt = vrf_leader_value(cert_nat)?; - trace!("cert_leader_vrf: {}", cert_leader_vrf); - let denominator = cert_nat_max - BigDecimal::from(cert_leader_vrf); - let recip_q: BigDecimal = normalize(cert_nat_max / denominator); + trace!("cert_leader_vrf: {}", &cert_leader_vrf); trace!("recip_q: {}", &recip_q); trace!("c: {}", c); - let x: BigDecimal = round(-c * sigma); trace!("x: {}", &x); - match taylor_exp_cmp(3, &recip_q, &x) { - TaylorCmp::Above => Ok(false), - TaylorCmp::Below => Ok(true), - TaylorCmp::MaxReached => Ok(false), - } + Ok(ordering.estimation == ExpOrdering::LT) } // Determine if our pool is a slot leader for this given slot @@ -421,38 +352,37 @@ fn is_slot_leader_praos( // @param cert_nat_max The value 2^512 // @param c 1-activeSlotsCoeff - usually 0.95 fn is_slot_leader_tpraos( - slot: i64, - sigma: &BigDecimal, + slot: u64, + sigma: &FixedDecimal, eta0: &[u8], pool_vrf_skey: &[u8], - cert_nat_max: &BigDecimal, - c: &BigDecimal, + cert_nat_max: &FixedDecimal, + c: &FixedDecimal, ) -> Result { - trace!("is_slot_leader: {}", slot); let seed: Vec = mk_seed(slot, eta0); + let cert_nat: FixedDecimal = FixedDecimal::from(vrf_eval_certified(&seed, pool_vrf_skey)?.as_slice()); + let denominator = cert_nat_max - &cert_nat; + let recip_q: FixedDecimal = cert_nat_max / &denominator; + let x: FixedDecimal = -(sigma * c); + let ordering = x.exp_cmp(1000, 3, &recip_q); + + let span = span!(Level::TRACE, "is_slot_leader_tpraos"); + let _enter = span.enter(); + trace!("is_slot_leader: {}", slot); trace!("seed: {}", hex::encode(&seed)); - let cert_nat: BigInt = vrf_eval_certified(&seed, pool_vrf_skey)?; trace!("cert_nat: {}", &cert_nat); - let denominator = cert_nat_max - BigDecimal::from(cert_nat); - let recip_q: BigDecimal = normalize(cert_nat_max / denominator); trace!("recip_q: {}", &recip_q); trace!("c: {}", c); - let x: BigDecimal = round(-c * sigma); trace!("x: {}", &x); - match taylor_exp_cmp(3, &recip_q, &x) { - TaylorCmp::Above => Ok(false), - TaylorCmp::Below => Ok(true), - TaylorCmp::MaxReached => Ok(false), - } + Ok(ordering.estimation == ExpOrdering::LT) } -fn get_current_slot(byron: &ByronGenesis, shelley: &ShelleyGenesis, shelley_trans_epoch: &i64) -> Result { - let shelley_transition_epoch = match shelley_trans_epoch { - -1 => guess_shelley_transition_epoch(shelley.network_magic), - _ => *shelley_trans_epoch, - }; - +fn get_current_slot( + byron: &ByronGenesis, + shelley: &ShelleyGenesis, + shelley_transition_epoch: u64, +) -> Result { // read byron genesis values let byron_slot_length = byron.block_version_data.slot_duration; let byron_k = byron.protocol_consts.k; @@ -464,7 +394,7 @@ fn get_current_slot(byron: &ByronGenesis, shelley: &ShelleyGenesis, shelley_tran // read shelley genesis values let slot_length = shelley.slot_length; - let current_time_sec = Utc::now().timestamp(); + let current_time_sec = Utc::now().timestamp() as u64; // Calculate current slot let byron_slots = shelley_transition_epoch * byron_epoch_length; @@ -472,12 +402,7 @@ fn get_current_slot(byron: &ByronGenesis, shelley: &ShelleyGenesis, shelley_tran Ok(byron_slots + shelley_slots) } -fn get_current_epoch(byron: &ByronGenesis, shelley: &ShelleyGenesis, shelley_trans_epoch: &i64) -> i64 { - let shelley_transition_epoch = match shelley_trans_epoch { - -1 => guess_shelley_transition_epoch(shelley.network_magic), - _ => *shelley_trans_epoch, - }; - +fn get_current_epoch(byron: &ByronGenesis, shelley: &ShelleyGenesis, shelley_transition_epoch: u64) -> u64 { // read byron genesis values let byron_slot_length = byron.block_version_data.slot_duration; let byron_k = byron.protocol_consts.k; @@ -491,7 +416,7 @@ fn get_current_epoch(byron: &ByronGenesis, shelley: &ShelleyGenesis, shelley_tra let slot_length = shelley.slot_length; let epoch_length = shelley.epoch_length; - let current_time_sec = Utc::now().timestamp(); + let current_time_sec = Utc::now().timestamp() as u64; shelley_transition_epoch + ((current_time_sec - byron_end_time_sec) / slot_length / epoch_length) } @@ -511,10 +436,11 @@ pub(crate) fn calculate_leader_logs( timezone: &str, is_just_nonce: bool, consensus: &str, - shelley_transition_epoch: &i64, + shelley_transition_epoch: &Option, nonce: &Option, - epoch: &Option, + epoch: &Option, ) -> Result<(), Error> { + debug!("calculate_leader_logs() start"); let tz: Tz = timezone.parse::().unwrap(); if !db_path.exists() { @@ -549,7 +475,14 @@ pub(crate) fn calculate_leader_logs( return Err(Error::Leaderlog(format!("Invalid Consensus: --consensus {consensus}"))); } - let db = Connection::open(db_path)?; + // check if db_path is a redb database based on magic number + let use_redb = is_redb_database(db_path)?; + + let mut block_store: Box = if use_redb { + Box::new(RedbBlockStore::new(db_path)?) + } else { + Box::new(SqLiteBlockStore::new(db_path)?) + }; let byron = read_byron_genesis(byron_genesis)?; debug!("{:?}", byron); @@ -557,6 +490,11 @@ pub(crate) fn calculate_leader_logs( let shelley = read_shelley_genesis(shelley_genesis)?; debug!("{:?}", shelley); + let shelley_transition_epoch = match *shelley_transition_epoch { + None => guess_shelley_transition_epoch(shelley.network_magic), + Some(value) => value, + }; + let ledger_info = calculate_ledger_state_sigma_d_and_extra_entropy(pool_stake, active_stake, d, extra_entropy)?; let tip_slot_number = match nonce { @@ -567,7 +505,7 @@ pub(crate) fn calculate_leader_logs( now_slot_number } None => { - let tip_slot_number = get_tip_slot_number(&db)?; + let tip_slot_number = block_store.get_tip_slot_number()?; debug!("tip_slot_number: {}", tip_slot_number); tip_slot_number } @@ -577,7 +515,7 @@ pub(crate) fn calculate_leader_logs( let epoch_offset = match epoch { Some(epoch) => { - if *epoch > current_epoch || *epoch <= *shelley_transition_epoch { + if *epoch > current_epoch || *epoch <= shelley_transition_epoch { return Err(Error::Leaderlog(format!("Invalid Epoch: --epoch {epoch}, current_epoch: {current_epoch}, shelley_transition_epoch: {shelley_transition_epoch}"))); } current_epoch - *epoch @@ -589,36 +527,26 @@ pub(crate) fn calculate_leader_logs( // pretend we're on a different slot number if we want to calculate past or future epochs. let additional_slots: i64 = match epoch_offset { 0 => match ledger_set { - LedgerSet::Mark => shelley.epoch_length, + LedgerSet::Mark => shelley.epoch_length as i64, LedgerSet::Set => 0, - LedgerSet::Go => -shelley.epoch_length, + LedgerSet::Go => -(shelley.epoch_length as i64), }, - _ => -shelley.epoch_length * epoch_offset, + _ => -((shelley.epoch_length * epoch_offset) as i64), }; let (epoch, first_slot_of_epoch) = get_first_slot_of_epoch( &byron, &shelley, - tip_slot_number + additional_slots, - *shelley_transition_epoch, + (tip_slot_number as i64 + additional_slots) as u64, + shelley_transition_epoch, ); debug!("epoch: {}", epoch); - let epoch_nonce = match nonce { - Some(nonce) => match hex::decode(nonce) { - Ok(nonce) => { - if nonce.len() != 32 { - return Err(Error::Leaderlog(format!("Invalid Nonce: --nonce {:?}", nonce))); - } - nonce - } - Err(_error) => { - return Err(Error::Leaderlog(format!("Invalid Nonce: --nonce {:?}", nonce))); - } - }, + let epoch_nonce: Hash<32> = match nonce { + Some(nonce) => Hash::<32>::from_str(nonce.as_str())?, None => { // Make sure we're fully sync'd - let tip_time = slot_to_naivedatetime(&byron, &shelley, tip_slot_number, *shelley_transition_epoch) + let tip_time = slot_to_naivedatetime(&byron, &shelley, tip_slot_number, shelley_transition_epoch) .and_utc() .timestamp(); let system_time = Utc::now().timestamp(); @@ -632,65 +560,44 @@ pub(crate) fn calculate_leader_logs( debug!("first_slot_of_epoch: {}", first_slot_of_epoch); debug!("first_slot_of_prev_epoch: {}", first_slot_of_prev_epoch); let stability_window_multiplier = match consensus { - "cpraos" => 4i64, - _ => 3i64, + "cpraos" => 4u64, + _ => 3u64, }; - let stability_window: i64 = ((stability_window_multiplier * byron.protocol_consts.k) as f64 + let stability_window = ((stability_window_multiplier * byron.protocol_consts.k) as f64 / shelley.active_slots_coeff) - .ceil() as i64; + .ceil() as u64; let stability_window_start = first_slot_of_epoch - stability_window; debug!("stability_window: {}", stability_window); debug!("stability_window_start: {}", stability_window_start); let stability_window_start_plus_1_min = stability_window_start + 60; - let tip_slot_number = get_tip_slot_number(&db)?; + let tip_slot_number = block_store.get_tip_slot_number()?; if tip_slot_number < stability_window_start_plus_1_min { return Err(Error::Leaderlog(format!( "Not enough blocks sync'd to calculate! Try again later after slot {stability_window_start_plus_1_min} is sync'd." ))); } - let nc = get_eta_v_before_slot(&db, stability_window_start)?; + let nc: Hash<32> = block_store.get_eta_v_before_slot(stability_window_start)?; debug!("nc: {}", nc); - let nh = get_prev_hash_before_slot(&db, first_slot_of_prev_epoch)?; + let nh: Hash<32> = block_store.get_prev_hash_before_slot(first_slot_of_prev_epoch)?; debug!("nh: {}", nh); - let mut nc_nh = String::new(); - nc_nh.push_str(&nc); - nc_nh.push_str(&nh); - let epoch_nonce = Params::new() - .hash_length(32) - .to_state() - .update(&hex::decode(nc_nh)?) - .finalize() - .as_bytes() - .to_owned(); - - match &ledger_info.extra_entropy { - None => epoch_nonce, - Some(entropy) => { - let mut nonce_entropy = String::new(); - nonce_entropy.push_str(&hex::encode(&epoch_nonce)); - nonce_entropy.push_str(entropy); - Params::new() - .hash_length(32) - .to_state() - .update(&hex::decode(nonce_entropy)?) - .finalize() - .as_bytes() - .to_owned() - } - } + debug!("extra_entropy: {:?}", &ledger_info.extra_entropy); + let extra_entropy_vec: Option> = ledger_info + .extra_entropy + .map(|entropy| hex::decode(entropy).expect("Invalid hex string")); + generate_epoch_nonce(nc, nh, extra_entropy_vec.as_deref()) } }; if is_just_nonce { - println!("{}", hex::encode(&epoch_nonce)); + println!("{}", hex::encode(epoch_nonce)); return Ok(()); } - debug!("epoch_nonce: {}", hex::encode(&epoch_nonce)); + debug!("epoch_nonce: {}", hex::encode(epoch_nonce)); let pool_vrf_skey = read_vrf_key(pool_vrf_skey_path)?; if pool_vrf_skey.key_type != "VrfSigningKey_PraosVRF" { @@ -699,31 +606,34 @@ pub(crate) fn calculate_leader_logs( )); } - let sigma = normalize(BigDecimal::from(ledger_info.sigma.0) / BigDecimal::from(ledger_info.sigma.1)); - debug!("sigma: {:?}", &sigma); + let sigma = FixedDecimal::from(ledger_info.sigma.0) / FixedDecimal::from(ledger_info.sigma.1); + debug!("sigma: {}", &sigma); debug!("decentralization_param: {:?}", &ledger_info.decentralization); - debug!("extra_entropy: {:?}", &ledger_info.extra_entropy); - let d: f64 = (ledger_info.decentralization.to_f64().unwrap() * 100.0).round() / 100.0; + let d: f64 = (ledger_info.decentralization * 1000.0).round() / 1000.0; debug!("d: {:?}", &d); - let epoch_slots_ideal = (sigma.to_f64().unwrap() - * (shelley.epoch_length.to_f64().unwrap() * shelley.active_slots_coeff) - * (1.0 - d) - * 100.0) - .round() - / 100.0; + let active_slots_coeff = (shelley.active_slots_coeff * 10000f64) as u64; + let active_slots_coeff = format!("{}000000000000000000000000000000", active_slots_coeff); + let active_slots_coeff = FixedDecimal::from_str(&active_slots_coeff.to_string(), DEFAULT_PRECISION)?; + debug!("active_slots_coeff: {}", &active_slots_coeff); + + let d_multiplier = FixedDecimal::from(((1.0 - d) * 1000.0).round() as u64) / FixedDecimal::from(1000u64); + let epoch_slots_ideal = f64::from_str( + &(&sigma * &(&FixedDecimal::from(shelley.epoch_length) * &active_slots_coeff) * d_multiplier).to_string(), + )?; + let epoch_slots_ideal = (epoch_slots_ideal * 100.0).round() / 100.0; let mut leader_log = LeaderLog { status: "ok".to_string(), epoch, - epoch_nonce: hex::encode(&epoch_nonce), + epoch_nonce: hex::encode(epoch_nonce), consensus: consensus.to_string(), epoch_slots: 0, epoch_slots_ideal, max_performance: 0.0, pool_id: pool_id.to_string(), - sigma: sigma.to_f64().unwrap(), + sigma: f64::from_str(&sigma.to_string())?, active_stake: ledger_info.sigma.0, total_active_stake: ledger_info.sigma.1, d, @@ -731,14 +641,14 @@ pub(crate) fn calculate_leader_logs( assigned_slots: vec![], }; - let cert_nat_max: BigDecimal = match consensus { - "tpraos" => BigDecimal::from_str("13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096")?, // 2^512 - "praos" | "cpraos" => BigDecimal::from_str("115792089237316195423570985008687907853269984665640564039457584007913129639936")?, // 2^256 + let cert_nat_max: FixedDecimal = match consensus { + "tpraos" => FixedDecimal::from_str("134078079299425970995740249982058461274793658205923933777235614437217640300735469768018742981669034276900318581864860508537538828119465699464336490060840960000000000000000000000000000000000", DEFAULT_PRECISION)?, // 2^512 + "praos" | "cpraos" => FixedDecimal::from_str("1157920892373161954235709850086879078532699846656405640394575840079131296399360000000000000000000000000000000000", DEFAULT_PRECISION)?, // 2^256 _ => return Err(Error::Leaderlog(format!( "Invalid Consensus: --consensus {consensus}" ))) }; - let c: BigDecimal = ln(&(BigDecimal::one() - BigDecimal::from_f64(shelley.active_slots_coeff).unwrap())); + let c: FixedDecimal = (FixedDecimal::from(1u64) - active_slots_coeff).ln(); // Calculate all of our assigned slots in the epoch (in parallel) let assigned_slots = (0..shelley.epoch_length) @@ -747,7 +657,14 @@ pub(crate) fn calculate_leader_logs( .filter(|epoch_slot| !is_overlay_slot(&first_slot_of_epoch, epoch_slot, &ledger_info.decentralization)) .filter_map(|leader_slot| match consensus { "tpraos" => { - match is_slot_leader_tpraos(leader_slot, &sigma, &epoch_nonce, &pool_vrf_skey.key, &cert_nat_max, &c) { + match is_slot_leader_tpraos( + leader_slot, + &sigma, + epoch_nonce.as_slice(), + &pool_vrf_skey.key, + &cert_nat_max, + &c, + ) { Ok(true) => Some(leader_slot), Ok(false) => None, Err(msg) => { @@ -757,7 +674,14 @@ pub(crate) fn calculate_leader_logs( } } "praos" | "cpraos" => { - match is_slot_leader_praos(leader_slot, &sigma, &epoch_nonce, &pool_vrf_skey.key, &cert_nat_max, &c) { + match is_slot_leader_praos( + leader_slot, + &sigma, + epoch_nonce.as_slice(), + &pool_vrf_skey.key, + &cert_nat_max, + &c, + ) { Ok(true) => Some(leader_slot), Ok(false) => None, Err(msg) => { @@ -772,12 +696,12 @@ pub(crate) fn calculate_leader_logs( // Update leader log with all assigned slots (sort first) for (i, slot) in sorted(assigned_slots.iter()).enumerate() { - let no = (i + 1) as i64; + let no = (i + 1) as u64; let slot = Slot { no, slot: *slot, slot_in_epoch: slot - first_slot_of_epoch, - at: slot_to_timestamp(&byron, &shelley, *slot, &tz, *shelley_transition_epoch), + at: slot_to_timestamp(&byron, &shelley, *slot, &tz, shelley_transition_epoch), }; debug!("Found assigned slot: {:?}", &slot); @@ -799,38 +723,28 @@ pub(crate) fn calculate_leader_logs( } slots.push(']'); - let hash = hex::encode( - Params::new() - .hash_length(32) - .to_state() - .update(slots.as_ref()) - .finalize() - .as_bytes(), - ); + let hash = Hasher::<256>::hash(slots.as_bytes()).to_string(); - db.execute("INSERT INTO slots (epoch,pool_id,slot_qty,slots,hash) VALUES (:epoch,:pool_id,:slot_qty,:slots,:hash) ON CONFLICT (epoch,pool_id) DO UPDATE SET slot_qty=excluded.slot_qty, slots=excluded.slots, hash=excluded.hash", - named_params! { - ":epoch" : epoch, - ":pool_id" : pool_id, - ":slot_qty" : assigned_slots.len() as i64, - ":slots" : slots, - ":hash" : hash - } - )?; + block_store.save_slots(epoch, pool_id, assigned_slots.len() as u64, slots.as_str(), &hash)?; println!("{}", serde_json::to_string_pretty(&leader_log)?); - db.close().unwrap(); - Ok(()) } -pub(crate) fn status(db_path: &Path, byron_genesis: &Path, shelley_genesis: &Path, shelley_trans_epoch: &i64) { +pub(crate) fn status(db_path: &Path, byron_genesis: &Path, shelley_genesis: &Path, shelley_trans_epoch: &Option) { if !db_path.exists() { handle_error("database not found!"); return; } - let db = Connection::open(db_path).unwrap(); + // check if db_path is a redb database based on magic number + let use_redb = is_redb_database(db_path).expect("infallible"); + + let mut block_store: Box = if use_redb { + Box::new(RedbBlockStore::new(db_path).expect("infallible")) + } else { + Box::new(SqLiteBlockStore::new(db_path).expect("infallible")) + }; match read_byron_genesis(byron_genesis) { Ok(byron) => { @@ -838,13 +752,17 @@ pub(crate) fn status(db_path: &Path, byron_genesis: &Path, shelley_genesis: &Pat match read_shelley_genesis(shelley_genesis) { Ok(shelley) => { debug!("{:?}", shelley); - match get_tip_slot_number(&db) { + match block_store.get_tip_slot_number() { Ok(tip_slot_number) => { debug!("tip_slot_number: {}", tip_slot_number); - let tip_time = - slot_to_naivedatetime(&byron, &shelley, tip_slot_number, *shelley_trans_epoch) - .and_utc() - .timestamp(); + let tip_time = slot_to_naivedatetime( + &byron, + &shelley, + tip_slot_number, + shelley_trans_epoch.expect("infallible"), + ) + .and_utc() + .timestamp(); let system_time = Utc::now().timestamp(); if system_time - tip_time < 120 { print_status_synced(); @@ -860,10 +778,6 @@ pub(crate) fn status(db_path: &Path, byron_genesis: &Path, shelley_genesis: &Pat } Err(error) => handle_error(error), } - - if let Err(error) = db.close() { - handle_error(format!("db close error: {}", error.1)); - } } pub(crate) fn send_slots( @@ -871,14 +785,21 @@ pub(crate) fn send_slots( byron_genesis: &Path, shelley_genesis: &Path, pooltool_config: PooltoolConfig, - shelley_trans_epoch: &i64, + shelley_trans_epoch: &Option, override_time: &Option, ) { if !db_path.exists() { handle_error("database not found!"); return; } - let db = Connection::open(db_path).unwrap(); + // check if db_path is a redb database based on magic number + let use_redb = is_redb_database(db_path).expect("infallible"); + + let mut block_store: Box = if use_redb { + Box::new(RedbBlockStore::new(db_path).expect("infallible")) + } else { + Box::new(SqLiteBlockStore::new(db_path).expect("infallible")) + }; match read_byron_genesis(byron_genesis) { Ok(byron) => { @@ -886,24 +807,32 @@ pub(crate) fn send_slots( match read_shelley_genesis(shelley_genesis) { Ok(shelley) => { debug!("{:?}", shelley); - match get_tip_slot_number(&db) { + match block_store.get_tip_slot_number() { Ok(tip_slot_number) => { debug!("tip_slot_number: {}", tip_slot_number); - let tip_time = - slot_to_naivedatetime(&byron, &shelley, tip_slot_number, *shelley_trans_epoch) - .and_utc() - .timestamp(); + let tip_time = slot_to_naivedatetime( + &byron, + &shelley, + tip_slot_number, + shelley_trans_epoch.expect("infallible"), + ) + .and_utc() + .timestamp(); let system_time = Utc::now().timestamp(); if system_time - tip_time < 120 { - let (epoch, _) = - get_first_slot_of_epoch(&byron, &shelley, tip_slot_number, *shelley_trans_epoch); + let (epoch, _) = get_first_slot_of_epoch( + &byron, + &shelley, + tip_slot_number, + shelley_trans_epoch.expect("infallible"), + ); debug!("epoch: {}", epoch); for pool in pooltool_config.pools.iter() { - match get_current_slots(&db, epoch, &pool.pool_id) { + match block_store.get_current_slots(epoch, &pool.pool_id) { Ok((slot_qty, hash)) => { debug!("slot_qty: {}", slot_qty); debug!("hash: {}", &hash); - match get_prev_slots(&db, epoch - 1, &pool.pool_id) { + match block_store.get_previous_slots(epoch - 1, &pool.pool_id) { Ok(prev_slots) => { let request = serde_json::ser::to_string(&PooltoolSendSlots { api_key: pooltool_config.api_key.clone(), @@ -964,10 +893,6 @@ pub(crate) fn send_slots( } Err(error) => handle_error(error), } - - if let Err(error) = db.close() { - handle_error(format!("db close error: {}", error.1)); - } } fn print_status_synced() { @@ -988,3 +913,70 @@ pub fn handle_error(error_message: T) { ) .unwrap(); } + +#[cfg(test)] +mod tests { + use crate::nodeclient::leaderlog::is_overlay_slot; + use chrono::{NaiveDateTime, Utc}; + + #[test] + fn test_is_overlay_slot() { + let first_slot_of_epoch = 15724800_u64; + let mut current_slot = 16128499_u64; + let d: f64 = 32_f64 / 100_f64; + + assert!(!is_overlay_slot(&first_slot_of_epoch, ¤t_slot, &d)); + + // AD test + current_slot = 15920150_u64; + assert!(is_overlay_slot(&first_slot_of_epoch, ¤t_slot, &d)); + } + + #[test] + fn test_date_parsing() { + let genesis_start_time_sec = NaiveDateTime::parse_from_str("2022-10-25T00:00:00Z", "%Y-%m-%dT%H:%M:%S%.fZ") + .unwrap() + .and_utc() + .timestamp(); + + assert_eq!(genesis_start_time_sec, 1666656000); + } + + #[test] + fn test_date_parsing2() { + let genesis_start_time_sec = + NaiveDateTime::parse_from_str("2024-05-16T17:18:10.000000000Z", "%Y-%m-%dT%H:%M:%S%.fZ") + .unwrap() + .and_utc() + .timestamp(); + + assert_eq!(genesis_start_time_sec, 1715879890); + } + + #[test] + fn test_date_parsing3() { + let genesis_start_time_sec = NaiveDateTime::parse_from_str("2021-12-09T22:55:22Z", "%Y-%m-%dT%H:%M:%S%.fZ") + .unwrap() + .and_utc() + .timestamp(); + assert_eq!(genesis_start_time_sec, 1639090522); + let current_time_sec = Utc::now().timestamp(); + println!("current_time_sec: {}", current_time_sec); + let current_epoch = (current_time_sec - genesis_start_time_sec) / 3600; + println!("current_epoch: {}", current_epoch); + } + + #[test] + fn test_date_parsing_mainnet() { + let genesis_start_time_sec = NaiveDateTime::parse_from_str("2017-09-23T21:44:51Z", "%Y-%m-%dT%H:%M:%S%.fZ") + .unwrap() + .and_utc() + .timestamp(); + + assert_eq!(genesis_start_time_sec, 1506203091); + let current_time_sec = Utc::now().timestamp(); + println!("current_time_sec: {}", current_time_sec); + let current_epoch = (current_time_sec - genesis_start_time_sec) / 432000; + println!("current_epoch: {}", current_epoch); + } +} diff --git a/src/nodeclient/math.rs b/src/nodeclient/math.rs deleted file mode 100644 index 37eb056..0000000 --- a/src/nodeclient/math.rs +++ /dev/null @@ -1,441 +0,0 @@ -use std::cmp::Ordering; -use std::str::FromStr; - -use bigdecimal::{BigDecimal, One, Signed, ToPrimitive, Zero}; -use num_bigint::BigInt; - -// ipow' :: Num a => a -> Integer -> a -// ipow' x n -// | n == 0 = 1 -// | m == 0 = let y = ipow' x d in y * y -// | otherwise = x * ipow' x (n - 1) -// where (d,m) = divMod n 2 -fn ipow_p(x: &BigDecimal, n: i32) -> BigDecimal { - if n == 0 { - return BigDecimal::one(); - } - let d = n / 2_i32; - let m = n % 2_i32; - if m == 0 { - return normalize(ipow_p(x, d).square()); - } - - normalize(x * ipow_p(x, n - 1)) -} - -// ipow :: Fractional a => a -> Integer -> a -// ipow x n -// | n < 0 = 1 / ipow' x (-n) -// | otherwise = ipow' x n -pub fn ipow(x: &BigDecimal, n: i32) -> BigDecimal { - if n < 0 { - normalize(ipow_p(x, -n).inverse()) - } else { - ipow_p(x, n) - } -} - -// logAs :: (Num a) => a -> [a] -// logAs a = a' : a' : logAs (a + 1) -// where -// a' = a * a -// fn log_as(a: &BigDecimal) -> &[BigDecimal] { -// -// } - -// -- | Approximate ln(1+x) for x \in [0, \infty) -// -- a_1 = x, a_{2k} = a_{2k+1} = x·k^2, k >= 1 -// -- b_n = n, n >= 0 -// lncf :: (Fractional a, Enum a, Ord a, Show a) => Int -> a -> a -// lncf maxN x -// | x < 0 = error ("x = " ++ show x ++ " is not inside domain [0,..)") -// | otherwise = cf maxN 0 eps Nothing 1 0 0 1 as [1,2..] -// where as = x : map (*x) (logAs 1) -fn lncf(max_n: i32, x: &BigDecimal) -> BigDecimal { - if x < &BigDecimal::zero() { - panic!("Cannot call lncf with x < 0") - } - - let eps = BigDecimal::from_str("1.E-24").unwrap(); - cf( - max_n, - x, - &eps, - &BigDecimal::one(), - &BigDecimal::zero(), - &BigDecimal::zero(), - &BigDecimal::one(), - ) -} - -// -- | Compute natural logarithm via continued fraction, first splitting integral -// -- part and then using continued fractions approximation for `ln(1+x)` -// ln' :: (RealFrac a, Enum a, Show a) => a -> a -// ln' x -// | x <= 0 = error (show x ++ " is not in domain of ln") -// | otherwise = fromIntegral n + lncf 1000 x' -// where (n, x') = splitLn x -pub fn ln(x: &BigDecimal) -> BigDecimal { - if x <= &BigDecimal::zero() { - panic!("X must be positive, non zero"); - } - - let exp1 = exp(&BigDecimal::one()); - let (n, xp) = split_ln(&exp1, x); - - BigDecimal::from(n) + lncf(1000, &xp) -} - -// -- | Compute continued fraction using max steps or bounded list of a/b factors. -// -- The 'maxN' parameter gives the maximum recursion depth, 'n' gives the current -// -- rursion depth, 'lastVal' is the optional last value ('Nothing' for the first -// -- iteration). 'aNm2' / 'bNm2' are A_{n-2} / B_{n-2}, 'aNm1' / 'bNm1' are -// -- A_{n-1} / B_{n-1}, and 'aN' / 'bN' are A_n / B_n respectively, 'an' / 'bn' -// -- are lists of succecsive a_n / b_n values for the recurrence relation: -// -- -// -- A_{-1} = 1, A_0 = b_0 -// -- B_{-1} = 0, B_0 = 1 -// -- A_n = b_n*A_{n-1} + a_n*A_{n-2} -// -- B_n = b_n*B_{n-1} + a_n*B_{n-2} -// -- -// -- The convergent 'xn' is calculated as x_n = A_n/B_n -// -- -// -- a_1 -// -- result = b_0 + --------------------- -// -- a_2 -// -- b_1 + --------------- -// -- a_3 -// -- b_2 + --------- -// -- . -// -- b_3 + . -// -- . -// -- -// -- The recursion stops once 'maxN' iterations have been reached, or either the -// -- list 'as' or 'bs' is exhausted or 'lastVal' differs less than 'epsilon' from the -// -- new convergent. -// cf :: -// (Fractional a, Ord a, Show a) -// => Int -// -> Int -// -> a -// -> Maybe a -// -> a -// -> a -// -> a -// -> a -// -> [a] -// -> [a] -// -> a -// cf maxN n epsilon lastVal aNm2 bNm2 aNm1 bNm1 (an:as) (bn:bs) -// | maxN == n = xn -// | converges = xn -// | otherwise = cf maxN (n + 1) epsilon (Just xn) aNm1 bNm1 aN bN as bs -// where -// converges = maybe False (\x -> abs (x - xn) < epsilon) lastVal -// xn = aN / bN -- convergent -// aN = bn * aNm1 + an * aNm2 -// bN = bn * bNm1 + an * bNm2 -// cf _ _ _ _ _ _ aN bN _ _ = aN / bN -//fn cf(max_n: i32, n: i32, epsilon: &BigDecimal, last_val: Option<&BigDecimal>, a_nm2: &BigDecimal, b_nm2: &BigDecimal, a_nm1: &BigDecimal, b_nm1: &BigDecimal, mut range: RangeFrom) -> BigDecimal { -// let b_bn = normalize(BigDecimal::from(range.by_ref().take(1).next().unwrap())); -// let a_an = normalize(&b_bn * &b_bn); -// let a_n = normalize(&b_bn * a_nm1 + &a_an * a_nm2); -// let b_n = normalize(&b_bn * b_nm1 + &a_an * b_nm2); -// let xn = normalize(&a_n / &b_n); -// if max_n == n { -// return xn; -// } -// let converges = match last_val { -// None => { false } -// Some(x) => { &(x - &xn).abs() < epsilon } -// }; -// if converges { -// return xn; -// } -// -// return cf(max_n, n + 1, epsilon, Some(&xn), a_nm1, b_nm1, &a_n, &b_n, range); -//} - -// def cf(maxN,x,epsilon,aNm2,bNm2,aNm1,bNm1): -// an = x -// bn = 1 -// aN = bn * aNm1 + an * aNm2 -// bN = bn * bNm1 + an * bNm2 -// aNm2 = aNm1 -// bNm2 = bNm1 -// aNm1 = aN -// bNm1 = bN -// x_ = aN / bN -// for n in range(2,maxN+1): -// if n % 2 == 0: -// an = (n/2)**2 * x -// bn = n -// aN = bn * aNm1 + an * aNm2 -// bN = bn * bNm1 + an * bNm2 -// aNm2 = aNm1 -// bNm2 = bNm1 -// aNm1 = aN -// bNm1 = bN -// xn = aN / bN -// if abs(x_ - xn) < epsilon: -// return xn -// x_ = xn -// return x_ -fn cf( - max_n: i32, - x: &BigDecimal, - epsilon: &BigDecimal, - a_nm_2: &BigDecimal, - b_nm_2: &BigDecimal, - a_nm_1: &BigDecimal, - b_nm_1: &BigDecimal, -) -> BigDecimal { - let mut an = x.clone(); - let mut bn = BigDecimal::one(); - let mut a_n = normalize(&bn * a_nm_1 + &an * a_nm_2); - let mut b_n = normalize(&bn * b_nm_1 + &an * b_nm_2); - let mut a_nm_2 = a_nm_1.clone(); - let mut b_nm_2 = b_nm_1.clone(); - let mut a_nm_1 = a_n.clone(); - let mut b_nm_1 = b_n.clone(); - let mut xp = normalize(&a_n / &b_n); - for n in 2..(max_n + 1) { - if n % 2 == 0 { - an = normalize(BigDecimal::from((n / 2).pow(2)) * x); - } - bn = BigDecimal::from(n); - a_n = normalize(&bn * &a_nm_1 + &an * &a_nm_2); - b_n = normalize(&bn * &b_nm_1 + &an * &b_nm_2); - a_nm_2 = a_nm_1.clone(); - b_nm_2 = b_nm_1.clone(); - a_nm_1 = a_n.clone(); - b_nm_1 = b_n.clone(); - let xn = normalize(&a_n / &b_n); - if (&xp - &xn).abs() < *epsilon { - return xn; - } - xp = xn; - } - - xp -} - -// taylorExp :: (RealFrac a, Show a) => Int -> Int -> a -> a -> a -> a -> a -// taylorExp maxN n x lastX acc divisor -// | maxN == n = acc -// | abs nextX < eps = acc -// | otherwise = taylorExp maxN (n + 1) x nextX (acc + nextX) (divisor + 1) -// where nextX = (lastX * x) / divisor -fn taylor_exp( - eps: &BigDecimal, - max_n: i32, - n: i32, - x: &BigDecimal, - last_x: &BigDecimal, - acc: &BigDecimal, - divisor: &BigDecimal, -) -> BigDecimal { - if max_n == n { - return acc.clone(); - } - let next_x = normalize((last_x * x) / divisor); - if &next_x.abs() < eps { - return acc.clone(); - } - - taylor_exp( - eps, - max_n, - n + 1, - x, - &next_x, - &(acc + &next_x), - &(divisor + BigDecimal::one()), - ) -} - -pub enum TaylorCmp { - Above, - Below, - MaxReached, -} - -pub fn taylor_exp_cmp(bound_x: i32, cmp: &BigDecimal, x: &BigDecimal) -> TaylorCmp { - let max_n: i32 = 1000; - let bound_xf = BigDecimal::from(bound_x); - let mut divisor: i32 = 1; - let mut acc: BigDecimal = BigDecimal::one(); - let mut err: BigDecimal = x.clone(); - let mut error_term: BigDecimal = normalize(&err * &bound_xf); - let mut next_x: BigDecimal; - for _n in 0..max_n { - if cmp >= &normalize(&acc + &error_term) { - return TaylorCmp::Above; - } else if cmp < &normalize(&acc - &error_term) { - return TaylorCmp::Below; - } else { - divisor += 1; - next_x = err.clone(); - err = normalize(normalize(&err * x) / BigDecimal::from(divisor)); - error_term = normalize(&err * &bound_xf); - acc = normalize(&acc + &next_x); - } - } - - TaylorCmp::MaxReached -} - -// splitLn :: (RealFrac a, Show a) => a -> (Integer, a) -// splitLn x = (n, x') -// where n = findE exp1 x -// y' = ipow exp1 n -// x' = (x / y') - 1 -- x / e^n > 1! -pub fn split_ln(exp1: &BigDecimal, x: &BigDecimal) -> (i32, BigDecimal) { - let n = find_e(exp1, x); - let yp = ipow(exp1, n); - let xp = (x / yp) - BigDecimal::one(); - - (n, normalize(xp)) -} - -pub fn ceiling(x: &BigDecimal) -> BigDecimal { - if x.is_integer() { - x.with_scale(0) - } else { - (x + BigDecimal::one()).with_scale(0) - } -} - -// scaleExp :: (RealFrac a) => a -> (Integer, a) -// scaleExp x = (x', x / fromIntegral x') -// where x' = ceiling x -fn scale_exp(x: &BigDecimal) -> (i32, BigDecimal) { - let xp = ceiling(x); - (xp.to_i32().unwrap(), normalize(x / xp)) -} - -// exp' :: (RealFrac a, Show a) => a -> a -// exp' x -// | x < 0 = 1 / exp' (-x) -// | otherwise = ipow x' n -// where (n, x_) = scaleExp x -// x' = taylorExp 1000 1 x_ 1 1 1 -pub fn exp(x: &BigDecimal) -> BigDecimal { - let zero = BigDecimal::zero(); - match x.cmp(&zero) { - Ordering::Equal => BigDecimal::one(), - Ordering::Less => normalize(exp(&-x).inverse()), - Ordering::Greater => { - let (n, x_) = scale_exp(x); - let eps = BigDecimal::from_str("1.E-24").unwrap(); - let xp = taylor_exp( - &eps, - 1000, - 1, - &x_, - &BigDecimal::one(), - &BigDecimal::one(), - &BigDecimal::one(), - ); - ipow(&xp, n) - } - } -} - -// -- | find n with `e^n<=x a -> a -> Integer -// findE e x = contract e x lower upper -// where -// (lower, upper) = bound e x (1/e) e (-1) 1 -pub fn find_e(e: &BigDecimal, x: &BigDecimal) -> i32 { - let (lower, upper) = bound(e, x, &e.inverse(), e, -1, 1); - contract(e, x, lower, upper) -} - -// -- | Simple way to find integer powers that bound x. At every step the bounds -// -- are doubled. Assumption x > 0, the calculated bound is `factor^l <= x <= -// -- factor^u`, initially x' is assumed to be `1/factor` and x'' `factor`, l = -1 -// -- and u = 1. -// bound :: -// (Fractional a, Ord a) -// => a -// -> a -// -> a -// -> a -// -> Integer -// -> Integer -// -> (Integer, Integer) -// bound factor x x' x'' l u -// | x' <= x && x <= x'' = (l, u) -// | otherwise = bound factor x (x' * x') (x'' * x'') (2 * l) (2 * u) -fn bound(_factor: &BigDecimal, x: &BigDecimal, xp: &BigDecimal, xpp: &BigDecimal, l: i32, u: i32) -> (i32, i32) { - if xp <= x && x <= xpp { - (l, u) - } else { - bound(_factor, x, &xp.square(), &xpp.square(), 2 * l, 2 * u) - } -} - -// -- | Bisect bounds to find the smallest integer power such that -// -- `factor^n<=x a -// -> a -// -> Integer -// -> Integer -// -> Integer -// contract factor x = go -// where -// go l u -// | l + 1 == u = l -// | otherwise = -// if x < x' -// then go l mid -// else go mid u -// where -// mid = l + ((u - l) `div` 2) -// x' = ipow factor mid -fn contract(factor: &BigDecimal, x: &BigDecimal, l: i32, u: i32) -> i32 { - if l + 1 == u { - l - } else { - let mid = l + ((u - l) / 2); - let xp = ipow(factor, mid); - if x < &xp { - contract(factor, x, l, mid) - } else { - contract(factor, x, mid, u) - } - } -} - -pub fn normalize(x: BigDecimal) -> BigDecimal { - x.with_scale(34) -} - -pub fn round(x: BigDecimal) -> BigDecimal { - let round_digits = 34_i64; - let (bigint, decimal_part_digits) = &x.as_bigint_and_exponent(); - let need_to_round_digits = decimal_part_digits - round_digits; - if round_digits >= 0 && need_to_round_digits <= 0 { - return x; - } - - let mut number = bigint.clone(); - if number < BigInt::from(0i32) { - number = -number; - } - for _ in 0..(need_to_round_digits - 1) { - number /= 10; - } - let digit = number % 10; - - if digit <= BigInt::from(4i32) { - x.with_scale(round_digits) - } else if bigint.is_negative() { - x.with_scale(round_digits) - BigDecimal::new(BigInt::from(1), round_digits) - } else { - x.with_scale(round_digits) + BigDecimal::new(BigInt::from(1), round_digits) - } -} diff --git a/src/nodeclient/mod.rs b/src/nodeclient/mod.rs new file mode 100644 index 0000000..c02e238 --- /dev/null +++ b/src/nodeclient/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod blockstore; +pub(crate) mod leaderlog; +pub(crate) mod ping; +pub(crate) mod sign; +pub(crate) mod snapshot; +pub(crate) mod sync; +pub(crate) mod validate; diff --git a/src/nodeclient/ping.rs b/src/nodeclient/ping/mod.rs similarity index 65% rename from src/nodeclient/ping.rs rename to src/nodeclient/ping/mod.rs index 358dd48..40f0e69 100644 --- a/src/nodeclient/ping.rs +++ b/src/nodeclient/ping/mod.rs @@ -127,3 +127,73 @@ fn ping_json_error(out: &mut W, message: String, host: &str, port: u16 ) .unwrap(); } + +#[cfg(not(target_os = "windows"))] +#[cfg(test)] +mod tests { + use crate::nodeclient::ping; + use regex::Regex; + + #[tokio::test] + async fn test_ping() { + let host = "preprod-node.play.dev.cardano.org".to_string(); + let port = 30000; + let network_magic = 1; + let mut stdout: Vec = Vec::new(); + + ping::ping(&mut stdout, &host, port, network_magic, 2).await; + + assert_eq!( + &std::str::from_utf8(&stdout).unwrap()[..85], + "{\n \"status\": \"ok\",\n \"host\": \"preprod-node.play.dev.cardano.org\",\n \"port\": 30000,\n " + ); + } + + #[tokio::test] + async fn test_ping_failure_address() { + let host = "murrika.relays-new.cardano-testnet.iohkdev.io".to_string(); + let port = 30000; + let network_magic = 1; + let mut stdout: Vec = Vec::new(); + + ping::ping(&mut stdout, &host, port, network_magic, 2).await; + + let regex_str = ".*failed to lookup address information: .*"; + let regex = Regex::new(regex_str); + let ping_result = std::str::from_utf8(&stdout).unwrap(); + // println!("ping_result: {}", ping_result); + assert_eq!(regex.unwrap().is_match(ping_result), true); + } + + #[tokio::test] + async fn test_ping_failure_bad_port() { + let host = "preprod-node.play.dev.cardano.org".to_string(); + let port = 3992; + let network_magic = 1; + let mut stdout: Vec = Vec::new(); + + ping::ping(&mut stdout, &host, port, network_magic, 2).await; + + let regex_str = ".*connect(ion)? time(out)?.*"; + let regex = Regex::new(regex_str); + let ping_result = std::str::from_utf8(&stdout).unwrap(); + println!("ping_result: {}", ping_result); + assert_eq!(regex.unwrap().is_match(ping_result), true); + } + + #[tokio::test] + async fn test_ping_failure_bad_magic() { + let host = "preview-node.play.dev.cardano.org".to_string(); + let port = 3001; + let network_magic = 111111; + let mut stdout: Vec = Vec::new(); + + ping::ping(&mut stdout, &host, port, network_magic, 2).await; + + let regex_str = ".*\"Refused\\(\\d+, \\\\\"version data mismatch.*"; + let regex = Regex::new(regex_str); + let ping_result = std::str::from_utf8(&stdout).unwrap(); + // println!("ping_result: {}", ping_result); + assert!(regex.unwrap().is_match(ping_result)); + } +} diff --git a/src/nodeclient/sign/mod.rs b/src/nodeclient/sign/mod.rs new file mode 100644 index 0000000..dd4cc02 --- /dev/null +++ b/src/nodeclient/sign/mod.rs @@ -0,0 +1,231 @@ +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_crypto::vrf::{ + VrfProof, VrfPublicKey, VrfSecretKey, VRF_PROOF_SIZE, VRF_PUBLIC_KEY_SIZE, VRF_SECRET_KEY_SIZE, +}; +use rand::{thread_rng, Rng}; +use serde::Serialize; +use std::fmt::Display; +use std::io::stdout; +use std::path::Path; +use tracing::debug; + +use crate::nodeclient::leaderlog::read_vrf_key; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct SignVerifyError { + status: String, + error_message: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct ChallengeSuccess { + status: String, + domain: String, + nonce: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct SignSuccess { + status: String, + signature: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct VerifySuccess { + status: String, +} + +pub(crate) fn create_challenge(domain: &str) -> Option> { + let mut nonce_seed = [0u8; 64]; + thread_rng().fill(&mut nonce_seed); + let nonce = hex::encode(nonce_seed); + match hex::decode(hex::encode("cip-0022".as_bytes()) + &*hex::encode(domain.as_bytes()) + &*nonce) { + Ok(challenge_seed) => { + let challenge = Hasher::<256>::hash(&challenge_seed); + debug!("challenge: {}", hex::encode(challenge)); + serde_json::ser::to_writer_pretty( + &mut stdout(), + &ChallengeSuccess { + status: "ok".to_string(), + domain: domain.to_string(), + nonce, + }, + ) + .unwrap(); + Some(challenge) + } + Err(error) => { + handle_error(error); + None + } + } +} + +pub(crate) fn sign_challenge(pool_vrf_skey: &Path, domain: &str, nonce: &str) { + let challenge_seed = hex::encode("cip-0022".as_bytes()) + &*hex::encode(domain.as_bytes()) + nonce; + match hex::decode(challenge_seed) { + Ok(challenge_seed_bytes) => { + let challenge_bytes = Hasher::<256>::hash(&challenge_seed_bytes); + debug!("challenge: {}", hex::encode(challenge_bytes)); + match read_vrf_key(pool_vrf_skey) { + Ok(vrf_skey) => { + if vrf_skey.key_type != "VrfSigningKey_PraosVRF" { + handle_error("Pool VRF Skey must be of type: VrfSigningKey_PraosVRF"); + return; + } + + let vrf_skey: &[u8; VRF_SECRET_KEY_SIZE] = vrf_skey.key[0..VRF_SECRET_KEY_SIZE] + .try_into() + .expect("Invalid VRF signing key length"); + let vrf_skey = VrfSecretKey::from(vrf_skey); + let vrf_proof = vrf_skey.prove(challenge_bytes.as_slice()); + let signature = vrf_proof.signature(); + debug!("signature: {}", hex::encode(signature)); + serde_json::ser::to_writer_pretty( + &mut stdout(), + &SignSuccess { + status: "ok".to_string(), + signature: hex::encode(signature), + }, + ) + .unwrap(); + } + Err(error) => handle_error(error), + } + } + Err(error) => handle_error(error), + } +} + +pub(crate) fn verify_challenge( + pool_vrf_vkey: &Path, + pool_vrf_vkey_hash: &str, + domain: &str, + nonce: &str, + signature: &str, +) { + let challenge_seed = hex::encode("cip-0022".as_bytes()) + &*hex::encode(domain.as_bytes()) + nonce; + match hex::decode(challenge_seed) { + Ok(challenge_seed_bytes) => { + let challenge_bytes = Hasher::<256>::hash(&challenge_seed_bytes); + debug!("challenge: {}", hex::encode(challenge_bytes)); + match read_vrf_key(pool_vrf_vkey) { + Ok(vrf_vkey) => { + if vrf_vkey.key_type != "VrfVerificationKey_PraosVRF" { + handle_error("Pool VRF Vkey must be of type: VrfVerificationKey_PraosVRF"); + return; + } + // Verify that the vkey the client supplied is the same as the one on-chain + let vkey_hash_verify = hex::encode(Hasher::<224>::hash(&vrf_vkey.key[0..VRF_SECRET_KEY_SIZE])); + debug!("vkey_hash_verify: {}", &vkey_hash_verify); + + if pool_vrf_vkey_hash != vkey_hash_verify { + handle_error(format!( + "Hash of pool-vrf-vkey({vkey_hash_verify}) did not match supplied pool-vrf-vkey-hash({pool_vrf_vkey_hash})" + )); + return; + } + + let vrf_public_key_bytes: [u8; VRF_PUBLIC_KEY_SIZE] = + match vrf_vkey.key[0..VRF_PUBLIC_KEY_SIZE].try_into() { + Ok(slice) => slice, + Err(_) => { + handle_error("Invalid VRF public key length"); + return; + } + }; + + // Verify that the signature is a valid format. This will fail if the signature is mal-formed + match hex::decode(signature) { + Ok(signature_bytes) => { + let signature_slice: [u8; VRF_PROOF_SIZE] = match signature_bytes.as_slice().try_into() { + Ok(slice) => slice, + Err(_) => { + handle_error("Invalid signature length"); + return; + } + }; + let vrf_public_key = VrfPublicKey::from(&vrf_public_key_bytes); + let vrf_proof: VrfProof = VrfProof::from(&signature_slice); + let signature_hash = vrf_proof.to_hash(); + debug!("signature_hash: {}", hex::encode(signature_hash)); + match vrf_proof.verify(&vrf_public_key, challenge_bytes.as_slice()) { + Ok(verification) => { + debug!("verification: {}", hex::encode(verification)); + if verification != signature_hash { + handle_error("Signature failed to match!"); + return; + } + serde_json::ser::to_writer_pretty( + &mut stdout(), + &VerifySuccess { + status: "ok".to_string(), + }, + ) + .unwrap(); + } + Err(error) => handle_error(error), + } + } + Err(error) => handle_error(error), + } + } + Err(error) => handle_error(error), + } + } + Err(error) => handle_error(error), + } +} + +fn handle_error(error_message: T) { + serde_json::ser::to_writer_pretty( + &mut stdout(), + &SignVerifyError { + status: "error".to_string(), + error_message: format!("{error_message}"), + }, + ) + .unwrap(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cip_0022_verification() { + // Node operational VRF-Verification-Key: pool.vrf.vkey + // { + // "type": "VrfVerificationKey_PraosVRF", + // "description": "VRF Verification Key", + // "cborHex": "5820e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381" + // } + // + // Node operational VRF-Signing-Key: pool.vrf.skey + // { + // "type": "VrfSigningKey_PraosVRF", + // "description": "VRF Signing Key", + // "cborHex": "5840adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381" + // } + let vrf_skey_bytes: [u8; VRF_SECRET_KEY_SIZE] = hex::decode("adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381").unwrap().as_slice()[0..VRF_SECRET_KEY_SIZE].try_into().unwrap(); + let vrf_skey: VrfSecretKey = VrfSecretKey::from(&vrf_skey_bytes); + let vrf_vkey_bytes: [u8; VRF_PUBLIC_KEY_SIZE] = + hex::decode("e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381") + .unwrap() + .as_slice()[0..VRF_PUBLIC_KEY_SIZE] + .try_into() + .unwrap(); + let vrf_vkey: VrfPublicKey = VrfPublicKey::from(&vrf_vkey_bytes); + + let challenge = create_challenge("pooltool.io").unwrap(); + let proof = vrf_skey.prove(challenge.as_slice()); + let proof_signature_hash = proof.to_hash(); + let verification_signature_hash = proof.verify(&vrf_vkey, challenge.as_slice()).unwrap(); + + assert_eq!(proof_signature_hash, verification_signature_hash); + } +} diff --git a/src/nodeclient/signing.rs b/src/nodeclient/signing.rs deleted file mode 100644 index 1b578f5..0000000 --- a/src/nodeclient/signing.rs +++ /dev/null @@ -1,200 +0,0 @@ -use std::fmt::Display; -use std::io::stdout; -use std::path::Path; - -use blake2b_simd::Params; -use log::debug; -use rand::{thread_rng, Rng}; -use serde::Serialize; - -use crate::nodeclient::leaderlog::libsodium::{ - sodium_crypto_vrf_proof_to_hash, sodium_crypto_vrf_prove, sodium_crypto_vrf_verify, -}; -use crate::nodeclient::leaderlog::read_vrf_key; - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct SignVerifyError { - status: String, - error_message: String, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct ChallengeSuccess { - status: String, - domain: String, - nonce: String, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct SignSuccess { - status: String, - signature: String, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct VerifySuccess { - status: String, -} - -pub(crate) fn create_challenge(domain: &str) { - let mut nonce_seed = [0u8; 64]; - thread_rng().fill(&mut nonce_seed); - let nonce = hex::encode(nonce_seed); - match hex::decode(hex::encode("cip-0022".as_bytes()) + &*hex::encode(domain.as_bytes()) + &*nonce) { - Ok(challenge_seed) => { - let challenge = Params::new() - .hash_length(32) - .to_state() - .update(&challenge_seed) - .finalize() - .as_bytes() - .to_owned(); - debug!("challenge: {}", hex::encode(challenge)); - serde_json::ser::to_writer_pretty( - &mut stdout(), - &ChallengeSuccess { - status: "ok".to_string(), - domain: domain.to_string(), - nonce, - }, - ) - .unwrap(); - } - Err(error) => handle_error(error), - } -} - -pub(crate) fn sign_challenge(pool_vrf_skey: &Path, domain: &str, nonce: &str) { - let challenge_seed = hex::encode("cip-0022".as_bytes()) + &*hex::encode(domain.as_bytes()) + nonce; - match hex::decode(challenge_seed) { - Ok(challenge_seed_bytes) => { - let challenge_bytes = Params::new() - .hash_length(32) - .to_state() - .update(&challenge_seed_bytes) - .finalize() - .as_bytes() - .to_owned(); - debug!("challenge: {}", hex::encode(&challenge_bytes)); - match read_vrf_key(pool_vrf_skey) { - Ok(vrf_skey) => { - if vrf_skey.key_type != "VrfSigningKey_PraosVRF" { - handle_error("Pool VRF Skey must be of type: VrfSigningKey_PraosVRF"); - return; - } - match sodium_crypto_vrf_prove(&vrf_skey.key, &challenge_bytes) { - Ok(signature) => { - debug!("signature: {}", hex::encode(&signature)); - serde_json::ser::to_writer_pretty( - &mut stdout(), - &SignSuccess { - status: "ok".to_string(), - signature: hex::encode(&signature), - }, - ) - .unwrap(); - } - Err(error) => handle_error(error), - } - } - Err(error) => handle_error(error), - } - } - Err(error) => handle_error(error), - } -} - -pub(crate) fn verify_challenge( - pool_vrf_vkey: &Path, - pool_vrf_vkey_hash: &str, - domain: &str, - nonce: &str, - signature: &str, -) { - let challenge_seed = hex::encode("cip-0022".as_bytes()) + &*hex::encode(domain.as_bytes()) + nonce; - match hex::decode(challenge_seed) { - Ok(challenge_seed_bytes) => { - let challenge_bytes = Params::new() - .hash_length(32) - .to_state() - .update(&challenge_seed_bytes) - .finalize() - .as_bytes() - .to_owned(); - debug!("challenge: {}", hex::encode(&challenge_bytes)); - match read_vrf_key(pool_vrf_vkey) { - Ok(vrf_vkey) => { - if vrf_vkey.key_type != "VrfVerificationKey_PraosVRF" { - handle_error("Pool VRF Vkey must be of type: VrfVerificationKey_PraosVRF"); - return; - } - // Verify that the vkey the client supplied is the same as the one on-chain - let vkey_hash_verify = hex::encode( - Params::new() - .hash_length(32) - .to_state() - .update(&vrf_vkey.key) - .finalize() - .as_bytes(), - ); - debug!("vkey_hash_verify: {}", &vkey_hash_verify); - - if pool_vrf_vkey_hash != vkey_hash_verify { - handle_error(format!( - "Hash of pool-vrf-vkey({vkey_hash_verify}) did not match supplied pool-vrf-vkey-hash({pool_vrf_vkey_hash})" - )); - return; - } - - // Verify that the signature is a valid format. This will fail if the signature is mal-formed - match hex::decode(signature) { - Ok(signature_bytes) => { - match sodium_crypto_vrf_proof_to_hash(&signature_bytes) { - Ok(signature_hash) => { - debug!("signature_hash: {}", hex::encode(&signature_hash)); - // Verify that the signature matches - match sodium_crypto_vrf_verify(&vrf_vkey.key, &signature_bytes, &challenge_bytes) { - Ok(verification) => { - debug!("verification: {}", hex::encode(&verification)); - if verification != signature_hash { - handle_error("Signature failed to match!"); - return; - } - serde_json::ser::to_writer_pretty( - &mut stdout(), - &VerifySuccess { - status: "ok".to_string(), - }, - ) - .unwrap(); - } - Err(error) => handle_error(error), - } - } - Err(error) => handle_error(error), - } - } - Err(error) => handle_error(error), - } - } - Err(error) => handle_error(error), - } - } - Err(error) => handle_error(error), - } -} - -fn handle_error(error_message: T) { - serde_json::ser::to_writer_pretty( - &mut stdout(), - &SignVerifyError { - status: "error".to_string(), - error_message: format!("{error_message}"), - }, - ) - .unwrap(); -} diff --git a/src/nodeclient/snapshot.rs b/src/nodeclient/snapshot/mod.rs similarity index 91% rename from src/nodeclient/snapshot.rs rename to src/nodeclient/snapshot/mod.rs index a57e85b..b1fa830 100644 --- a/src/nodeclient/snapshot.rs +++ b/src/nodeclient/snapshot/mod.rs @@ -2,12 +2,12 @@ use std::io::Write; use std::path::PathBuf; use bech32::{Bech32, Hrp}; -use log::debug; use minicbor::data::Type; use pallas_network::facades::NodeClient; use pallas_network::miniprotocols::localstate::queries_v16::BlockQuery; use pallas_network::miniprotocols::localstate::{queries_v16, ClientError}; use thiserror::Error; +use tracing::debug; use crate::nodeclient::snapshot::Error::UnexpectedCborType; @@ -22,23 +22,20 @@ pub enum Error { #[error("Unexpected array length: expected {expected}, got {actual}")] UnexpectedArrayLength { expected: u64, actual: u64 }, - #[error("Unexpected map length: expected {expected}, got {actual}")] - UnexpectedMapLength { expected: u64, actual: u64 }, - #[error("Unexpected Cbor Type: {value:?}")] UnexpectedCborType { value: Type }, #[error(transparent)] - Bech32Error(#[from] bech32::primitives::hrp::Error), + Bech32(#[from] bech32::primitives::hrp::Error), #[error(transparent)] - Bech32EncodingError(#[from] bech32::EncodeError), + Bech32Encoding(#[from] bech32::EncodeError), #[error(transparent)] - IoError(#[from] std::io::Error), + Io(#[from] std::io::Error), #[error("Snapshot error: {0}")] - SnapshotError(String), + Snapshot(String), } #[derive(Debug)] @@ -63,7 +60,7 @@ pub(crate) async fn dump( "mark" => Snapshot::Mark, "set" => Snapshot::Set, "go" => Snapshot::Go, - _ => return Err(Error::SnapshotError(format!("Unknown snapshot name: {}", name))), + _ => return Err(Error::Snapshot(format!("Unknown snapshot name: {}", name))), }; let client = client.statequery(); @@ -160,7 +157,7 @@ pub(crate) async fn dump( let stake_key_prefix = [match address_type { 0 => 0xe0u8, // key-based stake address 1 => 0xf0u8, // script-based stake address - _ => return Err(Error::SnapshotError(format!("Unknown address type: {}", address_type))), + _ => return Err(Error::Snapshot(format!("Unknown address type: {}", address_type))), } | network_id]; let stake_key_bytes = decoder.bytes()?; let stake_key_bytes = [&stake_key_prefix, stake_key_bytes].concat(); diff --git a/src/nodeclient/sync.rs b/src/nodeclient/sync/mod.rs similarity index 91% rename from src/nodeclient/sync.rs rename to src/nodeclient/sync/mod.rs index bfd99d3..51d3ece 100644 --- a/src/nodeclient/sync.rs +++ b/src/nodeclient/sync/mod.rs @@ -4,9 +4,6 @@ use std::ops::Sub; use std::path::Path; use std::time::{Duration, Instant}; -use thiserror::Error; - -use log::{debug, error, info, warn}; use pallas_network::facades::{KeepAliveLoop, PeerClient, DEFAULT_KEEP_ALIVE_INTERVAL_SEC}; use pallas_network::miniprotocols::chainsync::{HeaderContent, NextResponse, Tip}; use pallas_network::miniprotocols::handshake::Confirmation; @@ -16,40 +13,40 @@ use pallas_network::miniprotocols::{ }; use pallas_network::multiplexer::{Bearer, Plexer}; use pallas_traverse::MultiEraHeader; +use thiserror::Error; +use tracing::{debug, error, info, warn}; -use crate::nodeclient::pooltool; -use crate::nodeclient::sqlite; -use crate::nodeclient::sqlite::BlockStore; +use crate::nodeclient::blockstore; +use crate::nodeclient::blockstore::redb::RedbBlockStore; +use crate::nodeclient::blockstore::sqlite::SqLiteBlockStore; +use crate::nodeclient::blockstore::BlockStore; -use super::sqlite::SqLiteBlockStore; +pub(crate) mod pooltool; const FIVE_SECS: Duration = Duration::from_secs(5); #[derive(Error, Debug)] pub enum Error { - #[error("loggingobserver error occurred")] - LoggingObserverError(String), - #[error("pallas_traverse error occurred: {0}")] - PallasTraverseError(#[from] pallas_traverse::Error), + PallasTraverse(#[from] pallas_traverse::Error), #[error("io error occurred: {0}")] - IoError(#[from] std::io::Error), + Io(#[from] std::io::Error), #[error("keepalive error occurred: {0}")] - KeepAliveError(#[from] keepalive::ClientError), + KeepAlive(#[from] keepalive::ClientError), #[error("chainsync error occurred: {0}")] - ChainSyncError(#[from] chainsync::ClientError), + ChainSync(#[from] chainsync::ClientError), - #[error("chainsync canceled")] - ChainSyncCanceled, + #[error("blockstore error occurred: {0}")] + BlockStore(#[from] blockstore::Error), } #[derive(Debug, Clone)] -pub struct BlockHeader { - pub block_number: i64, - pub slot_number: i64, +pub(crate) struct BlockHeader { + pub block_number: u64, + pub slot_number: u64, pub hash: Vec, pub prev_hash: Vec, pub node_vkey: Vec, @@ -60,14 +57,14 @@ pub struct BlockHeader { pub eta_vrf_1: Vec, pub leader_vrf_0: Vec, pub leader_vrf_1: Vec, - pub block_size: i64, + pub block_size: u64, pub block_body_hash: Vec, pub pool_opcert: Vec, - pub unknown_0: i64, - pub unknown_1: i64, + pub unknown_0: u64, + pub unknown_1: u64, pub unknown_2: Vec, - pub protocol_major_version: i64, - pub protocol_minor_version: i64, + pub protocol_major_version: u64, + pub protocol_minor_version: u64, } struct LoggingObserver { @@ -123,8 +120,8 @@ impl Observer for LoggingObserver { MultiEraHeader::ShelleyCompatible(header) => { //sqlite only handles signed values so some casting is done here self.pending_blocks.push(BlockHeader { - block_number: header.header_body.block_number as i64, - slot_number: slot as i64, + block_number: header.header_body.block_number, + slot_number: slot, hash: hash.to_vec(), prev_hash: match header.header_body.prev_hash { None => vec![], @@ -138,14 +135,14 @@ impl Observer for LoggingObserver { eta_vrf_1: header.header_body.nonce_vrf.1.to_vec(), leader_vrf_0: leader_vrf_output, leader_vrf_1: header.header_body.leader_vrf.1.to_vec(), - block_size: header.header_body.block_body_size as i64, + block_size: header.header_body.block_body_size, block_body_hash: header.header_body.block_body_hash.to_vec(), pool_opcert: header.header_body.operational_cert_hot_vkey.to_vec(), - unknown_0: header.header_body.operational_cert_sequence_number as i64, - unknown_1: header.header_body.operational_cert_kes_period as i64, + unknown_0: header.header_body.operational_cert_sequence_number, + unknown_1: header.header_body.operational_cert_kes_period, unknown_2: header.header_body.operational_cert_sigma.to_vec(), - protocol_major_version: header.header_body.protocol_major as i64, - protocol_minor_version: header.header_body.protocol_minor as i64, + protocol_major_version: header.header_body.protocol_major, + protocol_minor_version: header.header_body.protocol_minor, }); let block_number: f64 = header.header_body.block_number as f64; let tip_block_number: f64 = tip.1 as f64; @@ -174,8 +171,8 @@ impl Observer for LoggingObserver { MultiEraHeader::BabbageCompatible(header) => { //sqlite only handles signed values so some casting is done here self.pending_blocks.push(BlockHeader { - block_number: header.header_body.block_number as i64, - slot_number: slot as i64, + block_number: header.header_body.block_number, + slot_number: slot, hash: hash.to_vec(), prev_hash: match header.header_body.prev_hash { None => vec![], @@ -189,15 +186,14 @@ impl Observer for LoggingObserver { eta_vrf_1: vec![], leader_vrf_0: leader_vrf_output, leader_vrf_1: vec![], - block_size: header.header_body.block_body_size as i64, + block_size: header.header_body.block_body_size, block_body_hash: header.header_body.block_body_hash.to_vec(), pool_opcert: header.header_body.operational_cert.operational_cert_hot_vkey.to_vec(), - unknown_0: header.header_body.operational_cert.operational_cert_sequence_number - as i64, - unknown_1: header.header_body.operational_cert.operational_cert_kes_period as i64, + unknown_0: header.header_body.operational_cert.operational_cert_sequence_number, + unknown_1: header.header_body.operational_cert.operational_cert_kes_period, unknown_2: header.header_body.operational_cert.operational_cert_sigma.to_vec(), - protocol_major_version: header.header_body.protocol_version.0 as i64, - protocol_minor_version: header.header_body.protocol_version.1 as i64, + protocol_major_version: header.header_body.protocol_version.0, + protocol_minor_version: header.header_body.protocol_version.1, }); let block_number: f64 = header.header_body.block_number as f64; let tip_block_number = max(header.header_body.block_number, tip.1); @@ -257,24 +253,20 @@ impl Observer for LoggingObserver { } } -fn get_intersect_blocks(block_store: &mut SqLiteBlockStore) -> Result, Error> { +fn get_intersect_blocks(block_store: &mut Box) -> Result, Error> { let start = Instant::now(); debug!("get_intersect_blocks"); let mut chain_blocks: Vec = vec![]; /* Classic sync: Use blocks from store if available. */ - match block_store.load_blocks() { - None => {} - Some(blocks) => { - for (i, block) in blocks.iter().enumerate() { - // all powers of 2 including 0th element 0, 2, 4, 8, 16, 32 - if (i == 0) || ((i > 1) && (i & (i - 1) == 0)) { - chain_blocks.push(Point::Specific(block.0 as u64, block.1.clone())); - } - } + let blocks = block_store.load_blocks()?; + for (i, (slot, hash)) in blocks.iter().enumerate() { + // all powers of 2 including 0th element 0, 2, 4, 8, 16, 32 + if (i == 0) || ((i > 1) && (i & (i - 1) == 0)) { + chain_blocks.push(Point::Specific(*slot, hash.clone())); } - }; + } // add known points chain_blocks.push( @@ -363,10 +355,15 @@ pub(crate) async fn sync( network_magic: u64, shelley_genesis_hash: &str, no_service: bool, + use_redb: bool, ) { loop { // Retry to establish connection forever - let mut block_store = sqlite::SqLiteBlockStore::new(db).unwrap(); + let mut block_store: Box = if use_redb { + Box::new(RedbBlockStore::new(db).unwrap()) + } else { + Box::new(SqLiteBlockStore::new(db).unwrap()) + }; let chain_blocks = get_intersect_blocks(&mut block_store).unwrap(); match Bearer::connect_tcp_timeout( &format!("{host}:{port}").to_socket_addrs().unwrap().next().unwrap(), @@ -421,7 +418,7 @@ pub(crate) async fn sync( false, no_service, Some(chain_blocks), - Some(Box::new(block_store)), + Some(block_store), shelley_genesis_hash, ) .await diff --git a/src/nodeclient/pooltool.rs b/src/nodeclient/sync/pooltool.rs similarity index 76% rename from src/nodeclient/pooltool.rs rename to src/nodeclient/sync/pooltool.rs index e63bfde..bd34eef 100644 --- a/src/nodeclient/pooltool.rs +++ b/src/nodeclient/sync/pooltool.rs @@ -1,16 +1,38 @@ +use std::fs::File; +use std::io::BufReader; use std::ops::Sub; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::time::{Duration, Instant}; +use crate::nodeclient::blockstore; +use crate::nodeclient::blockstore::{Block, BlockStore, Error}; +use crate::nodeclient::sync::BlockHeader; +use crate::APP_USER_AGENT; use chrono::{SecondsFormat, Utc}; -use log::{error, info}; +use pallas_crypto::hash::Hash; use regex::Regex; -use serde::Serialize; +use serde::{Deserialize, Serialize}; +use tracing::{error, info}; -use crate::nodeclient::sqlite::BlockStore; -use crate::nodeclient::sync::BlockHeader; -use crate::nodeclient::APP_USER_AGENT; +pub(crate) fn get_pooltool_config(config: &Path) -> PooltoolConfig { + let buf = BufReader::new(File::open(config).unwrap()); + serde_json::from_reader(buf).unwrap() +} + +#[derive(Debug, Deserialize)] +pub(crate) struct PooltoolConfig { + pub(crate) api_key: String, + pub(crate) pools: Vec, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct Pool { + pub(crate) name: String, + pub(crate) pool_id: String, + pub(crate) host: String, + pub(crate) port: u16, +} #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -26,15 +48,15 @@ struct PooltoolData0 { node_id: String, version: String, at: String, - block_no: i64, - slot_no: i64, + block_no: u64, + slot_no: u64, block_hash: String, parent_hash: String, leader_vrf: String, leader_vrf_proof: String, node_v_key: String, - protocol_major_version: i64, - protocol_minor_version: i64, + protocol_major_version: u64, + protocol_minor_version: u64, platform: String, } @@ -52,16 +74,16 @@ struct PooltoolData1 { node_id: String, version: String, at: String, - block_no: i64, - slot_no: i64, + block_no: u64, + slot_no: u64, block_hash: String, parent_hash: String, leader_vrf: String, block_vrf: String, block_vrf_proof: String, node_v_key: String, - protocol_major_version: i64, - protocol_minor_version: i64, + protocol_major_version: u64, + protocol_minor_version: u64, platform: String, } @@ -207,12 +229,47 @@ impl BlockStore for PoolToolNotifier { &mut self, pending_blocks: &mut Vec, _shelley_genesis_hash: &str, - ) -> std::io::Result<()> { + ) -> Result<(), blockstore::Error> { self.send_to_pooltool(pending_blocks.last().unwrap()); Ok(()) } - fn load_blocks(&mut self) -> Option)>> { - None + fn load_blocks(&mut self) -> Result)>, Error> { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn find_block_by_hash(&mut self, _hash_start: &str) -> Result, Error> { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn get_tip_slot_number(&mut self) -> Result { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn get_eta_v_before_slot(&mut self, _slot_number: u64) -> Result, Error> { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn get_prev_hash_before_slot(&mut self, _slot_number: u64) -> Result, Error> { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn save_slots( + &mut self, + _epoch: u64, + _pool_id: &str, + _slot_qty: u64, + _slots: &str, + _hash: &str, + ) -> Result<(), Error> { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn get_current_slots(&mut self, _epoch: u64, _pool_id: &str) -> Result<(u64, String), Error> { + Err(Error::Blockstore("Not implemented".to_string())) + } + + fn get_previous_slots(&mut self, _epoch: u64, _pool_id: &str) -> Result, Error> { + Err(Error::Blockstore("Not implemented".to_string())) } } diff --git a/src/nodeclient/validate.rs b/src/nodeclient/validate.rs deleted file mode 100644 index f8cdb24..0000000 --- a/src/nodeclient/validate.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::path::Path; - -use rusqlite::{Connection, Error}; - -struct Block { - block_number: i64, - slot_number: i64, - hash: String, - prev_hash: String, - pool_id: String, - leader_vrf: String, - orphaned: bool, -} - -pub fn validate_block(db_path: &Path, hash: &str) { - let like = format!("{hash}%"); - match query_block(db_path, like) { - Ok(block) => { - println!( - "{{\n\ - \x20\"status\": \"{}\",\n\ - \x20\"block_number\": \"{}\",\n\ - \x20\"slot_number\": \"{}\",\n\ - \x20\"pool_id\": \"{}\",\n\ - \x20\"hash\": \"{}\",\n\ - \x20\"prev_hash\": \"{}\",\n\ - \x20\"leader_vrf\": \"{}\"\n\ - }}", - if block.orphaned { "orphaned" } else { "ok" }, - block.block_number, - block.slot_number, - block.pool_id, - block.hash, - block.prev_hash, - block.leader_vrf - ); - } - Err(error) => { - println!( - "{{\n\ - \x20\"status\": \"error\",\n\ - \x20\"errorMessage\": \"{error}\"\n\ - }}" - ); - } - } -} - -fn query_block(db_path: &Path, like: String) -> Result { - if !db_path.exists() { - return Err(Error::InvalidPath(db_path.to_path_buf())); - } - - let db = Connection::open(db_path)?; - let query_result = db.query_row( - "SELECT block_number,slot_number,hash,prev_hash,pool_id,leader_vrf_0,orphaned FROM chain WHERE hash LIKE ? ORDER BY orphaned ASC", - [&like], - |row| { - Ok(Block { - block_number: row.get(0)?, - slot_number: row.get(1)?, - hash: row.get(2)?, - prev_hash: row.get(3)?, - pool_id: row.get(4)?, - leader_vrf: row.get(5)?, - orphaned: row.get(6)?, - }) - }, - ); - - if let Err(error) = db.close() { - return Err(error.1); - } - - query_result -} diff --git a/src/nodeclient/validate/mod.rs b/src/nodeclient/validate/mod.rs new file mode 100644 index 0000000..313ffdc --- /dev/null +++ b/src/nodeclient/validate/mod.rs @@ -0,0 +1,82 @@ +use std::path::Path; + +use thiserror::Error; + +use crate::nodeclient::blockstore::redb::{is_redb_database, RedbBlockStore}; +use crate::nodeclient::blockstore::sqlite::SqLiteBlockStore; +use crate::nodeclient::blockstore::{Block, BlockStore}; + +#[derive(Debug, Error)] +pub enum Error { + #[error("Invalid path: {0}")] + InvalidPath(std::path::PathBuf), + + #[error("Redb error: {0}")] + Redb(#[from] crate::nodeclient::blockstore::redb::Error), + + #[error("Sqlite error: {0}")] + Sqlite(#[from] crate::nodeclient::blockstore::sqlite::Error), + + #[error("Blockstore error: {0}")] + Blockstore(#[from] crate::nodeclient::blockstore::Error), +} + +pub fn validate_block(db_path: &Path, hash: &str) { + let like = format!("{hash}%"); + match query_block(db_path, like) { + Ok(block) => match block { + Some(block) => { + println!( + "{{\n\ + \x20\"status\": \"{}\",\n\ + \x20\"block_number\": \"{}\",\n\ + \x20\"slot_number\": \"{}\",\n\ + \x20\"pool_id\": \"{}\",\n\ + \x20\"hash\": \"{}\",\n\ + \x20\"prev_hash\": \"{}\",\n\ + \x20\"leader_vrf\": \"{}\"\n\ + }}", + if block.orphaned { "orphaned" } else { "ok" }, + block.block_number, + block.slot_number, + block.pool_id, + block.hash, + block.prev_hash, + block.leader_vrf, + ); + } + None => { + println!( + "{{\n\ + \x20\"status\": \"error\",\n\ + \x20\"errorMessage\": \"Block not found\"\n\ + }}" + ); + } + }, + Err(error) => { + println!( + "{{\n\ + \x20\"status\": \"error\",\n\ + \x20\"errorMessage\": \"{error}\"\n\ + }}" + ); + } + } +} + +fn query_block(db_path: &Path, hash_start: String) -> Result, Error> { + if !db_path.exists() { + return Err(Error::InvalidPath(db_path.to_path_buf())); + } + // check if db_path is a redb database based on magic number + let use_redb = is_redb_database(db_path)?; + + let mut block_store: Box = if use_redb { + Box::new(RedbBlockStore::new(db_path)?) + } else { + Box::new(SqLiteBlockStore::new(db_path)?) + }; + + Ok(block_store.find_block_by_hash(&hash_start)?) +} diff --git a/src/test.rs b/src/test.rs deleted file mode 100644 index 83a7f74..0000000 --- a/src/test.rs +++ /dev/null @@ -1,261 +0,0 @@ -use std::str::FromStr; - -use bigdecimal::{BigDecimal, One, Zero}; -use chrono::{NaiveDateTime, Utc}; -use num_rational::BigRational; -use regex::Regex; - -use cncli::nodeclient::math::{ceiling, exp, find_e, ln, round, split_ln}; -use cncli::nodeclient::ping; -use nodeclient::leaderlog::is_overlay_slot; -use nodeclient::math::ipow; - -use super::*; - -#[test] -fn test_is_overlay_slot() { - pretty_env_logger::init_timed(); - - let first_slot_of_epoch = 15724800_i64; - let mut current_slot = 16128499_i64; - let d = BigRational::from_str("32/100").unwrap(); - - assert!(!is_overlay_slot(&first_slot_of_epoch, ¤t_slot, &d)); - - // AD test - current_slot = 15920150_i64; - assert!(is_overlay_slot(&first_slot_of_epoch, ¤t_slot, &d)); -} - -#[tokio::test] -async fn test_ping() { - let host = "preprod-node.play.dev.cardano.org".to_string(); - let port = 30000; - let network_magic = 1; - let mut stdout: Vec = Vec::new(); - - ping::ping(&mut stdout, &host, port, network_magic, 2).await; - - assert_eq!( - &std::str::from_utf8(&stdout).unwrap()[..85], - "{\n \"status\": \"ok\",\n \"host\": \"preprod-node.play.dev.cardano.org\",\n \"port\": 30000,\n " - ); -} - -#[tokio::test] -async fn test_ping_failure_address() { - let host = "murrika.relays-new.cardano-testnet.iohkdev.io".to_string(); - let port = 30000; - let network_magic = 1; - let mut stdout: Vec = Vec::new(); - - ping::ping(&mut stdout, &host, port, network_magic, 2).await; - - let regex_str = ".*failed to lookup address information: .*"; - let regex = Regex::new(regex_str); - let ping_result = std::str::from_utf8(&stdout).unwrap(); - // println!("ping_result: {}", ping_result); - assert_eq!(regex.unwrap().is_match(ping_result), true); -} - -#[tokio::test] -async fn test_ping_failure_bad_port() { - let host = "preprod-node.play.dev.cardano.org".to_string(); - let port = 3992; - let network_magic = 1; - let mut stdout: Vec = Vec::new(); - - ping::ping(&mut stdout, &host, port, network_magic, 2).await; - - let regex_str = ".*connect(ion)? time(out)?.*"; - let regex = Regex::new(regex_str); - let ping_result = std::str::from_utf8(&stdout).unwrap(); - println!("ping_result: {}", ping_result); - assert_eq!(regex.unwrap().is_match(ping_result), true); -} - -#[tokio::test] -async fn test_ping_failure_bad_magic() { - let host = "preview-node.play.dev.cardano.org".to_string(); - let port = 3001; - let network_magic = 111111; - let mut stdout: Vec = Vec::new(); - - ping::ping(&mut stdout, &host, port, network_magic, 2).await; - - let regex_str = ".*\"Refused\\(\\d+, \\\\\"version data mismatch.*"; - let regex = Regex::new(regex_str); - let ping_result = std::str::from_utf8(&stdout).unwrap(); - // println!("ping_result: {}", ping_result); - assert_eq!(regex.unwrap().is_match(ping_result), true); -} - -#[test] -fn test_eps() { - let eps = BigDecimal::from_str("1.E-24").unwrap(); - // println!("1/10^24 = {}", eps); - assert_eq!(eps.to_string(), "1E-24"); -} - -#[test] -fn test_ceiling() { - let x = BigDecimal::from_str("1234.00000").unwrap(); - let ceil_x = ceiling(&x); - assert_eq!(ceil_x, BigDecimal::from(1234)); - - let y = BigDecimal::from_str("1234.0000000000123").unwrap(); - let ceil_y = ceiling(&y); - assert_eq!(ceil_y, BigDecimal::from(1235)) -} - -#[test] -fn test_exp() { - let x = BigDecimal::zero(); - let exp_x = exp(&x); - assert_eq!(exp_x, BigDecimal::one()); - - let x = BigDecimal::one(); - let exp_x = exp(&x); - assert_eq!(exp_x.to_string(), "2.7182818284590452353602874043083282"); - - let x = BigDecimal::from_str("-54.268914").unwrap(); - let exp_x = exp(&x); - assert_eq!(exp_x.to_string(), "2.6996664594E-24"); -} - -#[test] -fn test_find_e() { - let exp1 = exp(&BigDecimal::one()); - let x = BigDecimal::from_str("8.5").unwrap(); - let n = find_e(&exp1, &x); - println!("find_e({}) = {}", &x, n); - assert_eq!(n, 2); -} - -#[test] -fn test_split_ln() { - let exp1 = exp(&BigDecimal::one()); - let x = BigDecimal::from_str("2.9").unwrap(); - let (n, xp) = split_ln(&exp1, &x); - println!("n: {}, xp: {}", n, xp); -} - -#[test] -fn test_ln() { - let x = BigDecimal::one(); - let ln_x = ln(&x); - assert_eq!(ln_x.to_string(), "0E-34"); - - let x = BigDecimal::from_str("0.95").unwrap(); - let ln_x = ln(&x); - assert_eq!(ln_x.to_string(), "-0.0512932943875505334261962382072846"); - println!("ln(1-f) = ln (0.95) = {}", ln_x); -} - -#[test] -fn test_infinite_range_stuff() { - let mut range = 1..; - - let mut i = 0; - - while i < 1024 { - let an = range.by_ref().take(1).next().unwrap(); - let bn = an * an; - println!("an: {}, bn: {}, range: {:?}", an, bn, &range); - i += 1; - } - - // let x:&[i32] = &(1..1025).collect()[..]; - // let y: &[i32] = &(1..1025).map(|m| m * m).collect()[..]; - // - // println!("x: {:?}", x); - // println!("y: {:?}", y); -} - -#[test] -fn test_pow() { - let mut x = BigDecimal::from_str("0.2587").unwrap(); - let mut y = ipow(&x, 5); - println!("{}^5 = {}", x, y); - - x = BigDecimal::from_str("-17.2589").unwrap(); - y = ipow(&x, 5); - println!("{}^5 = {}", x, y); - - x = BigDecimal::from(2); - y = ipow(&x, 512); - println!("{}^512 = {}", x, y); -} - -#[test] -fn test_leaderlog_math() { - let sigma = BigDecimal::from_str("0.0077949348290607914969808129687391").unwrap(); - let c = BigDecimal::from_str("-0.0512932943875505334261962382072846").unwrap(); - //let x = round(&(-c * sigma), 34); - let x = round(-c * sigma); - println!("x: {}", x); - assert_eq!(x.to_string(), "0.0003998278869187860731522824872380") -} - -// #[test] -// fn test_ledger_state_1_26_0() { -// // Calculate values from json -// let ledger_state: &PathBuf = &PathBuf::from(OsString::from_str("/tmp/ledger-state-guild.json").unwrap()); -// let ledger: Ledger = -// match serde_json::from_reader::, Ledger3>(BufReader::new(File::open(ledger_state).unwrap())) { -// Ok(ledger3) => ledger3.state_before, -// Err(error) => { -// panic!("Failed to parse ledger state: {:?}", error); -// } -// }; -// -// println!("ledger: {:?}", ledger); -// } - -#[test] -fn test_date_parsing() { - let genesis_start_time_sec = NaiveDateTime::parse_from_str("2022-10-25T00:00:00Z", "%Y-%m-%dT%H:%M:%S%.fZ") - .unwrap() - .and_utc() - .timestamp(); - - assert_eq!(genesis_start_time_sec, 1666656000); -} - -#[test] -fn test_date_parsing2() { - let genesis_start_time_sec = - NaiveDateTime::parse_from_str("2024-05-16T17:18:10.000000000Z", "%Y-%m-%dT%H:%M:%S%.fZ") - .unwrap() - .and_utc() - .timestamp(); - - assert_eq!(genesis_start_time_sec, 1715879890); -} - -#[test] -fn test_date_parsing3() { - let genesis_start_time_sec = NaiveDateTime::parse_from_str("2021-12-09T22:55:22Z", "%Y-%m-%dT%H:%M:%S%.fZ") - .unwrap() - .and_utc() - .timestamp(); - assert_eq!(genesis_start_time_sec, 1639090522); - let current_time_sec = Utc::now().timestamp(); - println!("current_time_sec: {}", current_time_sec); - let current_epoch = (current_time_sec - genesis_start_time_sec) / 3600; - println!("current_epoch: {}", current_epoch); -} - -#[test] -fn test_date_parsing_mainnet() { - let genesis_start_time_sec = NaiveDateTime::parse_from_str("2017-09-23T21:44:51Z", "%Y-%m-%dT%H:%M:%S%.fZ") - .unwrap() - .and_utc() - .timestamp(); - - assert_eq!(genesis_start_time_sec, 1506203091); - let current_time_sec = Utc::now().timestamp(); - println!("current_time_sec: {}", current_time_sec); - let current_epoch = (current_time_sec - genesis_start_time_sec) / 432000; - println!("current_epoch: {}", current_epoch); -}