diff --git a/.github/workflows/buf-pull-request.yml b/.github/workflows/buf-pull-request.yml index 4593874e18..416cc6a0cf 100644 --- a/.github/workflows/buf-pull-request.yml +++ b/.github/workflows/buf-pull-request.yml @@ -1,5 +1,11 @@ name: Protobuf -on: pull_request +on: + # Exclude feature branches, only run if the PR is targeting main. + pull_request_target: + types: + - opened + branches: + - "main" jobs: lint: name: Lint protobuf @@ -10,7 +16,7 @@ jobs: with: buf_api_token: ${{ secrets.BUF_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} - input: 'proto' + # input: 'proto' - uses: bufbuild/buf-lint-action@v1 with: @@ -55,11 +61,11 @@ jobs: toolchain: stable override: false - - uses: bufbuild/buf-setup-action@v1.27.1 + - uses: bufbuild/buf-setup-action@v1 with: buf_api_token: ${{ secrets.BUF_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} - input: 'proto' + # input: 'proto' - name: Install protobuf compiler shell: bash diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index a7caf921b8..d5c04b3775 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -102,6 +102,8 @@ jobs: permissions: contents: read packages: write + needs: + - penumbra steps: - name: Checkout repository uses: actions/checkout@v3 @@ -121,6 +123,10 @@ jobs: permissions: contents: read packages: write + # The upstream container references the Penumbra container by tag, + # so ensure it exists. + needs: + - penumbra steps: - name: Checkout repository uses: actions/checkout@v3 @@ -142,6 +148,8 @@ jobs: hermes: runs-on: ubuntu-latest + needs: + - penumbra permissions: contents: read packages: write diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 09461420e8..0a2ff0c65d 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -55,17 +55,9 @@ jobs: location: us-central1 - name: install helmfile - run: | - helmfile_version="0.157.0" - helmfile_url="https://github.com/helmfile/helmfile/releases/download/v${helmfile_version}/helmfile_${helmfile_version}_linux_amd64.tar.gz" - mkdir -p /tmp/helmfile-download - cd /tmp/helmfile-download || exit 1 - curl -SfL -O "$helmfile_url" - tar -xzf helmfile*.tar.gz - mkdir -p "$HOME/bin" - cp helmfile "$HOME/bin/" - export PATH="$HOME/bin:$PATH" - which helmfile + uses: mamezou-tech/setup-helmfile@v1.3.0 + with: + helmfile-version: "v0.157.0" - name: deploy run: |- diff --git a/.github/workflows/deploy-testnet.yml b/.github/workflows/deploy-testnet.yml index 5a8b2d05bd..081b0934d1 100644 --- a/.github/workflows/deploy-testnet.yml +++ b/.github/workflows/deploy-testnet.yml @@ -49,17 +49,9 @@ jobs: location: us-central1 - name: install helmfile - run: | - helmfile_version="0.157.0" - helmfile_url="https://github.com/helmfile/helmfile/releases/download/v${helmfile_version}/helmfile_${helmfile_version}_linux_amd64.tar.gz" - mkdir -p /tmp/helmfile-download - cd /tmp/helmfile-download || exit 1 - curl -SfL -O "$helmfile_url" - tar -xzf helmfile*.tar.gz - mkdir -p "$HOME/bin" - cp helmfile "$HOME/bin/" - export PATH="$HOME/bin:$PATH" - which helmfile + uses: mamezou-tech/setup-helmfile@v1.3.0 + with: + helmfile-version: "v0.157.0" - name: deploy run: |- @@ -75,6 +67,7 @@ jobs: - name: bounce osiris shell: bash run: |- + export PENUMBRA_VERSION='${{ github.event.inputs.image_tag || github.ref_name }}' # Set the exact version for the current testnet for Osiris, so deps match. kubectl set image deployments \ -l "app.kubernetes.io/instance=osiris-testnet" \ @@ -86,6 +79,7 @@ jobs: - name: bounce galileo shell: bash run: |- + export PENUMBRA_VERSION='${{ github.event.inputs.image_tag || github.ref_name }}' # Set the exact version for the current testnet for Galileo, so deps match. kubectl set image deployments \ -l "app.kubernetes.io/instance=galileo" \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4aba9304c5..f6a0613ee4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,7 +52,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install Rust - run: rustup update 1.67.1 --no-self-update && rustup default 1.67.1 + run: rustup update 1.73.0 --no-self-update && rustup default 1.73.0 - name: Install cargo-dist run: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.5/cargo-dist-v0.0.5-installer.sh | sh - id: create-release @@ -107,7 +107,7 @@ jobs: with: lfs: true - name: Install Rust - run: rustup update 1.67.1 --no-self-update && rustup default 1.67.1 + run: rustup update 1.73.0 --no-self-update && rustup default 1.73.0 - name: Install cargo-dist run: ${{ matrix.install-dist }} - name: Run cargo-dist diff --git a/Cargo.lock b/Cargo.lock index 35b4c69a1b..9c35e6786f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ "getrandom 0.2.10", "once_cell", @@ -122,13 +122,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -142,9 +143,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -185,54 +186,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - [[package]] name = "anyhow" version = "1.0.75" @@ -345,7 +304,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -427,7 +386,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -473,6 +432,54 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "askama" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139" +dependencies = [ + "askama_derive", + "askama_escape", + "askama_shared", +] + +[[package]] +name = "askama_derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71" +dependencies = [ + "askama_shared", + "proc-macro2 1.0.69", + "syn 1.0.109", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_shared" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0" +dependencies = [ + "askama_escape", + "humansize", + "mime", + "mime_guess", + "nom", + "num-traits", + "percent-encoding", + "proc-macro2 1.0.69", + "quote 1.0.33", + "serde", + "syn 1.0.109", + "toml 0.5.11", +] + [[package]] name = "asn1-rs" version = "0.3.1" @@ -495,7 +502,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "synstructure", @@ -507,7 +514,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -540,18 +547,19 @@ dependencies = [ [[package]] name = "async-dup" -version = "1.2.3" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865d94538a2d4f7197f9e08daf94203c06be78fe87a9d293ba4dd718028c5783" +checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" dependencies = [ "futures-io", + "simple-mutex", ] [[package]] name = "async-executor" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" dependencies = [ "async-lock", "async-task", @@ -581,7 +589,7 @@ checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", - "async-io", + "async-io 1.13.0", "async-lock", "blocking", "futures-lite", @@ -590,14 +598,15 @@ dependencies = [ [[package]] name = "async-h1" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8101020758a4fc3a7c326cb42aa99e9fa77cbfb76987c128ad956406fe1f70a7" +checksum = "5d1d1dae8cb2c4258a79d6ed088b7fb9b4763bf4e9b22d040779761e046a2971" dependencies = [ "async-channel", "async-dup", - "async-std", - "futures-core", + "async-global-executor", + "async-io 1.13.0", + "futures-lite", "http-types", "httparse", "log", @@ -617,13 +626,33 @@ dependencies = [ "futures-lite", "log", "parking", - "polling", - "rustix 0.37.23", + "polling 2.8.0", + "rustix 0.37.27", "slab", - "socket2 0.4.9", + "socket2 0.4.10", "waker-fn", ] +[[package]] +name = "async-io" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10da8f3146014722c89e7859e1d7bb97873125d7346d10ca642ffab794355828" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling 3.3.0", + "rustix 0.38.21", + "slab", + "tracing", + "waker-fn", + "windows-sys 0.48.0", +] + [[package]] name = "async-lock" version = "2.8.0" @@ -639,42 +668,41 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" dependencies = [ - "async-io", + "async-io 1.13.0", "blocking", "futures-lite", ] [[package]] name = "async-process" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf012553ce51eb7aa6dc2143804cc8252bd1cb681a1c5cb7fa94ca88682dee1d" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "async-io", + "async-io 1.13.0", "async-lock", "async-signal", "blocking", "cfg-if", - "event-listener 3.0.0", + "event-listener 3.0.1", "futures-lite", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] [[package]] name = "async-signal" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99f3cb3f9ff89f7d718fbb942c9eb91bedff12e396adf09a622dfe7ffec2bc2" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io", + "async-io 2.1.0", "async-lock", "atomic-waker", "cfg-if", - "concurrent-queue", "futures-core", "futures-io", - "libc", + "rustix 0.38.21", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -688,7 +716,7 @@ checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-channel", "async-global-executor", - "async-io", + "async-io 1.13.0", "async-lock", "crossbeam-utils", "futures-channel", @@ -733,7 +761,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -744,26 +772,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "async-task" -version = "4.4.1" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -852,7 +880,11 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", "sync_wrapper", + "tokio", "tower", "tower-layer", "tower-service", @@ -952,9 +984,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -990,12 +1022,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "regex", "rustc-hash", "shlex", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1039,9 +1071,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitmaps" @@ -1115,9 +1147,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" dependencies = [ "async-channel", "async-lock", @@ -1148,7 +1180,7 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "syn 1.0.109", ] @@ -1158,7 +1190,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -1169,7 +1201,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -1185,12 +1217,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ "memchr", - "regex-automata 0.3.8", + "regex-automata 0.4.3", "serde", ] @@ -1208,9 +1240,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -1400,8 +1432,8 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive 3.2.25", - "clap_lex 0.2.4", + "clap_derive", + "clap_lex", "indexmap 1.9.3", "once_cell", "strsim", @@ -1409,28 +1441,6 @@ dependencies = [ "textwrap 0.16.0", ] -[[package]] -name = "clap" -version = "4.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" -dependencies = [ - "clap_builder", - "clap_derive 4.4.2", -] - -[[package]] -name = "clap_builder" -version = "4.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" -dependencies = [ - "anstream", - "anstyle", - "clap_lex 0.5.1", - "strsim", -] - [[package]] name = "clap_derive" version = "3.2.25" @@ -1439,23 +1449,11 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error 1.0.4", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] -[[package]] -name = "clap_derive" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" -dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -1465,18 +1463,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "clap_lex" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "colored_json" version = "2.1.0" @@ -1630,9 +1616,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1832,9 +1818,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", @@ -1844,9 +1830,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] @@ -1901,7 +1887,7 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "strsim", "syn 1.0.109", @@ -1915,10 +1901,10 @@ checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "strsim", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1940,7 +1926,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2003,7 +1989,7 @@ dependencies = [ [[package]] name = "decaf377-fmd" -version = "0.62.0" +version = "0.63.1" dependencies = [ "ark-ff", "ark-serialize", @@ -2018,7 +2004,7 @@ dependencies = [ [[package]] name = "decaf377-ka" -version = "0.62.0" +version = "0.63.1" dependencies = [ "ark-ff", "decaf377 0.5.0", @@ -2078,7 +2064,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -2089,7 +2075,7 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -2165,9 +2151,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2197,9 +2183,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "signature", @@ -2228,9 +2214,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ "base16ct", "crypto-bigint", @@ -2280,7 +2266,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -2302,30 +2288,19 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "ethnum" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8ff382b2fa527fb7fb06eeebfc5bbb3f17e3cc6b9d70b006c41daa8824adac" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" [[package]] name = "event-listener" @@ -2335,9 +2310,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e56284f00d94c1bc7fd3c77027b4623c88c1f53d8d2394c6199f2921dea325" +checksum = "01cec0252c2afff729ee6f00e903d479fba81784c8e2bd77447673471fdfaea1" dependencies = [ "concurrent-queue", "parking", @@ -2419,9 +2394,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -2504,9 +2479,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -2519,9 +2494,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -2529,15 +2504,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -2546,9 +2521,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-lite" @@ -2567,13 +2542,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2584,26 +2559,26 @@ checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" dependencies = [ "futures-io", "rustls 0.20.9", - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -2642,7 +2617,7 @@ checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738" dependencies = [ "proc-macro-error 0.4.12", "proc-macro-hack", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -2689,7 +2664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" dependencies = [ "proc-macro-error 1.0.4", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -2716,7 +2691,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "libc", "libgit2-sys", "log", @@ -2767,7 +2742,7 @@ dependencies = [ "indexmap 1.9.3", "slab", "tokio", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tracing", ] @@ -2789,7 +2764,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", ] [[package]] @@ -2797,9 +2772,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -2807,16 +2779,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", ] [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "allocator-api2", ] @@ -2826,7 +2798,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -2848,7 +2820,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "headers-core", "http", @@ -3002,6 +2974,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" + [[package]] name = "humantime" version = "2.1.0" @@ -3025,7 +3003,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -3034,14 +3012,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.7", + "rustls 0.21.8", "tokio", "tokio-rustls 0.24.1", ] @@ -3073,16 +3051,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -3100,7 +3078,7 @@ version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63042806bb2f662ca1c68026231900cfe13361136ddfd0dd09bcb315056a22b8" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "flex-error", "ics23", @@ -3450,7 +3428,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -3474,7 +3452,7 @@ checksum = "3a7d6e1419fa3129eb0802b4c99603c0d425c79fb5d76191d5a20d0ab0d664e8" dependencies = [ "libflate", "proc-macro-hack", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -3503,7 +3481,7 @@ checksum = "bfbcff6ae46750b15cc594bfd277b188cbddcfdc1817848f97f03f26f8625b9e" dependencies = [ "cfg-if", "js-sys", - "uuid 1.4.1", + "uuid 1.5.0", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3522,12 +3500,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -3589,9 +3567,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itertools" @@ -3641,18 +3619,18 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -3701,9 +3679,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libflate" @@ -3749,9 +3727,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "librocksdb-sys" @@ -3800,15 +3778,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -3890,9 +3868,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -3921,7 +3899,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "142c53885123b68d94108295a09d4afe1a1388ed95b54d5dacd9a454753030f2" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", "metrics-macros", ] @@ -3949,7 +3927,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49e30813093f757be5cf21e50389a24dc7dbb22c49f23b7e8f51d69b508a5ffa" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -3997,6 +3975,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -4014,9 +4002,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", @@ -4038,7 +4026,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "narsil" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "async-stream 0.2.1", @@ -4067,7 +4055,7 @@ dependencies = [ "tendermint-proto", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tonic", "tonic-web", "tower", @@ -4195,7 +4183,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -4235,9 +4223,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -4297,11 +4285,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "a9dfc0783362704e97ef3bd24261995a699468440099ef95d869b4d9732f829a" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -4316,9 +4304,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4329,9 +4317,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "2f55da20b29f956fb01f0add8683eb26ee13ebe3ebd935e49898717c6b4b2830" dependencies = [ "cc", "libc", @@ -4341,18 +4329,18 @@ dependencies = [ [[package]] name = "ordered-float" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] [[package]] name = "os_str_bytes" -version = "6.5.1" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "overload" @@ -4381,16 +4369,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "parking" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -4410,7 +4398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.8", + "parking_lot_core 0.9.9", ] [[package]] @@ -4429,13 +4417,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -4462,7 +4450,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "serde", ] @@ -4505,14 +4493,14 @@ dependencies = [ [[package]] name = "pcli" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", "assert_cmd", "async-stream 0.2.1", "atty", - "base64 0.21.4", + "base64 0.21.5", "bincode", "blake2b_simd 0.5.11", "bytes", @@ -4571,7 +4559,7 @@ dependencies = [ "tendermint", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "toml 0.7.8", "tonic", "tower", @@ -4584,7 +4572,7 @@ dependencies = [ [[package]] name = "pclientd" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "assert_cmd", @@ -4638,7 +4626,7 @@ dependencies = [ [[package]] name = "pd" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -4675,6 +4663,7 @@ dependencies = [ "penumbra-asset", "penumbra-chain", "penumbra-compact-block", + "penumbra-custody", "penumbra-dex", "penumbra-governance", "penumbra-ibc", @@ -4687,7 +4676,6 @@ dependencies = [ "penumbra-tendermint-proxy", "penumbra-tower-trace", "penumbra-transaction", - "penumbra-wallet", "pin-project", "pin-project-lite", "prost 0.12.1", @@ -4711,7 +4699,7 @@ dependencies = [ "tendermint-rpc", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "toml 0.5.11", "tonic", "tonic-reflection", @@ -4749,7 +4737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", ] @@ -4770,7 +4758,7 @@ dependencies = [ [[package]] name = "penumbra-app" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -4834,7 +4822,7 @@ dependencies = [ [[package]] name = "penumbra-asset" -version = "0.62.0" +version = "0.63.1" dependencies = [ "aes 0.8.3", "anyhow", @@ -4881,7 +4869,7 @@ dependencies = [ [[package]] name = "penumbra-chain" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -4910,7 +4898,7 @@ dependencies = [ [[package]] name = "penumbra-compact-block" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -4946,7 +4934,7 @@ dependencies = [ [[package]] name = "penumbra-component" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "async-trait", @@ -4957,7 +4945,7 @@ dependencies = [ [[package]] name = "penumbra-custody" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "base64 0.20.0", @@ -4982,7 +4970,7 @@ dependencies = [ [[package]] name = "penumbra-dao" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5013,7 +5001,7 @@ dependencies = [ [[package]] name = "penumbra-dex" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5072,7 +5060,7 @@ dependencies = [ [[package]] name = "penumbra-distributions" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "async-trait", @@ -5087,7 +5075,7 @@ dependencies = [ [[package]] name = "penumbra-eddy" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5105,7 +5093,7 @@ dependencies = [ [[package]] name = "penumbra-fee" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5130,7 +5118,7 @@ dependencies = [ [[package]] name = "penumbra-governance" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5139,8 +5127,9 @@ dependencies = [ "ark-relations", "ark-serialize", "ark-snark", + "async-stream 0.2.1", "async-trait", - "base64 0.21.4", + "base64 0.21.5", "blake2b_simd 0.5.11", "bytes", "decaf377 0.5.0", @@ -5180,7 +5169,7 @@ dependencies = [ [[package]] name = "penumbra-ibc" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5201,7 +5190,6 @@ dependencies = [ "penumbra-keys", "penumbra-num", "penumbra-proto", - "penumbra-shielded-pool", "penumbra-storage", "prost 0.12.1", "serde", @@ -5217,7 +5205,7 @@ dependencies = [ [[package]] name = "penumbra-keys" -version = "0.62.0" +version = "0.63.1" dependencies = [ "aes 0.8.3", "anyhow", @@ -5266,7 +5254,7 @@ dependencies = [ [[package]] name = "penumbra-measure" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "bytesize", @@ -5288,7 +5276,7 @@ dependencies = [ [[package]] name = "penumbra-num" -version = "0.62.0" +version = "0.63.1" dependencies = [ "aes 0.8.3", "anyhow", @@ -5334,7 +5322,7 @@ dependencies = [ [[package]] name = "penumbra-proof-params" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ec", @@ -5377,7 +5365,7 @@ dependencies = [ [[package]] name = "penumbra-proof-setup" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ec", @@ -5405,7 +5393,7 @@ dependencies = [ [[package]] name = "penumbra-proto" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "async-stream 0.2.1", @@ -5433,7 +5421,7 @@ dependencies = [ [[package]] name = "penumbra-sct" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5466,7 +5454,7 @@ dependencies = [ [[package]] name = "penumbra-shielded-pool" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5485,12 +5473,14 @@ dependencies = [ "decaf377-ka", "decaf377-rdsa", "hex", + "ibc-types", "im", "metrics", "once_cell", "penumbra-asset", "penumbra-chain", "penumbra-component", + "penumbra-ibc", "penumbra-keys", "penumbra-num", "penumbra-proof-params", @@ -5500,9 +5490,11 @@ dependencies = [ "penumbra-tct", "poseidon377", "proptest", + "prost 0.12.1", "rand 0.8.5", "rand_core 0.6.4", "serde", + "serde_json", "tendermint", "thiserror", "tonic", @@ -5511,7 +5503,7 @@ dependencies = [ [[package]] name = "penumbra-stake" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", @@ -5564,7 +5556,7 @@ dependencies = [ [[package]] name = "penumbra-storage" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "async-trait", @@ -5594,7 +5586,7 @@ dependencies = [ [[package]] name = "penumbra-tct" -version = "0.62.0" +version = "0.63.1" dependencies = [ "ark-ed-on-bls12-377", "ark-ff", @@ -5625,7 +5617,7 @@ dependencies = [ [[package]] name = "penumbra-tct-property-test" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "futures", @@ -5638,7 +5630,7 @@ dependencies = [ [[package]] name = "penumbra-tct-visualize" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "axum 0.5.17", @@ -5660,7 +5652,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tonic", "tower-http 0.3.5", "tracing-subscriber 0.3.17", @@ -5668,7 +5660,7 @@ dependencies = [ [[package]] name = "penumbra-tendermint-proxy" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "chrono", @@ -5689,7 +5681,7 @@ dependencies = [ "tendermint-rpc", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tonic", "tower", "tower-service", @@ -5699,7 +5691,7 @@ dependencies = [ [[package]] name = "penumbra-tower-trace" -version = "0.62.0" +version = "0.63.1" dependencies = [ "futures", "hex", @@ -5711,7 +5703,7 @@ dependencies = [ "tendermint-proto", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tonic", "tower", "tower-service", @@ -5720,13 +5712,13 @@ dependencies = [ [[package]] name = "penumbra-transaction" -version = "0.62.0" +version = "0.63.1" dependencies = [ "aes 0.7.5", "anyhow", "ark-ff", "ark-serialize", - "base64 0.21.4", + "base64 0.21.5", "bech32", "blake2b_simd 0.5.11", "bytes", @@ -5775,7 +5767,7 @@ dependencies = [ [[package]] name = "penumbra-view" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-std", @@ -5831,7 +5823,7 @@ dependencies = [ [[package]] name = "penumbra-wallet" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-std", @@ -5870,11 +5862,11 @@ dependencies = [ [[package]] name = "penumbra-wasm" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-ff", - "base64 0.21.4", + "base64 0.21.5", "console_error_panic_hook", "decaf377 0.5.0", "hex", @@ -5917,7 +5909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.1", + "indexmap 2.1.0", ] [[package]] @@ -5935,9 +5927,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -6023,6 +6015,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "polling" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.21", + "tracing", + "windows-sys 0.48.0", +] + [[package]] name = "poly1305" version = "0.7.2" @@ -6164,15 +6170,15 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ - "proc-macro2 1.0.67", - "syn 2.0.37", + "proc-macro2 1.0.69", + "syn 2.0.38", ] [[package]] name = "primitive-types" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", @@ -6206,7 +6212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr 0.4.12", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "version_check", @@ -6219,7 +6225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr 1.0.4", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "version_check", @@ -6231,7 +6237,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "syn-mid", @@ -6244,7 +6250,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "version_check", ] @@ -6275,28 +6281,28 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", - "bitflags 1.3.2", - "byteorder", + "bit-vec", + "bitflags 2.4.1", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.6.29", + "regex-syntax 0.7.5", "rusty-fork", "tempfile", "unarray", @@ -6350,7 +6356,7 @@ dependencies = [ "prost 0.12.1", "prost-types", "regex", - "syn 2.0.37", + "syn 2.0.38", "tempfile", "which", ] @@ -6363,7 +6369,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -6376,9 +6382,9 @@ checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -6427,7 +6433,7 @@ version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", ] [[package]] @@ -6607,7 +6613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", - "ring", + "ring 0.16.20", "time 0.3.19", "yasna", ] @@ -6623,9 +6629,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] @@ -6643,14 +6649,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ - "aho-corasick 1.1.1", + "aho-corasick 1.1.2", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -6664,13 +6670,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ - "aho-corasick 1.1.1", + "aho-corasick 1.1.2", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -6685,13 +6691,19 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -6710,12 +6722,13 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.7", + "rustls 0.21.8", "rustls-native-certs", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", @@ -6746,12 +6759,26 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom 0.2.10", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -6804,7 +6831,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -6845,7 +6872,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.19", + "semver 1.0.20", ] [[package]] @@ -6859,9 +6886,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.23" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", @@ -6873,14 +6900,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.7", + "linux-raw-sys 0.4.10", "windows-sys 0.48.0", ] @@ -6891,19 +6918,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct", - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", - "ring", + "ring 0.17.5", "rustls-webpki", "sct", ] @@ -6915,7 +6942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01be93c9190b1c62d16f99512f86c2b171667c675806452f744669bd5e1ec6ae" dependencies = [ "async-h1", - "async-io", + "async-io 1.13.0", "async-trait", "base64 0.13.1", "chrono", @@ -6926,7 +6953,7 @@ dependencies = [ "pem", "pin-project", "rcgen", - "ring", + "ring 0.16.20", "rustls 0.20.9", "serde", "serde_json", @@ -6955,17 +6982,17 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", ] [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -7080,12 +7107,12 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -7135,9 +7162,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "semver-parser" @@ -7147,9 +7174,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] @@ -7186,27 +7213,37 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.1.0", "itoa", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_qs" version = "0.8.5" @@ -7220,20 +7257,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -7303,7 +7340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -7315,9 +7352,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling 0.20.3", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -7382,9 +7419,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -7441,6 +7478,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simple-mutex" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" +dependencies = [ + "event-listener 2.5.3", +] + [[package]] name = "sized-chunks" version = "0.6.5" @@ -7481,7 +7527,7 @@ dependencies = [ "async-channel", "async-executor", "async-fs", - "async-io", + "async-io 1.13.0", "async-lock", "async-net", "async-process", @@ -7491,9 +7537,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -7501,9 +7547,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -7515,6 +7561,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.2" @@ -7560,7 +7612,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "serde", "serde_derive", @@ -7574,7 +7626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "serde", "serde_derive", @@ -7608,7 +7660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rustversion", "syn 1.0.109", @@ -7637,19 +7689,23 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "summonerd" -version = "0.62.0" +version = "0.63.1" dependencies = [ "anyhow", "ark-groth16", "ark-serialize", + "askama", "async-trait", "atty", + "axum 0.6.20", "bytes", "camino", - "clap 4.4.6", + "chrono", + "clap 3.2.25", "console-subscriber", "decaf377 0.5.0", "futures", + "hex", "http-body", "metrics-tracing-context", "penumbra-asset", @@ -7691,18 +7747,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "unicode-ident", ] @@ -7713,7 +7769,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -7730,7 +7786,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "unicode-xid 0.2.4", @@ -7751,6 +7807,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -7759,14 +7836,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall 0.3.5", - "rustix 0.38.14", + "redox_syscall 0.4.1", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -7858,7 +7935,7 @@ dependencies = [ "peg", "pin-project", "reqwest", - "semver 1.0.19", + "semver 1.0.20", "serde", "serde_bytes", "serde_json", @@ -7908,22 +7985,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -7995,7 +8072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "standback", "syn 1.0.109", @@ -8028,9 +8105,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -8040,7 +8117,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -8062,9 +8139,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -8085,7 +8162,7 @@ checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.9", "tokio", - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] @@ -8094,7 +8171,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.7", + "rustls 0.21.8", "tokio", ] @@ -8107,7 +8184,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.9", + "tokio-util 0.7.10", ] [[package]] @@ -8126,16 +8203,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.12.3", + "hashbrown 0.14.2", "pin-project-lite", "slab", "tokio", @@ -8157,7 +8234,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -8166,9 +8243,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -8179,7 +8256,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -8195,7 +8272,7 @@ dependencies = [ "async-stream 0.3.5", "async-trait", "axum 0.6.20", - "base64 0.21.4", + "base64 0.21.5", "bytes", "h2", "http", @@ -8205,7 +8282,7 @@ dependencies = [ "percent-encoding", "pin-project", "prost 0.12.1", - "rustls 0.21.7", + "rustls 0.21.8", "rustls-pemfile", "tokio", "tokio-rustls 0.24.1", @@ -8236,7 +8313,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fddb2a37b247e6adcb9f239f4e5cefdcc5ed526141a416b943929f13aea2cce" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "http", "http-body", @@ -8265,7 +8342,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tower-layer", "tower-service", "tracing", @@ -8300,7 +8377,7 @@ dependencies = [ "pin-project", "thiserror", "tokio", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tower", "tracing", ] @@ -8331,7 +8408,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "bytes", "futures-core", "futures-util", @@ -8357,11 +8434,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -8370,20 +8446,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -8391,12 +8467,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -8480,6 +8556,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -8541,6 +8626,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" @@ -8553,12 +8644,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "uuid" version = "0.8.2" @@ -8567,9 +8652,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom 0.2.10", "wasm-bindgen", @@ -8583,9 +8668,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" +checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" [[package]] name = "vcpkg" @@ -8671,9 +8756,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -8681,24 +8766,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if", "js-sys", @@ -8708,9 +8793,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote 1.0.33", "wasm-bindgen-macro-support", @@ -8718,28 +8803,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasm-bindgen-test" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +checksum = "c6433b7c56db97397842c46b67e11873eda263170afeb3a2dc74a7cb370fee0d" dependencies = [ "console_error_panic_hook", "js-sys", @@ -8751,19 +8836,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", + "syn 2.0.38", ] [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -8775,18 +8861,18 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "webpki" -version = "0.22.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -8813,7 +8899,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.14", + "rustix 0.38.21", ] [[package]] @@ -8848,10 +8934,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ] @@ -8990,9 +9076,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32" dependencies = [ "memchr", ] @@ -9043,6 +9129,26 @@ dependencies = [ "time 0.3.19", ] +[[package]] +name = "zerocopy" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50cbb27c30666a6108abd6bc7577556265b44f243e2be89a8bc4e07a528c107" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25f293fe55f0a48e7010d65552bb63704f6ceb55a1a385da10d41d8f78e4a3d" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -9058,18 +9164,17 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 23772ce6e9..d0eda1a1b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,8 +60,8 @@ opt-level = "s" # The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.0.5" # The preferred Rust toolchain to use in CI (rustup toolchain syntax). -# We use the same value as the MSRV in pd/Cargo.toml. -rust-toolchain-version = "1.65" +# We use the same value as the MSRV in crates/bin/pd/Cargo.toml. +rust-toolchain-version = "1.73" # CI backends to support (see 'cargo dist generate-ci') ci = ["github"] # Target platforms to build apps for (Rust target-triple syntax) diff --git a/crates/bin/pcli/Cargo.toml b/crates/bin/pcli/Cargo.toml index 9d5d043bbe..76d499f5e2 100644 --- a/crates/bin/pcli/Cargo.toml +++ b/crates/bin/pcli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pcli" -version = "0.62.0" +version = "0.63.1" authors = ["Penumbra Labs "] edition = "2021" description = "The command-line interface for the Penumbra Zone" @@ -86,7 +86,7 @@ indicatif = "0.16" http-body = "0.4.5" clap = { version = "3", features = ["derive", "env"] } camino = "1" -url = "2" +url = { version = "2", features = ["serde"] } colored_json = "2.1" toml = { version = "0.7", features = ["preserve_order"] } walkdir = "2" diff --git a/crates/bin/pcli/src/command.rs b/crates/bin/pcli/src/command.rs index 25d7a7d9ba..a7946e6605 100644 --- a/crates/bin/pcli/src/command.rs +++ b/crates/bin/pcli/src/command.rs @@ -1,6 +1,6 @@ mod ceremony; mod debug; -mod keys; +mod init; mod query; mod tx; mod utils; @@ -8,7 +8,7 @@ mod validator; mod view; pub use debug::DebugCmd; -pub use keys::KeysCmd; +pub use init::InitCmd; pub use query::QueryCmd; pub use tx::TxCmd; pub use validator::ValidatorCmd; @@ -30,7 +30,15 @@ use self::ceremony::CeremonyCmd; // // https://docs.rs/clap/latest/clap/builder/struct.App.html#method.display_order #[derive(Debug, clap::Subcommand)] +#[allow(clippy::large_enum_variant)] pub enum Command { + /// Initialize `pcli` with a new wallet, or reset it. + /// + /// This command requires selecting a custody backend. The `SoftKMS` + /// backend is a good default choice. More backends (e.g., threshold + /// custody, hardware wallets) may be added in the future. + #[clap(display_order = 100)] + Init(InitCmd), /// Query the public chain state, like the validator set. /// /// This command has two modes: it can be used to query raw bytes of @@ -44,9 +52,6 @@ pub enum Command { /// Create and broadcast a transaction. #[clap(subcommand, display_order = 400, visible_alias = "tx")] Transaction(TxCmd), - /// Manage your wallet's keys. - #[clap(subcommand, display_order = 500)] - Keys(KeysCmd), /// Manage a validator. #[clap(subcommand, display_order = 900)] Validator(ValidatorCmd), @@ -62,9 +67,9 @@ impl Command { /// Determine if this command can run in "offline" mode. pub fn offline(&self) -> bool { match self { + Command::Init(_) => true, Command::Transaction(cmd) => cmd.offline(), Command::View(cmd) => cmd.offline(), - Command::Keys(cmd) => cmd.offline(), Command::Validator(cmd) => cmd.offline(), Command::Query(cmd) => cmd.offline(), Command::Debug(cmd) => cmd.offline(), diff --git a/crates/bin/pcli/src/command/ceremony.rs b/crates/bin/pcli/src/command/ceremony.rs index a9ffba47dc..b591a3205d 100644 --- a/crates/bin/pcli/src/command/ceremony.rs +++ b/crates/bin/pcli/src/command/ceremony.rs @@ -7,6 +7,7 @@ use penumbra_proof_setup::all::{ Phase1CeremonyContribution, Phase1RawCeremonyCRS, Phase2CeremonyContribution, Phase2RawCeremonyCRS, }; +use penumbra_proof_setup::single::log::Hashable; use penumbra_proto::{ penumbra::tools::summoning::v1alpha1::ceremony_coordinator_service_client::CeremonyCoordinatorServiceClient, tools::summoning::v1alpha1::{ @@ -45,8 +46,14 @@ async fn handle_bid(app: &mut App, to: Address, from: AddressIndex, bid: &str) - let value = bid.parse::()?; + // If the bid is 0, skip creating a transaction. For instance, this allows reconnecting + // without paying extra. + if value.amount == 0u64.into() { + return Ok(()); + } + let memo_plaintext = MemoPlaintext { - sender: app.fvk.payment_address(from).0, + sender: app.config.full_viewing_key.payment_address(from).0, text: "E PLURIBUS UNUM".to_owned(), }; @@ -59,7 +66,7 @@ async fn handle_bid(app: &mut App, to: Address, from: AddressIndex, bid: &str) - app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), from, ) .await @@ -74,7 +81,7 @@ pub enum CeremonyCmd { Contribute { #[clap(long)] phase: u8, - #[clap(long)] + #[clap(long, default_value = "https://summoning.penumbra.zone")] coordinator_url: Url, #[clap(long)] coordinator_address: Address, @@ -94,16 +101,32 @@ impl CeremonyCmd { bid, } => { println!("¸,ø¤º°` initiating summoning participation `°º¤ø,¸"); - println!("submitting bid for contribution slot: {}", bid); - if *phase != 1 && *phase != 2 { - anyhow::bail!("phase must be 1 or 2."); - } - let index = AddressIndex { - account: 0, - randomizer: b"ceremonyaddr".as_slice().try_into().unwrap(), + + let index = match *phase { + 1 => AddressIndex { + account: 0, + randomizer: b"ceremnyaddr1" + .as_slice() + .try_into() + .expect("12 bytes long"), + }, + 2 => AddressIndex { + account: 0, + randomizer: b"ceremnyaddr2" + .as_slice() + .try_into() + .expect("12 bytes long"), + }, + _ => anyhow::bail!("phase must be 1 or 2."), }; + let address = app.config.full_viewing_key.payment_address(index).0; + + println!( + "submitting bid {} for contribution slot from address {}", + bid, address + ); + handle_bid(app, *coordinator_address, index, bid).await?; - let address = app.fvk.payment_address(index).0; println!("connecting to coordinator..."); // After we bid, we need to wait a couple of seconds just for the transaction to be @@ -133,10 +156,12 @@ Otherwise, please keep this window open. ); use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; let progress_bar = ProgressBar::with_draw_target(1, ProgressDrawTarget::stdout()) - .with_style(ProgressStyle::default_bar().template( - "[{elapsed}] {bar:50.cyan/blue} {pos:>7}/{len:7} {per_sec}\tETA: {eta}\t{msg}", - )); + .with_style( + ProgressStyle::default_bar() + .template("[{elapsed}] {bar:50.blue/cyan} position {pos} out of {len} connected summoners\t{msg}"), + ); progress_bar.set_position(0); + progress_bar.enable_steady_tick(1000); let mut response_rx = client .participate(ReceiverStream::new(req_rx)) @@ -151,12 +176,14 @@ Otherwise, please keep this window open. Some(ParticipateResponse { msg: Some(ResponseMsg::Position(p)), }) => { + tracing::debug!(?p); let len = p.connected_participants; - let pos = p.connected_participants - p.position; + // e.g. displaying 1 / 2 instead of 0 / 2 + let pos = p.position + 1; progress_bar.set_length(len as u64); progress_bar.set_position(pos as u64); progress_bar.set_message(format!( - "(Your bid: {}, Top bid: {})", + "(your bid: {}, most recent slot bid: {})", Amount::try_from( p.your_bid.ok_or(anyhow!("expected bid amount"))? )?, @@ -164,6 +191,7 @@ Otherwise, please keep this window open. p.last_slot_bid.ok_or(anyhow!("expected top bid amount"))? )? )); + progress_bar.tick(); } Some(ParticipateResponse { msg: @@ -181,16 +209,18 @@ Otherwise, please keep this window open. } }; println!("preparing contribution... (please keep this window open)"); - let contribution = if *phase == 1 { + let (contribution, hash) = if *phase == 1 { let parent = Phase1RawCeremonyCRS::unchecked_from_protobuf(unparsed_parent)? .assume_valid(); let contribution = Phase1CeremonyContribution::make(&parent); - contribution.try_into()? + let hash = contribution.hash(); + (contribution.try_into()?, hash) } else { let parent = Phase2RawCeremonyCRS::unchecked_from_protobuf(unparsed_parent)? .assume_valid(); let contribution = Phase2CeremonyContribution::make(&parent); - contribution.try_into()? + let hash = contribution.hash(); + (contribution.try_into()?, hash) }; println!("submitting contribution..."); @@ -207,6 +237,7 @@ Otherwise, please keep this window open. }) => { println!("contribution confirmed at slot {slot}"); println!("thank you for your help summoning penumbra <3"); + println!("here's your contribution receipt (save this to verify inclusion in the final transcript):\n{}", hex::encode_upper(hash.as_ref())); } m => { anyhow::bail!("Received unexpected message from coordinator: {:?}", m) diff --git a/crates/bin/pcli/src/command/init.rs b/crates/bin/pcli/src/command/init.rs new file mode 100644 index 0000000000..eb3535b530 --- /dev/null +++ b/crates/bin/pcli/src/command/init.rs @@ -0,0 +1,161 @@ +use std::{io::Read, str::FromStr}; + +use anyhow::Result; +use penumbra_keys::keys::{Bip44Path, SeedPhrase, SpendKey}; +use rand_core::OsRng; +use url::Url; + +use crate::config::{CustodyConfig, PcliConfig}; + +#[derive(Debug, clap::Parser)] +pub struct InitCmd { + #[clap(subcommand)] + pub subcmd: InitSubCmd, + /// The GRPC URL that will be used in the generated config. + #[clap( + long, + default_value = "https://grpc.testnet.penumbra.zone", + // Note: reading from the environment here means that running + // pcli init inside of the test harness (where we override that) + // will correctly set the URL, even though we don't subsequently + // read it from the environment. + env = "PENUMBRA_NODE_PD_URL", + parse(try_from_str = Url::parse), + )] + grpc_url: Url, +} + +#[derive(Debug, clap::Subcommand)] +pub enum InitSubCmd { + /// Initialize `pcli` with a basic, file-based custody backend. + #[clap(subcommand, display_order = 100)] + SoftKms(SoftKmsInitCmd), + /// Initialize `pcli` in view-only mode, without spending keys. + #[clap(display_order = 200)] + ViewOnly { + /// The full viewing key for the wallet to view. + full_viewing_key: String, + }, + /// Wipe all `pcli` configuration and data, INCLUDING KEYS. + #[clap(display_order = 900)] + UnsafeWipe {}, +} + +#[derive(Debug, clap::Subcommand)] +pub enum SoftKmsInitCmd { + /// Generate a new seed phrase and import its corresponding key. + #[clap(display_order = 100)] + Generate, + /// Import a spend key from an existing seed phrase. + #[clap(display_order = 200)] + ImportPhrase { + /// If set, will use legacy BIP39 derivation. + /// + /// Use this ONLY if: + /// - you generated your wallet prior to Testnet 62. + /// - you need to replicate legacy derivation for some reason. + #[clap(long, action)] + legacy_raw_bip39_derivation: bool, + }, +} + +impl SoftKmsInitCmd { + fn spend_key(&self) -> Result { + Ok(match self { + SoftKmsInitCmd::Generate => { + let seed_phrase = SeedPhrase::generate(OsRng); + + // xxx: Something better should be done here, this is in danger of being + // shared by users accidentally in log output. + println!( + "YOUR PRIVATE SEED PHRASE:\n{seed_phrase}\nSave this in a safe place!\nDO NOT SHARE WITH ANYONE!" + ); + + let path = Bip44Path::new(0); + SpendKey::from_seed_phrase_bip44(seed_phrase, &path) + } + SoftKmsInitCmd::ImportPhrase { + legacy_raw_bip39_derivation, + } => { + let mut seed_phrase = String::new(); + // The `rpassword` crate doesn't support reading from stdin, so we check + // for an interactive session. We must support non-interactive use cases, + // for integration with other tooling. + if atty::is(atty::Stream::Stdin) { + seed_phrase = rpassword::prompt_password("Enter seed phrase: ")?; + } else { + while let Ok(n_bytes) = std::io::stdin().lock().read_to_string(&mut seed_phrase) + { + if n_bytes == 0 { + break; + } + seed_phrase = seed_phrase.trim().to_string(); + } + } + + let seed_phrase = SeedPhrase::from_str(&seed_phrase)?; + + if *legacy_raw_bip39_derivation { + SpendKey::from_seed_phrase_bip39(seed_phrase, 0) + } else { + let path = Bip44Path::new(0); + SpendKey::from_seed_phrase_bip44(seed_phrase, &path) + } + } + }) + } +} + +impl InitCmd { + pub fn exec(&self, home_dir: impl AsRef) -> Result<()> { + let home_dir = home_dir.as_ref(); + + match &self.subcmd { + InitSubCmd::UnsafeWipe {} => { + println!("Deleting all data in {}...", home_dir); + std::fs::remove_dir_all(home_dir)?; + return Ok(()); + } + _ => { + // Check that the data_dir is empty before running init: + if home_dir.exists() && home_dir.read_dir()?.next().is_some() { + anyhow::bail!( + "home directory {:?} is not empty; refusing to initialize", + home_dir + ); + } + } + } + + let (full_viewing_key, custody) = match &self.subcmd { + InitSubCmd::UnsafeWipe {} => unreachable!("this case is handled above"), + InitSubCmd::SoftKms(cmd) => { + let spend_key = cmd.spend_key()?; + ( + spend_key.full_viewing_key().clone(), + CustodyConfig::SoftKms(spend_key.into()), + ) + } + InitSubCmd::ViewOnly { full_viewing_key } => { + let full_viewing_key = full_viewing_key.parse()?; + (full_viewing_key, CustodyConfig::ViewOnly) + } + }; + + let config = PcliConfig { + custody, + full_viewing_key, + grpc_url: self.grpc_url.clone(), + view_url: None, + disable_warning: false, + }; + + // Create the config directory, if + + let config_path = home_dir.join(crate::CONFIG_FILE_NAME); + println!("Writing generated configs to {}", config_path); + config.save(config_path)?; + + Ok(()) + } +} diff --git a/crates/bin/pcli/src/command/keys.rs b/crates/bin/pcli/src/command/keys.rs deleted file mode 100644 index 2ddbf7ebce..0000000000 --- a/crates/bin/pcli/src/command/keys.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::io::Read; -use std::str::FromStr; - -use anyhow::Result; -use directories::ProjectDirs; -use penumbra_keys::keys::{Bip44Path, SeedPhrase}; -use rand_core::OsRng; -use sha2::{Digest, Sha256}; - -use crate::KeyStore; - -#[derive(Debug, clap::Subcommand)] -pub enum KeysCmd { - /// Import an existing key. - #[clap(subcommand)] - Import(ImportCmd), - /// Export keys from the wallet. - #[clap(subcommand)] - Export(ExportCmd), - /// Generate a new seed phrase and import its corresponding key. - Generate, - /// Delete the entire wallet permanently. - Delete, -} - -#[derive(Debug, clap::Subcommand)] -pub enum ImportCmd { - /// Import wallet from an existing 24-word seed phrase. Will prompt for input interactively. - /// Also accepts input from stdin, for use with pipes. Use `--legacy-raw-bip39-derivation` if - /// you generated your wallet prior to Testnet 62. - Phrase { - /// If set, will use legacy BIP39 derivation. Use this if you generated your wallet prior to Testnet 62. - #[clap(long, action)] - legacy_raw_bip39_derivation: bool, - }, -} - -#[derive(Debug, clap::Subcommand)] -pub enum ExportCmd { - /// Export the full viewing key for the wallet. - FullViewingKey, - /// Export the wallet ID. - WalletId, -} - -impl KeysCmd { - /// Determine if this command requires a network sync before it executes. - pub fn offline(&self) -> bool { - true - } - - fn archive_wallet(&self, wallet: &KeyStore) -> Result<()> { - // Archive the newly generated state - let archive_dir = ProjectDirs::from("zone", "penumbra", "penumbra-testnet-archive") - .expect("can access penumbra-testnet-archive dir"); - - // Create the directory /penumbra-testnet-archive/// - let spend_key_hash = Sha256::digest(&wallet.spend_key.to_bytes().0); - let wallet_archive_dir = archive_dir - .data_dir() - .join(hex::encode(&spend_key_hash[0..8])); - std::fs::create_dir_all(&wallet_archive_dir) - .expect("can create penumbra wallet archive directory"); - - // Save the wallet file in the archive directory - let archive_path = wallet_archive_dir.join(crate::CUSTODY_FILE_NAME); - println!("Saving backup wallet to {}", archive_path.display()); - wallet.save(archive_path)?; - Ok(()) - } - - pub fn exec(&self, data_dir: impl AsRef) -> Result<()> { - let data_dir = data_dir.as_ref(); - match self { - KeysCmd::Generate => { - let seed_phrase: SeedPhrase = SeedPhrase::generate(OsRng); - - // xxx: Something better should be done here, this is in danger of being - // shared by users accidentally in log output. - println!("YOUR PRIVATE SEED PHRASE: {seed_phrase}\nDO NOT SHARE WITH ANYONE!"); - - let path = Bip44Path::new(0); - let wallet = KeyStore::from_seed_phrase_bip44(seed_phrase, &path); - wallet.save(data_dir.join(crate::CUSTODY_FILE_NAME))?; - self.archive_wallet(&wallet)?; - } - KeysCmd::Import(ImportCmd::Phrase { - legacy_raw_bip39_derivation, - }) => { - let mut seed_phrase = String::new(); - // The `rpassword` crate doesn't support reading from stdin, so we check - // for an interactive session. We must support non-interactive use cases, - // for integration with other tooling. - if atty::is(atty::Stream::Stdin) { - seed_phrase = rpassword::prompt_password("Enter seed phrase: ")?; - } else { - while let Ok(n_bytes) = std::io::stdin().lock().read_to_string(&mut seed_phrase) - { - if n_bytes == 0 { - break; - } - seed_phrase = seed_phrase.trim().to_string(); - } - } - - let seed_phrase = SeedPhrase::from_str(&seed_phrase)?; - let wallet = if *legacy_raw_bip39_derivation { - KeyStore::from_seed_phrase_bip39(seed_phrase) - } else { - let path = Bip44Path::new(0); - KeyStore::from_seed_phrase_bip44(seed_phrase, &path) - }; - wallet.save(data_dir.join(crate::CUSTODY_FILE_NAME))?; - self.archive_wallet(&wallet)?; - } - KeysCmd::Export(ExportCmd::FullViewingKey) => { - let wallet = KeyStore::load(data_dir.join(crate::CUSTODY_FILE_NAME))?; - println!("{}", wallet.spend_key.full_viewing_key()); - } - KeysCmd::Export(ExportCmd::WalletId) => { - let key_store = KeyStore::load(data_dir.join(crate::CUSTODY_FILE_NAME))?; - let wallet_id = key_store.spend_key.full_viewing_key().wallet_id(); - println!("{}", serde_json::to_string_pretty(&wallet_id)?); - } - KeysCmd::Delete => { - let wallet_path = data_dir.join(crate::CUSTODY_FILE_NAME); - if wallet_path.is_file() { - std::fs::remove_file(&wallet_path)?; - println!("Deleted wallet file at {wallet_path}"); - } else if wallet_path.exists() { - anyhow::bail!( - "Expected wallet file at {} but found something that is not a file; refusing to delete it", - wallet_path - ); - } else { - anyhow::bail!( - "No wallet exists at {}, so it cannot be deleted", - wallet_path - ); - } - } - }; - - Ok(()) - } -} diff --git a/crates/bin/pcli/src/command/query/dao.rs b/crates/bin/pcli/src/command/query/dao.rs index 36a60a2eda..8605165a07 100644 --- a/crates/bin/pcli/src/command/query/dao.rs +++ b/crates/bin/pcli/src/command/query/dao.rs @@ -1,17 +1,13 @@ -use anyhow::Result; -//use anyhow::{Context, Result}; -/* +use crate::App; +use anyhow::{Context, Result}; use futures::TryStreamExt; -//use penumbra_app::dao; -use penumbra_dao::component::state_key; - -use penumbra_asset::{asset, Value}; -use penumbra_num::Amount; +use penumbra_asset::Value; +use penumbra_proto::{ + core::component::dao::v1alpha1::DaoAssetBalancesRequest, + penumbra::core::component::dao::v1alpha1::query_service_client::QueryServiceClient as DaoQueryServiceClient, +}; use penumbra_view::ViewClient; - -use crate::{command::query::dao, App}; - */ -use crate::App; +use std::io::{stdout, Write}; #[derive(Debug, clap::Subcommand)] pub enum DaoCmd { @@ -29,54 +25,44 @@ impl DaoCmd { } } - pub async fn print_balance(&self, _app: &mut App, _asset: &Option) -> Result<()> { - unimplemented!("dao component needs an RPC defined"); - // below code is not usable outside of our own crates because - // it does raw state key access - /* + pub async fn print_balance(&self, app: &mut App, asset: &Option) -> Result<()> { let asset_id = asset.as_ref().map(|asset| { // Try to parse as an asset ID, then if it's not an asset ID, assume it's a unit name if let Ok(asset_id) = asset.parse() { asset_id } else { - asset::REGISTRY.parse_unit(asset.as_str()).id() + penumbra_asset::asset::REGISTRY + .parse_unit(asset.as_str()) + .id() } }); - let mut client = app.specific_client().await?; - let asset_cache = app.view().assets().await?; - if let Some(asset_id) = asset_id { - let key = state_key::balance_for_asset(asset_id); - let amount: Amount = client - .key_domain(&key) - .await? - .context(format!("No balance found for asset {asset_id}"))?; - - let value = Value { asset_id, amount }; - let value_str = value.format(&asset_cache); - - println!("{value_str}"); - } else { - let prefix = dao::state_key::all_assets_balance(); - let results: Vec<_> = client.prefix_domain(prefix).await?.try_collect().await?; + let mut client = DaoQueryServiceClient::new(app.pd_channel().await?); + let chain_id = app.view().app_params().await?.chain_params.chain_id; + let balances = client + .dao_asset_balances(DaoAssetBalancesRequest { + chain_id, + asset_ids: asset_id.map_or_else(std::vec::Vec::new, |id| vec![id.into()]), + }) + .await? + .into_inner() + .try_collect::>() + .await + .context("cannot process dao balance data")?; - println!("DAO balance ({} unique assets):", results.len()); + let asset_cache = app.view().assets().await?; + let mut writer = stdout(); + for balance_response in balances { + let balance: Value = balance_response + .balance + .expect("balance should always be set") + .try_into() + .context("cannot parse balance")?; + let value_str = balance.format(&asset_cache); - for (key, amount) in results { - // Parse every key/value pair into a Value - let asset_id: asset::Id = key - .rsplit('/') - .next() - .expect("valid key") - .parse() - .expect("valid asset ID"); - let value = Value { asset_id, amount }; - let value_str = value.format(&asset_cache); - println!("{value_str}"); - } - }; + writeln!(writer, "{value_str}")?; + } Ok(()) - */ } } diff --git a/crates/bin/pcli/src/command/query/governance.rs b/crates/bin/pcli/src/command/query/governance.rs index 59c40455a6..58eefcd3d6 100644 --- a/crates/bin/pcli/src/command/query/governance.rs +++ b/crates/bin/pcli/src/command/query/governance.rs @@ -1,7 +1,20 @@ -//use std::io::{stdout, Write}; +use std::{ + collections::BTreeMap, + io::{stdout, Write}, +}; -use anyhow::Result; -//use serde::Serialize; +use anyhow::{Context, Result}; +use futures::TryStreamExt; +use penumbra_governance::Vote; +use penumbra_proto::core::component::governance::v1alpha1::{ + query_service_client::QueryServiceClient as GovernanceQueryServiceClient, + AllTalliedDelegatorVotesForProposalRequest, ProposalDataRequest, ProposalListRequest, + ProposalListResponse, ValidatorVotesRequest, ValidatorVotesResponse, + VotingPowerAtProposalStartRequest, +}; +use penumbra_stake::IdentityKey; +use serde::Serialize; +use serde_json::json; use crate::App; @@ -36,152 +49,156 @@ pub enum PerProposalCmd { } impl GovernanceCmd { - pub async fn exec(&self, _app: &mut App) -> Result<()> { + pub async fn exec(&self, app: &mut App) -> Result<()> { // use PerProposalCmd::*; + let mut client = GovernanceQueryServiceClient::new(app.pd_channel().await?); match self { - GovernanceCmd::ListProposals { .. } => { - unimplemented!("governance component needs an RPC defined") - // below code is not usable outside of our own crates because - // it does raw state key accesses. - /* - let proposal_id_list: Vec = if *inactive { - let next: u64 = client - .key_proto(next_proposal_id()) - .await? - .context("no proposal found")?; - - (0..next).collect() - } else { - let mut unfinished = client - .prefix_value(PrefixValueRequest { - prefix: all_unfinished_proposals().into(), - ..Default::default() - }) - .await? - .into_inner(); - let mut unfinished_proposals: Vec = Vec::new(); - while let Some(PrefixValueResponse { key, .. }) = - unfinished.next().await.transpose()? - { - let proposal_id = u64::from_str( - key.rsplit('/').next().context("key must contain a slash")?, - ) - .context("proposal id was not a valid u64")?; - unfinished_proposals.push(proposal_id); - } - unfinished_proposals - }; - + GovernanceCmd::ListProposals { inactive } => { + let proposals: Vec = client + .proposal_list(ProposalListRequest { + inactive: *inactive, + ..Default::default() + }) + .await? + .into_inner() + .try_collect::>() + .await + .context("cannot process proposal list data")?; let mut writer = stdout(); - for proposal_id in proposal_id_list { - let proposal: Proposal = client - .key_domain(proposal_definition(proposal_id)) - .await? - .context(format!("proposal {} not found", proposal_id))?; - + for proposal_response in proposals { + let proposal = proposal_response + .proposal + .expect("proposal should always be set"); let proposal_title = proposal.title; - let proposal_state: ProposalState = client - .key_domain(proposal_state(proposal_id)) - .await? - .context(format!("proposal state for {} not found", proposal_id))?; + let proposal_state = proposal_response + .state + .expect("proposal state should always be set"); + + let proposal_id = proposal.id; writeln!( writer, "#{proposal_id} {proposal_state:?} {proposal_title}" )?; } - */ + Ok(()) } - GovernanceCmd::Proposal { .. } => { - unimplemented!("governance component needs an RPC defined"); - // below code is not usable outside of our own crates because - // it does raw state key accesses. - /* - match query { - Definition => { - let proposal: Proposal = client - .key_domain(proposal_definition(*proposal_id)) + GovernanceCmd::Proposal { proposal_id, query } => { + match query { + &PerProposalCmd::Definition => { + let proposal = client + .proposal_data(ProposalDataRequest { + proposal_id: *proposal_id, + ..Default::default() + }) .await? - .context(format!( - "proposal definition for proposal {} not found", - proposal_id - ))?; - toml(&proposal)?; + .into_inner(); + toml( + &proposal + .proposal + .expect("proposal should always be populated"), + )?; } - State => { - let state: ProposalState = client - .key_domain(proposal_state(*proposal_id)) + PerProposalCmd::State => { + let proposal = client + .proposal_data(ProposalDataRequest { + proposal_id: *proposal_id, + ..Default::default() + }) .await? - .context(format!( - "proposal state for proposal {} not found", - proposal_id - ))?; - json(&state)?; + .into_inner(); + json( + &proposal + .state + .expect("proposal state should always be populated"), + )?; } - Period => { - let start: u64 = client - .key_proto(proposal_voting_start(*proposal_id)) - .await? - .context(format!( - "proposal voting start for proposal {} not found", - proposal_id - ))?; - let end: u64 = client - .key_proto(proposal_voting_end(*proposal_id)) + PerProposalCmd::Period => { + let proposal = client + .proposal_data(ProposalDataRequest { + proposal_id: *proposal_id, + ..Default::default() + }) .await? - .context(format!( - "proposal voting end for proposal {} not found", - proposal_id - ))?; + .into_inner(); + let start: u64 = proposal.start_block_height; + let end: u64 = proposal.end_block_height; let period = json!({ "voting_start_block": start, "voting_end_block": end, }); json(&period)?; } - Tally => { - let validator_votes: BTreeMap = client - .prefix_domain::(all_validator_votes_for_proposal(*proposal_id)) - .await? - .and_then(|r| async move { - let identity_key = IdentityKey::from_str( - r.0.rsplit('/').next().context("invalid key")?, - )?; - Ok((identity_key, r.1)) + PerProposalCmd::Tally => { + let validator_votes: Vec = client + .validator_votes(ValidatorVotesRequest { + proposal_id: *proposal_id, + ..Default::default() }) - .try_collect() + .await? + .into_inner() + .try_collect::>() .await?; let mut validator_votes_and_power: BTreeMap = BTreeMap::new(); - for (identity_key, vote) in validator_votes.iter() { + for vote_response in validator_votes { + let identity_key: IdentityKey = vote_response + .identity_key + .expect("identity key must be set for vote response") + .try_into()?; + let vote: Vote = vote_response + .vote + .expect("vote must be set for vote response") + .try_into()?; let power: u64 = client - .key_proto(voting_power_at_proposal_start(*proposal_id, *identity_key)) + .voting_power_at_proposal_start(VotingPowerAtProposalStartRequest { + proposal_id: *proposal_id, + identity_key: Some(identity_key.into()), + ..Default::default() + }) .await .context("Error looking for validator power")? - .context("validator power not found")?; + .into_inner() + .voting_power; - validator_votes_and_power.insert(*identity_key, (*vote, power)); + validator_votes_and_power.insert(identity_key, (vote, power)); } - let mut delegator_tallies: BTreeMap = - client - .prefix_domain::( - all_tallied_delegator_votes_for_proposal(*proposal_id), - ) - .await? - .and_then(|r| async move { - Ok(( - IdentityKey::from_str( - r.0.rsplit('/').next().context("invalid key")?, - )?, - r.1, - )) - }) - .try_collect() - .await?; + let mut delegator_tallies: BTreeMap< + IdentityKey, + penumbra_governance::Tally, + > = client + .all_tallied_delegator_votes_for_proposal( + AllTalliedDelegatorVotesForProposalRequest { + proposal_id: *proposal_id, + ..Default::default() + }, + ) + .await? + .into_inner() + .map_ok(|response| { + let identity_key: IdentityKey = response + .identity_key + .expect("identity key must be set for vote response") + .try_into()?; + let tally: penumbra_governance::Tally = response + .tally + .expect("tally must be set for vote response") + .try_into()?; + Ok::<(IdentityKey, penumbra_governance::Tally), anyhow::Error>(( + identity_key, + tally, + )) + }) + // TODO: double iterator here is suboptimal but trying to collect + // `Result>` was annoying + .try_collect::>() + .await? + .into_iter() + .collect::>>()?; // Combine the two mappings let mut total = penumbra_governance::Tally::default(); @@ -226,20 +243,17 @@ impl GovernanceCmd { } json(&json!({ - "total": json_tally(&total), - "details": all_votes_and_power, + "total": json_tally(&total), + "details": all_votes_and_power, }))?; } - }, - */ + }; + Ok(()) } } - - // Ok(()) } } -/* fn json(value: &T) -> Result<()> { let mut writer = stdout(); serde_json::to_writer_pretty(&mut writer, value)?; @@ -267,4 +281,3 @@ fn toml(value: &T) -> Result<()> { writer.write_all(string.as_bytes())?; Ok(()) } - */ diff --git a/crates/bin/pcli/src/command/tx.rs b/crates/bin/pcli/src/command/tx.rs index ff0dbe61c5..aa26036092 100644 --- a/crates/bin/pcli/src/command/tx.rs +++ b/crates/bin/pcli/src/command/tx.rs @@ -1,7 +1,7 @@ use std::{ collections::BTreeMap, fs::File, - io::Read, + io::{Read, Write}, str::FromStr, time::{SystemTime, UNIX_EPOCH}, }; @@ -17,8 +17,7 @@ use liquidity_position::PositionCmd; use penumbra_asset::{asset, asset::DenomMetadata, Value, STAKING_TOKEN_ASSET_ID}; use penumbra_dex::{lp::position, swap_claim::SwapClaimPlan}; use penumbra_fee::Fee; -use penumbra_governance::{proposal::ProposalToml, Vote}; -use penumbra_ibc::Ics20Withdrawal; +use penumbra_governance::{proposal::ProposalToml, proposal_state::State as ProposalState, Vote}; use penumbra_keys::keys::AddressIndex; use penumbra_num::Amount; use penumbra_proto::{ @@ -33,7 +32,8 @@ use penumbra_proto::{ }, governance::v1alpha1::{ query_service_client::QueryServiceClient as GovernanceQueryServiceClient, - ProposalInfoRequest, ProposalInfoResponse, ProposalRateDataRequest, + NextProposalIdRequest, ProposalDataRequest, ProposalInfoRequest, ProposalInfoResponse, + ProposalRateDataRequest, }, stake::v1alpha1::{ query_service_client::QueryServiceClient as StakeQueryServiceClient, @@ -42,6 +42,7 @@ use penumbra_proto::{ }, view::v1alpha1::GasPricesRequest, }; +use penumbra_shielded_pool::Ics20Withdrawal; use penumbra_stake::rate::RateData; use penumbra_stake::{DelegationToken, IdentityKey, Penalty, UnbondingToken, UndelegateClaimPlan}; use penumbra_transaction::{gas::swap_claim_gas_cost, memo::MemoPlaintext}; @@ -272,8 +273,11 @@ impl TxCmd { let to = to .parse() .map_err(|_| anyhow::anyhow!("address is invalid"))?; - let memo_ephemeral_address = - app.fvk.ephemeral_address(OsRng, AddressIndex::new(*from)).0; + let memo_ephemeral_address = app + .config + .full_viewing_key + .ephemeral_address(OsRng, AddressIndex::new(*from)) + .0; let memo_plaintext = MemoPlaintext { sender: memo_ephemeral_address, @@ -291,7 +295,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*from), ) .await @@ -314,7 +318,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -322,7 +326,7 @@ impl TxCmd { } TxCmd::Sweep => loop { let plans = plan::sweep( - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), app.view .as_mut() .context("view service must be initialized")?, @@ -352,7 +356,7 @@ impl TxCmd { let input = input.parse::()?; let into = asset::REGISTRY.parse_unit(into.as_str()).base(); - let fvk = app.fvk.clone(); + let fvk = app.config.full_viewing_key.clone(); // If a source address was specified, use it for the swap, otherwise, // use the default address. @@ -376,7 +380,7 @@ impl TxCmd { ); planner.swap(input, into.id(), estimated_claim_fee, claim_address)?; - let wallet_id = app.fvk.wallet_id(); + let wallet_id = app.config.full_viewing_key.wallet_id(); let plan = planner .plan(app.view(), wallet_id, AddressIndex::new(*source)) .await @@ -420,7 +424,7 @@ impl TxCmd { .format(&asset_cache), ); - let wallet_id = app.fvk.wallet_id(); + let wallet_id = app.config.full_viewing_key.wallet_id(); let params = app .view @@ -469,7 +473,7 @@ impl TxCmd { let mut planner = Planner::new(OsRng); planner.set_gas_prices(gas_prices); - let wallet_id = app.fvk.wallet_id().clone(); + let wallet_id = app.config.full_viewing_key.wallet_id().clone(); let plan = planner .delegate(unbonded_amount.value(), rate_data) .plan(app.view(), wallet_id, AddressIndex::new(*source)) @@ -513,7 +517,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await @@ -522,7 +526,7 @@ impl TxCmd { app.build_and_submit_transaction(plan).await?; } TxCmd::UndelegateClaim {} => { - let wallet_id = app.fvk.wallet_id(); // this should be optional? or saved in the client statefully? + let wallet_id = app.config.full_viewing_key.wallet_id(); // this should be optional? or saved in the client statefully? let channel = app.pd_channel().await?; let view: &mut dyn ViewClient = app @@ -612,7 +616,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), address_index, ) .await?; @@ -644,7 +648,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -663,28 +667,28 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; app.build_and_submit_transaction(plan).await?; } - TxCmd::Proposal(ProposalCmd::Template { .. }) => { - unimplemented!("governance needs an RPC endpoint"); - // below code isn't usable by any other client - /* - let chain_params = app.view().chain_params().await?; + TxCmd::Proposal(ProposalCmd::Template { file, kind }) => { + let app_params = app.view().app_params().await?; // Find out what the latest proposal ID is so we can include the next ID in the template: - let mut client = StorageQueryServiceClient::new(app.pd_channel().await?); + let mut client = GovernanceQueryServiceClient::new(app.pd_channel().await?); let next_proposal_id: u64 = client - .key_proto(penumbra_governance::state_key::next_proposal_id()) + .next_proposal_id(NextProposalIdRequest { + chain_id: app.view().app_params().await?.chain_params.chain_id, + }) .await? - .context(format!("there are no proposals yet"))?; + .into_inner() + .next_proposal_id; let toml_template: ProposalToml = kind - .template_proposal(&chain_params, next_proposal_id)? + .template_proposal(&app_params, next_proposal_id)? .into(); if let Some(file) = file { @@ -695,24 +699,33 @@ impl TxCmd { } else { println!("{}", toml::to_string_pretty(&toml_template)?); } - */ } - TxCmd::Proposal(ProposalCmd::DepositClaim { .. }) => { - unimplemented!("governance needs an RPC endpoint"); - // below code isn't usable by any other client - /* - use penumbra_governance::state_key; - - let fee = Fee::from_staking_token_amount((*fee).into()); - - let mut client = app.specific_client().await?; - let state: ProposalState = client - .key_domain(state_key::proposal_state(*proposal_id)) + TxCmd::Proposal(ProposalCmd::DepositClaim { + proposal_id, + source, + }) => { + let mut client = GovernanceQueryServiceClient::new(app.pd_channel().await?); + let proposal = client + .proposal_data(ProposalDataRequest { + chain_id: app.view().app_params().await?.chain_params.chain_id, + proposal_id: *proposal_id, + }) .await? + .into_inner(); + let state: ProposalState = proposal + .state .context(format!( "proposal state for proposal {} was not found", proposal_id - ))?; + ))? + .try_into()?; + let deposit_amount: Amount = proposal + .proposal_deposit_amount + .context(format!( + "proposal deposit amount for proposal {} was not found", + proposal_id + ))? + .try_into()?; let outcome = match state { ProposalState::Voting => anyhow::bail!( @@ -728,28 +741,18 @@ impl TxCmd { } }; - let deposit_amount: Amount = client - .key_domain(state_key::proposal_deposit_amount(*proposal_id)) - .await? - .context(format!( - "deposit amount for proposal {} was not found", - proposal_id - ))?; - let plan = Planner::new(OsRng) .proposal_deposit_claim(*proposal_id, deposit_amount, outcome) - .fee(fee) .plan( app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; app.build_and_submit_transaction(plan).await?; - */ } TxCmd::Vote { vote, source } => { let (proposal_id, vote): (u64, Vote) = (*vote).into(); @@ -809,7 +812,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -832,7 +835,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), source, ) .await?; @@ -850,7 +853,8 @@ impl TxCmd { let fee = Fee::from_staking_token_amount(Amount::zero()); let (ephemeral_return_address, _) = app - .fvk + .config + .full_viewing_key .ephemeral_address(OsRng, AddressIndex::from(*source)); // get the current time on the local machine @@ -904,7 +908,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -924,7 +928,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -965,7 +969,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -1042,7 +1046,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; @@ -1095,7 +1099,7 @@ impl TxCmd { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(*source), ) .await?; diff --git a/crates/bin/pcli/src/command/tx/proposal.rs b/crates/bin/pcli/src/command/tx/proposal.rs index 2df4b6a790..c8afa537a2 100644 --- a/crates/bin/pcli/src/command/tx/proposal.rs +++ b/crates/bin/pcli/src/command/tx/proposal.rs @@ -77,7 +77,6 @@ pub enum ProposalKindCmd { impl ProposalKindCmd { /// Generate a default proposal of a particular kind. - #[allow(dead_code)] // remove me after implementing governance rpcs pub fn template_proposal(&self, app_params: &AppParameters, id: u64) -> Result { let title = "A short title (at most 80 characters)".to_string(); let description = "A longer description (at most 10,000 characters)".to_string(); diff --git a/crates/bin/pcli/src/command/tx/replicate.rs b/crates/bin/pcli/src/command/tx/replicate.rs index 784e8538c4..cc4cd5e870 100644 --- a/crates/bin/pcli/src/command/tx/replicate.rs +++ b/crates/bin/pcli/src/command/tx/replicate.rs @@ -168,7 +168,7 @@ impl ConstantProduct { app.view .as_mut() .context("view service must be initialized")?, - app.fvk.wallet_id(), + app.config.full_viewing_key.wallet_id(), AddressIndex::new(self.source), ) .await?; diff --git a/crates/bin/pcli/src/command/validator.rs b/crates/bin/pcli/src/command/validator.rs index 3b85e2f749..df45808642 100644 --- a/crates/bin/pcli/src/command/validator.rs +++ b/crates/bin/pcli/src/command/validator.rs @@ -22,7 +22,7 @@ use penumbra_stake::{ }; use penumbra_wallet::plan; -use crate::App; +use crate::{config::CustodyConfig, App}; #[derive(Debug, clap::Subcommand)] pub enum ValidatorCmd { @@ -104,8 +104,14 @@ impl ValidatorCmd { // TODO: move use of sk into custody service pub async fn exec(&self, app: &mut App) -> Result<()> { - let sk = app.wallet.spend_key.clone(); - let fvk = sk.full_viewing_key().clone(); + let sk = match &app.config.custody { + CustodyConfig::SoftKms(config) => config.spend_key.clone(), + _ => { + anyhow::bail!("Validator commands require SoftKMS backend"); + } + }; + let fvk = app.config.full_viewing_key.clone(); + match self { ValidatorCmd::Identity { base64 } => { let ik = IdentityKey(fvk.spend_verification_key().clone()); @@ -150,7 +156,7 @@ impl ValidatorCmd { }; // Construct a new transaction and include the validator definition. - let wallet_id = app.fvk.wallet_id(); + let wallet_id = app.config.full_viewing_key.wallet_id(); let plan = plan::validator_definition( wallet_id, app.view @@ -206,7 +212,7 @@ impl ValidatorCmd { // Construct a new transaction and include the validator definition. let fee = Fee::from_staking_token_amount((*fee).into()); - let wallet_id = app.fvk.wallet_id(); + let wallet_id = app.config.full_viewing_key.wallet_id(); let plan = plan::validator_vote( wallet_id, diff --git a/crates/bin/pcli/src/command/view.rs b/crates/bin/pcli/src/command/view.rs index 549df7dc33..fbff10e6a6 100644 --- a/crates/bin/pcli/src/command/view.rs +++ b/crates/bin/pcli/src/command/view.rs @@ -57,7 +57,7 @@ impl ViewCmd { pub async fn exec(&self, app: &mut App) -> Result<()> { // TODO: refactor view methods to take a single App - let full_viewing_key = app.fvk.clone(); + let full_viewing_key = app.config.full_viewing_key.clone(); match self { ViewCmd::WalletId(wallet_id_cmd) => { diff --git a/crates/bin/pcli/src/command/view/tx.rs b/crates/bin/pcli/src/command/view/tx.rs index 7dd50c002e..e75e8b3512 100644 --- a/crates/bin/pcli/src/command/view/tx.rs +++ b/crates/bin/pcli/src/command/view/tx.rs @@ -233,7 +233,7 @@ impl TxCmd { false } pub async fn exec(&self, app: &mut App) -> Result<()> { - let fvk = app.fvk.clone(); + let fvk = app.config.full_viewing_key.clone(); let hash = self .hash // We have to convert to uppercase because `tendermint::Hash` only accepts uppercase :( diff --git a/crates/bin/pcli/src/config.rs b/crates/bin/pcli/src/config.rs new file mode 100644 index 0000000000..9de3748ec4 --- /dev/null +++ b/crates/bin/pcli/src/config.rs @@ -0,0 +1,99 @@ +use std::path::Path; + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; +use url::Url; + +use penumbra_custody::soft_kms::Config as SoftKmsConfig; +use penumbra_keys::FullViewingKey; + +/// Configuration data for `pcli`. +#[serde_as] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +pub struct PcliConfig { + /// The URL of the gRPC endpoint used to talk to pd. + pub grpc_url: Url, + /// If set, use a remote view service instead of local synchronization. + pub view_url: Option, + /// Disable the scary "you will lose all your money" warning. + #[serde(default, skip_serializing_if = "is_default")] + pub disable_warning: bool, + /// The FVK used for viewing chain data. + #[serde_as(as = "DisplayFromStr")] + pub full_viewing_key: FullViewingKey, + /// The custody backend to use. + pub custody: CustodyConfig, +} + +impl PcliConfig { + pub fn load + std::fmt::Display>(path: P) -> Result { + let contents = std::fs::read_to_string(&path).context(format!( + "pcli config file not found: {}. hint: run 'pcli init' to create new keys", + &path + ))?; + Ok(toml::from_str(&contents)?) + } + + pub fn save>(&self, path: P) -> Result<()> { + let contents = toml::to_string_pretty(&self)?; + std::fs::write(path, contents)?; + Ok(()) + } +} + +/// The custody backend to use. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(tag = "backend")] +#[allow(clippy::large_enum_variant)] +pub enum CustodyConfig { + /// A view-only client that can't sign transactions. + ViewOnly, + /// A software key management service. + SoftKms(SoftKmsConfig), +} + +impl Default for CustodyConfig { + fn default() -> Self { + Self::ViewOnly + } +} + +/// Helper function for Serde serialization, allowing us to skip serialization +/// of default config values. Rationale: if we don't skip serialization of +/// defaults, if someone serializes a config with some default values, they're +/// "pinning" the current defaults as their choices for all time, and we have no +/// way to distinguish between fields they configured explicitly and ones they +/// passed through from the defaults. If we skip serializing default values, +/// then we know every value in the config was explicitly set. +fn is_default(value: &T) -> bool { + *value == T::default() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn toml_config() { + let config = PcliConfig { + grpc_url: Url::parse("https://grpc.testnet.penumbra.zone").unwrap(), + disable_warning: false, + view_url: None, + full_viewing_key: penumbra_chain::test_keys::FULL_VIEWING_KEY.clone(), + custody: CustodyConfig::SoftKms(SoftKmsConfig::from( + penumbra_chain::test_keys::SPEND_KEY.clone(), + )), + }; + + let mut config2 = config.clone(); + config2.custody = CustodyConfig::ViewOnly; + config2.disable_warning = true; + + let toml_config = toml::to_string_pretty(&config).unwrap(); + let toml_config2 = toml::to_string_pretty(&config2).unwrap(); + + println!("{}", toml_config); + println!("{}", toml_config2); + } +} diff --git a/crates/bin/pcli/src/main.rs b/crates/bin/pcli/src/main.rs index 842a339679..042198b8c4 100644 --- a/crates/bin/pcli/src/main.rs +++ b/crates/bin/pcli/src/main.rs @@ -4,29 +4,28 @@ use std::fs; use anyhow::{Context, Result}; use clap::Parser; +use config::PcliConfig; use futures::StreamExt; -use penumbra_keys::FullViewingKey; use penumbra_proto::{ custody::v1alpha1::custody_protocol_service_client::CustodyProtocolServiceClient, view::v1alpha1::view_protocol_service_client::ViewProtocolServiceClient, }; use penumbra_view::ViewClient; -use url::Url; mod box_grpc_svc; mod command; +mod config; mod dex_utils; mod network; mod opt; mod warning; use opt::Opt; -use penumbra_wallet::KeyStore; use box_grpc_svc::BoxGrpcService; use command::*; -const CUSTODY_FILE_NAME: &str = "custody.json"; +const CONFIG_FILE_NAME: &str = "config.toml"; const VIEW_FILE_NAME: &str = "pcli-view.sqlite"; #[derive(Debug)] @@ -36,9 +35,7 @@ pub struct App { /// correctly, this can be unwrapped safely. pub view: Option>, pub custody: CustodyProtocolServiceClient, - pub fvk: FullViewingKey, - pub wallet: KeyStore, - pub pd_url: Url, + pub config: PcliConfig, } impl App { @@ -49,7 +46,7 @@ impl App { async fn sync(&mut self) -> Result<()> { let mut status_stream = ViewClient::status_stream( self.view.as_mut().expect("view service initialized"), - self.fvk.wallet_id(), + self.config.full_viewing_key.wallet_id(), ) .await?; @@ -103,11 +100,11 @@ async fn main() -> Result<()> { fs::create_dir_all(&opt.home) .with_context(|| format!("Failed to create home directory {}", opt.home))?; - // The keys command takes the home dir directly, since it may need to + // The init command takes the home dir directly, since it may need to // create the client state, so handle it specially here so that we can have // common code for the other subcommands. - if let Command::Keys(keys_cmd) = &opt.cmd { - keys_cmd.exec(opt.home.as_path())?; + if let Command::Init(init_cmd) = &opt.cmd { + init_cmd.exec(opt.home.as_path())?; return Ok(()); } @@ -135,7 +132,7 @@ async fn main() -> Result<()> { // concrete type match &cmd { - Command::Keys(_) => unreachable!("wallet command already executed"), + Command::Init(_) => unreachable!("init command already executed"), Command::Debug(_) => unreachable!("debug command already executed"), Command::Transaction(tx_cmd) => tx_cmd.exec(&mut app).await?, Command::View(view_cmd) => view_cmd.exec(&mut app).await?, diff --git a/crates/bin/pcli/src/network.rs b/crates/bin/pcli/src/network.rs index ef48aa0c99..ab8b743f1b 100644 --- a/crates/bin/pcli/src/network.rs +++ b/crates/bin/pcli/src/network.rs @@ -28,7 +28,7 @@ impl App { println!("building transaction..."); let start = std::time::Instant::now(); let tx = penumbra_wallet::build_transaction( - &self.fvk, + &self.config.full_viewing_key, self.view.as_mut().expect("view service initialized"), &mut self.custody, OsRng, @@ -84,16 +84,16 @@ impl App { // TODO: why do we need this here but not in the view crate? pub async fn pd_channel(&self) -> anyhow::Result { - match self.pd_url.scheme() { - "http" => Ok(Channel::from_shared(self.pd_url.to_string())? + match self.config.grpc_url.scheme() { + "http" => Ok(Channel::from_shared(self.config.grpc_url.to_string())? .connect() .await?), - "https" => Ok(Channel::from_shared(self.pd_url.to_string())? + "https" => Ok(Channel::from_shared(self.config.grpc_url.to_string())? .tls_config(ClientTlsConfig::new())? .connect() .await?), other => Err(anyhow::anyhow!("unknown url scheme {other}")) - .with_context(|| format!("could not connect to {}", self.pd_url)), + .with_context(|| format!("could not connect to {}", self.config.grpc_url)), } } diff --git a/crates/bin/pcli/src/opt.rs b/crates/bin/pcli/src/opt.rs index 2cbf86d55b..860a50d320 100644 --- a/crates/bin/pcli/src/opt.rs +++ b/crates/bin/pcli/src/opt.rs @@ -1,5 +1,6 @@ use crate::{ - box_grpc_svc::{self, BoxGrpcService}, + box_grpc_svc, + config::{CustodyConfig, PcliConfig}, App, Command, }; use anyhow::Result; @@ -7,7 +8,6 @@ use camino::Utf8PathBuf; use clap::Parser; use directories::ProjectDirs; use penumbra_custody::soft_kms::SoftKms; -use penumbra_keys::FullViewingKey; use penumbra_proto::{ custody::v1alpha1::{ custody_protocol_service_client::CustodyProtocolServiceClient, @@ -19,9 +19,7 @@ use penumbra_proto::{ }, }; use penumbra_view::ViewService; -use penumbra_wallet::KeyStore; use tracing_subscriber::EnvFilter; -use url::Url; #[derive(Debug, Parser)] #[clap( @@ -30,34 +28,18 @@ use url::Url; version = env!("VERGEN_GIT_SEMVER"), )] pub struct Opt { - /// The remote URL of the pd gRPC endpoint - #[clap( - short, - long, - default_value = "https://grpc.testnet.penumbra.zone", - env = "PENUMBRA_NODE_PD_URL", - parse(try_from_str = Url::parse), - )] - node: Url, #[clap(subcommand)] pub cmd: Command, /// The home directory used to store configuration and data. #[clap(long, default_value_t = default_home(), env = "PENUMBRA_PCLI_HOME")] pub home: Utf8PathBuf, - /// If set, use a remote view service instead of local synchronization. - /// Should be specified as a URL, e.g. http://127.0.0.1:8081. - #[clap(short, long, env = "PENUMBRA_VIEW_ADDRESS")] - view_address: Option, - /// The filter for `pcli`'s log messages. - #[clap( long, default_value_t = EnvFilter::new("warn"), env = "RUST_LOG")] - tracing_filter: EnvFilter, } impl Opt { pub fn init_tracing(&mut self) { tracing_subscriber::fmt() .with_env_filter( - std::mem::take(&mut self.tracing_filter) + EnvFilter::from_default_env() // Without explicitly disabling the `r1cs` target, the ZK proof implementations // will spend an enormous amount of CPU and memory building useless tracing output. .add_directive( @@ -70,62 +52,68 @@ impl Opt { .init(); } + pub fn load_config(&self) -> Result { + let path = self.home.join(crate::CONFIG_FILE_NAME); + PcliConfig::load(path) + } + pub async fn into_app(self) -> Result<(App, Command)> { - let home = self.home.clone(); - let custody_path = home.join(crate::CUSTODY_FILE_NAME); + let config = self.load_config()?; // Build the custody service... - let wallet = KeyStore::load(custody_path)?; - let soft_kms = SoftKms::new(wallet.spend_key.clone().into()); - let custody_svc = CustodyProtocolServiceServer::new(soft_kms); - let custody = CustodyProtocolServiceClient::new(box_grpc_svc::local(custody_svc)); - - let fvk = wallet.spend_key.full_viewing_key().clone(); + let custody = match &config.custody { + CustodyConfig::ViewOnly => { + tracing::info!("using view-only custody service"); + let null_kms = penumbra_custody::null_kms::NullKms::default(); + let custody_svc = CustodyProtocolServiceServer::new(null_kms); + CustodyProtocolServiceClient::new(box_grpc_svc::local(custody_svc)) + } + CustodyConfig::SoftKms(config) => { + tracing::info!("using software KMS custody service"); + let soft_kms = SoftKms::new(config.clone()); + let custody_svc = CustodyProtocolServiceServer::new(soft_kms); + CustodyProtocolServiceClient::new(box_grpc_svc::local(custody_svc)) + } + }; // ...and the view service... - let view = if !self.cmd.offline() { - Some(self.view_client(&fvk).await?) - } else { - None - }; + let view = match (self.cmd.offline(), &config.view_url) { + // In offline mode, don't construct a view service at all. + (true, _) => None, + (false, Some(view_url)) => { + // Use a remote view service. + tracing::info!(%view_url, "using remote view service"); + + let ep = tonic::transport::Endpoint::new(view_url.to_string())?; + Some(ViewProtocolServiceClient::new( + box_grpc_svc::connect(ep).await?, + )) + } + (false, None) => { + // Use an in-memory view service. + let path = self.home.join(crate::VIEW_FILE_NAME); + tracing::info!(%path, "using local view service"); - let pd_url = self.node; + let svc = ViewService::load_or_initialize( + Some(path), + &config.full_viewing_key, + config.grpc_url.clone(), + ) + .await?; + + // Now build the view and custody clients, doing gRPC with ourselves + let svc = ViewProtocolServiceServer::new(svc); + Some(ViewProtocolServiceClient::new(box_grpc_svc::local(svc))) + } + }; let app = App { view, custody, - fvk, - wallet, - pd_url, + config, }; Ok((app, self.cmd)) } - - /// Constructs a [`ViewProtocolServiceClient`] based on the command-line options. - async fn view_client( - &self, - fvk: &FullViewingKey, - ) -> Result> { - let svc = if let Some(address) = self.view_address.clone() { - // Use a remote view service. - tracing::info!(%address, "using remote view service"); - - let ep = tonic::transport::Endpoint::new(address.to_string())?; - box_grpc_svc::connect(ep).await? - } else { - // Use an in-memory view service. - let path = self.home.join(crate::VIEW_FILE_NAME); - tracing::info!(%path, "using local view service"); - - let svc = ViewService::load_or_initialize(Some(path), fvk, self.node.clone()).await?; - - // Now build the view and custody clients, doing gRPC with ourselves - let svc = ViewProtocolServiceServer::new(svc); - box_grpc_svc::local(svc) - }; - - Ok(ViewProtocolServiceClient::new(svc)) - } } fn default_home() -> Utf8PathBuf { diff --git a/crates/bin/pcli/tests/network_integration.rs b/crates/bin/pcli/tests/network_integration.rs index 5839baf518..56c0bbb96f 100644 --- a/crates/bin/pcli/tests/network_integration.rs +++ b/crates/bin/pcli/tests/network_integration.rs @@ -55,15 +55,15 @@ fn load_wallet_into_tmpdir() -> TempDir { .args([ "--home", tmpdir.path().to_str().unwrap(), - "keys", - "import", - "phrase", + "init", + "soft-kms", + "import-phrase", ]) .write_stdin(SEED_PHRASE) .timeout(std::time::Duration::from_secs(TIMEOUT_COMMAND_SECONDS)); setup_cmd .assert() - .stdout(predicate::str::contains("Saving backup wallet")); + .stdout(predicate::str::contains("Writing generated configs")); tmpdir } @@ -779,8 +779,6 @@ fn swap() { // .stdout(predicate::str::is_match(format!(r"1\s*1000penumbra")).unwrap()); // } -/* -// TODO: re-enable this after restoring governance functionality. #[ignore] #[test] fn governance_submit_proposal() { @@ -813,6 +811,8 @@ fn governance_submit_proposal() { "submit", "--file", "proposal.toml", + "--deposit-amount", + "10000000", ]) .timeout(std::time::Duration::from_secs(TIMEOUT_COMMAND_SECONDS)); submit_cmd.assert().success(); @@ -830,7 +830,6 @@ fn governance_submit_proposal() { .timeout(std::time::Duration::from_secs(TIMEOUT_COMMAND_SECONDS)); proposals_cmd.assert().success(); } -*/ #[ignore] #[test] @@ -1200,8 +1199,6 @@ fn test_orders() { withdraw_cmd.assert().success(); } -/* -// TODO: re-enable this after restoring governance functionality. #[ignore] #[test] fn delegate_submit_proposal_and_vote() { @@ -1281,6 +1278,8 @@ fn delegate_submit_proposal_and_vote() { "submit", "--file", template_path, + "--deposit-amount", + "10000000", ]) .timeout(std::time::Duration::from_secs(TIMEOUT_COMMAND_SECONDS)); submit_proposal_cmd.assert().success(); @@ -1315,4 +1314,3 @@ fn delegate_submit_proposal_and_vote() { .assert() .success(); } - */ diff --git a/crates/bin/pclientd/Cargo.toml b/crates/bin/pclientd/Cargo.toml index ae92401755..b845f36b17 100644 --- a/crates/bin/pclientd/Cargo.toml +++ b/crates/bin/pclientd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pclientd" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/bin/pd/Cargo.toml b/crates/bin/pd/Cargo.toml index 9fba3953f3..d981b75120 100644 --- a/crates/bin/pd/Cargo.toml +++ b/crates/bin/pd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pd" -version = "0.62.0" +version = "0.63.1" authors = ["Penumbra Labs "] edition = "2021" description = "The node software for the Penumbra Zone" @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" publish = false # Pin a MSRV. Anything more recent than this value is OK. # If a lower version is used for build, the build will fail. -rust-version = "1.65" +rust-version = "1.73" [features] std = ["ibc-types/std"] @@ -39,7 +39,7 @@ penumbra-compact-block = { path = "../../core/component/compact-block" } penumbra-chain = { path = "../../core/component/chain" } penumbra-transaction = { path = "../../core/transaction" } penumbra-app = { path = "../../core/app" } -penumbra-wallet = { path = "../../wallet" } +penumbra-custody = { path = "../../custody" } penumbra-tower-trace = { path = "../../util/tower-trace" } penumbra-tendermint-proxy = { path = "../../util/tendermint-proxy" } diff --git a/crates/bin/pd/src/main.rs b/crates/bin/pd/src/main.rs index 085d05f493..311f571230 100644 --- a/crates/bin/pd/src/main.rs +++ b/crates/bin/pd/src/main.rs @@ -21,6 +21,7 @@ use pd::testnet::{ join::testnet_join, }; use pd::upgrade::{self, Upgrade}; +use penumbra_proto::core::component::dex::v1alpha1::simulation_service_server::SimulationServiceServer; use penumbra_proto::util::tendermint_proxy::v1alpha1::tendermint_proxy_service_server::TendermintProxyServiceServer; use penumbra_storage::{StateDelta, Storage}; use penumbra_tendermint_proxy::TendermintProxy; @@ -44,7 +45,7 @@ use tendermint::v0_37::abci::{ConsensusRequest, MempoolRequest}; )] struct Opt { /// Enable Tokio Console support. - #[clap(long, default_value = "false")] + #[clap(long)] tokio_console: bool, /// Command to run. #[clap(subcommand)] @@ -114,6 +115,13 @@ enum RootCommand { alias = "tendermint-addr", )] cometbft_addr: Url, + + /// Enable expensive RPCs, such as the trade simulation service. + /// The trade simulation service allows clients to simulate trades without submitting them. + /// This is useful for approximating the cost of a trade before submitting it. + /// But, it is a potential DoS vector, so it is disabled by default. + #[clap(short, long, display_order = 500)] + enable_expensive_rpc: bool, }, /// Generate, join, or reset a testnet. Testnet { @@ -263,6 +271,7 @@ async fn main() -> anyhow::Result<()> { grpc_auto_https, metrics_bind, cometbft_addr, + enable_expensive_rpc, } => { tracing::info!( ?abci_bind, @@ -270,6 +279,7 @@ async fn main() -> anyhow::Result<()> { ?grpc_auto_https, ?metrics_bind, %cometbft_addr, + ?enable_expensive_rpc, "starting pd" ); @@ -364,7 +374,7 @@ async fn main() -> anyhow::Result<()> { use penumbra_shielded_pool::component::rpc::Server as ShieldedPoolServer; use penumbra_stake::component::rpc::Server as StakeServer; - let grpc_server = Server::builder() + let mut grpc_server = Server::builder() .trace_fn(|req| match remote_addr(req) { Some(remote_addr) => { tracing::error_span!("grpc", ?remote_addr) @@ -414,6 +424,12 @@ async fn main() -> anyhow::Result<()> { .build() .with_context(|| "could not configure grpc reflection service")?)); + if enable_expensive_rpc { + grpc_server = grpc_server.add_service(we(SimulationServiceServer::new( + DexServer::new(storage.clone()), + ))); + } + let grpc_server = if let Some(domain) = grpc_auto_https { use pd::auto_https::Wrapper; use rustls_acme::{caches::DirCache, AcmeConfig}; diff --git a/crates/bin/pd/src/testnet/config.rs b/crates/bin/pd/src/testnet/config.rs index cbedcee93f..5f6d534e33 100644 --- a/crates/bin/pd/src/testnet/config.rs +++ b/crates/bin/pd/src/testnet/config.rs @@ -2,8 +2,8 @@ use anyhow::Context; use decaf377_rdsa::{SigningKey, SpendAuth, VerificationKey}; use directories::UserDirs; use penumbra_app::genesis::AppState; +use penumbra_custody::soft_kms::Config as SoftKmsConfig; use penumbra_keys::keys::{SpendKey, SpendKeyBytes}; -use penumbra_wallet::KeyStore; use rand::Rng; use rand_core::OsRng; use regex::{Captures, Regex}; @@ -125,11 +125,12 @@ impl TestnetTendermintConfig { let validator_spend_key_filepath = cb_config_dir.clone().join("validator_custody.json"); tracing::debug!(validator_spend_key_filepath = %validator_spend_key_filepath.display(), "writing validator custody file"); let mut validator_spend_key_file = File::create(validator_spend_key_filepath)?; - let validator_wallet = KeyStore { - spend_key: v.keys.validator_spend_key.clone().into(), - }; + let validator_wallet = SoftKmsConfig::from( + SpendKey::try_from(v.keys.validator_spend_key.clone()) + .expect("spend key should be valid"), + ); validator_spend_key_file - .write_all(serde_json::to_string_pretty(&validator_wallet)?.as_bytes())?; + .write_all(toml::to_string_pretty(&validator_wallet)?.as_bytes())?; Ok(()) } diff --git a/crates/bin/pd/src/testnet/generate.rs b/crates/bin/pd/src/testnet/generate.rs index dc8a1c3798..a2164010f4 100644 --- a/crates/bin/pd/src/testnet/generate.rs +++ b/crates/bin/pd/src/testnet/generate.rs @@ -66,7 +66,7 @@ impl TestnetConfig { epoch_duration: Option, unbonding_epochs: Option, ) -> anyhow::Result { - let external_addresses = external_addresses.unwrap_or(Vec::new()); + let external_addresses = external_addresses.unwrap_or_default(); let testnet_validators = Self::collect_validators( validators_input_file, @@ -600,25 +600,25 @@ mod tests { fn parse_allocations_from_good_csv() -> anyhow::Result<()> { let csv_content = r#" "amount","denom","address" -"100000","udelegation_penumbravalid1jzcc6vsm29am9ggs8z0d7s9jk9uf8tfrqg7hglc9ufs7r23nu5yqtw77ex","penumbrav2t1tntrwl92wmud955405mhduuvlxqksa00d2yqe3npjafvley64pal4sre3jcjhq5xjmrs56hk2fs8u648zcaarnz57rynqa0vtaxyd6rwev225lx4kku3lrjktrcacjyw5070nj" -"100000","upenumbra","penumbrav2t1tntrwl92wmud955405mhduuvlxqksa00d2yqe3npjafvley64pal4sre3jcjhq5xjmrs56hk2fs8u648zcaarnz57rynqa0vtaxyd6rwev225lx4kku3lrjktrcacjyw5070nj" -"100000","udelegation_penumbravalid1p2hfuch2p8rshyc90qa23gqk82s74tqcu3x2x3y5tfwpzth4vvrq2gv283","penumbrav2t1ctq4cm9fjewj790alfka634n2t32vh32vqnfufw2dpegw7c2a3lw9np2f4pcthl2w2ke4a32cdmmnurd95sjreu92vey3fwj9ccjz2hpudd9kz9xqlwqp39sly8knl0e2esqjw" -"100000","upenumbra","penumbrav2t1ctq4cm9fjewj790alfka634n2t32vh32vqnfufw2dpegw7c2a3lw9np2f4pcthl2w2ke4a32cdmmnurd95sjreu92vey3fwj9ccjz2hpudd9kz9xqlwqp39sly8knl0e2esqjw" -"100000","udelegation_penumbravalid182k8x46hg5vx3ez8ec58ze5yd6a3q4q3fkx45ddt5jahnzz0xyyqdtz7hc","penumbrav2t1ks2ee68kf96zs3p2af2pzcu7p36uep5gynwc38slvs8kpcyk0t0gdseww4aulntzaq9vurahmka99c4frpgehtteur29p5kt39a2qv0nd89etty36s55knlv7e98kl93p8farz" -"100000","upenumbra","penumbrav2t1ks2ee68kf96zs3p2af2pzcu7p36uep5gynwc38slvs8kpcyk0t0gdseww4aulntzaq9vurahmka99c4frpgehtteur29p5kt39a2qv0nd89etty36s55knlv7e98kl93p8farz" -"100000","udelegation_penumbravalid1t2hr2lj5n2jt3hftzjw3strjllnakc7jtw234d229x3zakhaqsqsg9yarw","penumbrav2t1wvjml4xqa70x3ypqa530npy8ygsftyjxc2wfgh4t7yftja7cfrlr7temqcerhnkd6g7qe75r7wg0ny9vvf4wcrd9rttvuhj9fy20yx630g26khmnxd2zvjnhm2t3wqu59e7nzk" -"100000","upenumbra","penumbrav2t1wvjml4xqa70x3ypqa530npy8ygsftyjxc2wfgh4t7yftja7cfrlr7temqcerhnkd6g7qe75r7wg0ny9vvf4wcrd9rttvuhj9fy20yx630g26khmnxd2zvjnhm2t3wqu59e7nzk" +"100000","udelegation_penumbravalid1jzcc6vsm29am9ggs8z0d7s9jk9uf8tfrqg7hglc9ufs7r23nu5yqtw77ex","penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk" +"100000","upenumbra","penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk" +"100000","udelegation_penumbravalid1p2hfuch2p8rshyc90qa23gqk82s74tqcu3x2x3y5tfwpzth4vvrq2gv283","penumbra1xq2e9x7uhfzezwunvazdamlxepf4jr5htsuqnzlsahuayyqxjjwg9lk0aytwm6wfj3jy29rv2kdpen57903s8wxv3jmqwj6m6v5jgn6y2cypfd03rke652k8wmavxra7e9wkrg" +"100000","upenumbra","penumbra1xq2e9x7uhfzezwunvazdamlxepf4jr5htsuqnzlsahuayyqxjjwg9lk0aytwm6wfj3jy29rv2kdpen57903s8wxv3jmqwj6m6v5jgn6y2cypfd03rke652k8wmavxra7e9wkrg" +"100000","udelegation_penumbravalid182k8x46hg5vx3ez8ec58ze5yd6a3q4q3fkx45ddt5jahnzz0xyyqdtz7hc","penumbra100zd92fg6x27wc0mlu48cd6phq420u7ep59kzdalg2cq66mjkyl0xr54z0c64gectnj44mv5k2vyjjsz5gyd5gq33a6wnqzvgu2fz7namz7usazsl6p8wza83gcpwt8q76rc4y" +"100000","upenumbra","penumbra100zd92fg6x27wc0mlu48cd6phq420u7ep59kzdalg2cq66mjkyl0xr54z0c64gectnj44mv5k2vyjjsz5gyd5gq33a6wnqzvgu2fz7namz7usazsl6p8wza83gcpwt8q76rc4y" +"100000","udelegation_penumbravalid1t2hr2lj5n2jt3hftzjw3strjllnakc7jtw234d229x3zakhaqsqsg9yarw","penumbra1xap8sgefy9rl2nfvsse0h4y6c25hy2n20ymr5w7hs28m9xemt3tmz88atyulswumc32sv7h937wnfhyct282de66zm75nk6ywq3d4r32p5ju0cnscj2rraesnrxr9lvk6hcazp" +"100000","upenumbra","penumbra1xap8sgefy9rl2nfvsse0h4y6c25hy2n20ymr5w7hs28m9xemt3tmz88atyulswumc32sv7h937wnfhyct282de66zm75nk6ywq3d4r32p5ju0cnscj2rraesnrxr9lvk6hcazp" "#; let allos = TestnetAllocation::from_reader(csv_content.as_bytes())?; let a1 = &allos[0]; assert!(a1.denom == "udelegation_penumbravalid1jzcc6vsm29am9ggs8z0d7s9jk9uf8tfrqg7hglc9ufs7r23nu5yqtw77ex"); - assert!(a1.address == Address::from_str("penumbrav2t1tntrwl92wmud955405mhduuvlxqksa00d2yqe3npjafvley64pal4sre3jcjhq5xjmrs56hk2fs8u648zcaarnz57rynqa0vtaxyd6rwev225lx4kku3lrjktrcacjyw5070nj")?); + assert!(a1.address == Address::from_str("penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk")?); assert!(a1.amount.value() == 100000); let a2 = &allos[1]; assert!(a2.denom == "upenumbra"); - assert!(a2.address == Address::from_str("penumbrav2t1tntrwl92wmud955405mhduuvlxqksa00d2yqe3npjafvley64pal4sre3jcjhq5xjmrs56hk2fs8u648zcaarnz57rynqa0vtaxyd6rwev225lx4kku3lrjktrcacjyw5070nj")?); + assert!(a2.address == Address::from_str("penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk")?); assert!(a2.amount.value() == 100000); Ok(()) @@ -627,7 +627,7 @@ mod tests { #[test] fn parse_allocations_from_bad_csv() -> anyhow::Result<()> { let csv_content = r#" -"amount","denom","address"\n"100000","udelegation_penumbravalid1jzcc6vsm29am9ggs8z0d7s9jk9uf8tfrqg7hglc9ufs7r23nu5yqtw77ex","penumbrav2t1tntrwl92wmud955405mhduuvlxqksa00d2yqe3npjafvley64pal4sre3jcjhq5xjmrs56hk2fs8u648zcaarnz57rynqa0vtaxyd6rwev225lx4kku3lrjktrcacjyw5070nj"\n"100000","upenumbra","penumbrav2t1tntrwl92wmud955405mhduuvlxqksa00d2yqe3npjafvley64pal4sre3jcjhq5xjmrs56hk2fs8u648zcaarnz57rynqa0vtaxyd6rwev225lx4kku3lrjktrcacjyw5070nj"\n"100000","udelegation_penumbravalid1p2hfuch2p8rshyc90qa23gqk82s74tqcu3x2x3y5tfwpzth4vvrq2gv283","penumbrav2t1ctq4cm9fjewj790alfka634n2t32vh32vqnfufw2dpegw7c2a3lw9np2f4pcthl2w2ke4a32cdmmnurd95sjreu92vey3fwj9ccjz2hpudd9kz9xqlwqp39sly8knl0e2esqjw"\n"100000","upenumbra","penumbrav2t1ctq4cm9fjewj790alfka634n2t32vh32vqnfufw2dpegw7c2a3lw9np2f4pcthl2w2ke4a32cdmmnurd95sjreu92vey3fwj9ccjz2hpudd9kz9xqlwqp39sly8knl0e2esqjw"\n"100000","udelegation_penumbravalid182k8x46hg5vx3ez8ec58ze5yd6a3q4q3fkx45ddt5jahnzz0xyyqdtz7hc","penumbrav2t1ks2ee68kf96zs3p2af2pzcu7p36uep5gynwc38slvs8kpcyk0t0gdseww4aulntzaq9vurahmka99c4frpgehtteur29p5kt39a2qv0nd89etty36s55knlv7e98kl93p8farz"\n"100000","upenumbra","penumbrav2t1ks2ee68kf96zs3p2af2pzcu7p36uep5gynwc38slvs8kpcyk0t0gdseww4aulntzaq9vurahmka99c4frpgehtteur29p5kt39a2qv0nd89etty36s55knlv7e98kl93p8farz"\n"100000","udelegation_penumbravalid1t2hr2lj5n2jt3hftzjw3strjllnakc7jtw234d229x3zakhaqsqsg9yarw","penumbrav2t1wvjml4xqa70x3ypqa530npy8ygsftyjxc2wfgh4t7yftja7cfrlr7temqcerhnkd6g7qe75r7wg0ny9vvf4wcrd9rttvuhj9fy20yx630g26khmnxd2zvjnhm2t3wqu59e7nzk"\n"100000","upenumbra","penumbrav2t1wvjml4xqa70x3ypqa530npy8ygsftyjxc2wfgh4t7yftja7cfrlr7temqcerhnkd6g7qe75r7wg0ny9vvf4wcrd9rttvuhj9fy20yx630g26khmnxd2zvjnhm2t3wqu59e7nzk"\n +"amount","denom","address"\n"100000","udelegation_penumbravalid1jzcc6vsm29am9ggs8z0d7s9jk9uf8tfrqg7hglc9ufs7r23nu5yqtw77ex","penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk"\n"100000","upenumbra","penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk"\n"100000","udelegation_penumbravalid1p2hfuch2p8rshyc90qa23gqk82s74tqcu3x2x3y5tfwpzth4vvrq2gv283","penumbra1xq2e9x7uhfzezwunvazdamlxepf4jr5htsuqnzlsahuayyqxjjwg9lk0aytwm6wfj3jy29rv2kdpen57903s8wxv3jmqwj6m6v5jgn6y2cypfd03rke652k8wmavxra7e9wkrg"\n"100000","upenumbra","penumbra1xq2e9x7uhfzezwunvazdamlxepf4jr5htsuqnzlsahuayyqxjjwg9lk0aytwm6wfj3jy29rv2kdpen57903s8wxv3jmqwj6m6v5jgn6y2cypfd03rke652k8wmavxra7e9wkrg"\n"100000","udelegation_penumbravalid182k8x46hg5vx3ez8ec58ze5yd6a3q4q3fkx45ddt5jahnzz0xyyqdtz7hc","penumbra100zd92fg6x27wc0mlu48cd6phq420u7ep59kzdalg2cq66mjkyl0xr54z0c64gectnj44mv5k2vyjjsz5gyd5gq33a6wnqzvgu2fz7namz7usazsl6p8wza83gcpwt8q76rc4y"\n"100000","upenumbra","penumbra100zd92fg6x27wc0mlu48cd6phq420u7ep59kzdalg2cq66mjkyl0xr54z0c64gectnj44mv5k2vyjjsz5gyd5gq33a6wnqzvgu2fz7namz7usazsl6p8wza83gcpwt8q76rc4y"\n"100000","udelegation_penumbravalid1t2hr2lj5n2jt3hftzjw3strjllnakc7jtw234d229x3zakhaqsqsg9yarw","penumbra1xap8sgefy9rl2nfvsse0h4y6c25hy2n20ymr5w7hs28m9xemt3tmz88atyulswumc32sv7h937wnfhyct282de66zm75nk6ywq3d4r32p5ju0cnscj2rraesnrxr9lvk6hcazp"\n"100000","upenumbra","penumbra1xap8sgefy9rl2nfvsse0h4y6c25hy2n20ymr5w7hs28m9xemt3tmz88atyulswumc32sv7h937wnfhyct282de66zm75nk6ywq3d4r32p5ju0cnscj2rraesnrxr9lvk6hcazp"\n "#; let result = TestnetAllocation::from_reader(csv_content.as_bytes()); assert!(result.is_err()); diff --git a/crates/bin/pd/src/testnet/join.rs b/crates/bin/pd/src/testnet/join.rs index 87bd6018a6..d9d9fb772d 100644 --- a/crates/bin/pd/src/testnet/join.rs +++ b/crates/bin/pd/src/testnet/join.rs @@ -215,10 +215,8 @@ pub async fn fetch_peers(tm_url: &Url) -> anyhow::Result> ); seeds.push(peer_tm_address) // Otherwise, we check if we've found enough. - } else { - if peers.len() <= threshold { - peers.push(peer_tm_address) - } + } else if peers.len() <= threshold { + peers.push(peer_tm_address) } } if peers.len() < threshold && seeds.is_empty() { diff --git a/crates/core/app/Cargo.toml b/crates/core/app/Cargo.toml index 29d8efe9d9..48fb99740b 100644 --- a/crates/core/app/Cargo.toml +++ b/crates/core/app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-app" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/app/src/action_handler/actions.rs b/crates/core/app/src/action_handler/actions.rs index bef6fdfde2..344da024bb 100644 --- a/crates/core/app/src/action_handler/actions.rs +++ b/crates/core/app/src/action_handler/actions.rs @@ -4,6 +4,7 @@ use anyhow::Result; use async_trait::async_trait; use penumbra_chain::TransactionContext; use penumbra_ibc::component::StateReadExt as _; +use penumbra_shielded_pool::component::Ics20Transfer; use penumbra_storage::{StateRead, StateWrite}; use penumbra_transaction::Action; @@ -19,31 +20,36 @@ impl ActionHandler for Action { async fn check_stateless(&self, context: TransactionContext) -> Result<()> { match self { // These actions require a context - Action::SwapClaim(action) => action.check_stateless(context), - Action::Spend(action) => action.check_stateless(context), - Action::DelegatorVote(action) => action.check_stateless(context), + Action::SwapClaim(action) => action.check_stateless(context).await, + Action::Spend(action) => action.check_stateless(context).await, + Action::DelegatorVote(action) => action.check_stateless(context).await, // These actions don't require a context - Action::Delegate(action) => action.check_stateless(()), - Action::Undelegate(action) => action.check_stateless(()), - Action::UndelegateClaim(action) => action.check_stateless(()), - Action::ValidatorDefinition(action) => action.check_stateless(()), - Action::ValidatorVote(action) => action.check_stateless(()), - Action::PositionClose(action) => action.check_stateless(()), - Action::PositionOpen(action) => action.check_stateless(()), - Action::PositionRewardClaim(action) => action.check_stateless(()), - Action::PositionWithdraw(action) => action.check_stateless(()), - Action::ProposalSubmit(action) => action.check_stateless(()), - Action::ProposalWithdraw(action) => action.check_stateless(()), - Action::ProposalDepositClaim(action) => action.check_stateless(()), - Action::Swap(action) => action.check_stateless(()), - Action::Output(action) => action.check_stateless(()), - Action::IbcAction(action) => action.check_stateless(()), - Action::Ics20Withdrawal(action) => action.check_stateless(()), - Action::DaoSpend(action) => action.check_stateless(()), - Action::DaoOutput(action) => action.check_stateless(()), - Action::DaoDeposit(action) => action.check_stateless(()), + Action::Delegate(action) => action.check_stateless(()).await, + Action::Undelegate(action) => action.check_stateless(()).await, + Action::UndelegateClaim(action) => action.check_stateless(()).await, + Action::ValidatorDefinition(action) => action.check_stateless(()).await, + Action::ValidatorVote(action) => action.check_stateless(()).await, + Action::PositionClose(action) => action.check_stateless(()).await, + Action::PositionOpen(action) => action.check_stateless(()).await, + Action::PositionRewardClaim(action) => action.check_stateless(()).await, + Action::PositionWithdraw(action) => action.check_stateless(()).await, + Action::ProposalSubmit(action) => action.check_stateless(()).await, + Action::ProposalWithdraw(action) => action.check_stateless(()).await, + Action::ProposalDepositClaim(action) => action.check_stateless(()).await, + Action::Swap(action) => action.check_stateless(()).await, + Action::Output(action) => action.check_stateless(()).await, + Action::IbcAction(action) => { + action + .clone() + .with_handler::() + .check_stateless(()) + .await + } + Action::Ics20Withdrawal(action) => action.check_stateless(()).await, + Action::DaoSpend(action) => action.check_stateless(()).await, + Action::DaoOutput(action) => action.check_stateless(()).await, + Action::DaoDeposit(action) => action.check_stateless(()).await, } - .await } async fn check_stateful(&self, state: Arc) -> Result<()> { @@ -70,7 +76,11 @@ impl ActionHandler for Action { anyhow::bail!("transaction contains IBC actions, but IBC is not enabled"); } - action.check_stateful(state).await + action + .clone() + .with_handler::() + .check_stateful(state) + .await } Action::Ics20Withdrawal(action) => action.check_stateful(state).await, Action::DaoSpend(action) => action.check_stateful(state).await, @@ -98,7 +108,13 @@ impl ActionHandler for Action { Action::SwapClaim(action) => action.execute(state).await, Action::Spend(action) => action.execute(state).await, Action::Output(action) => action.execute(state).await, - Action::IbcAction(action) => action.execute(state).await, + Action::IbcAction(action) => { + action + .clone() + .with_handler::() + .execute(state) + .await + } Action::Ics20Withdrawal(action) => action.execute(state).await, Action::DaoSpend(action) => action.execute(state).await, Action::DaoOutput(action) => action.execute(state).await, diff --git a/crates/core/app/src/genesis/app_state.rs b/crates/core/app/src/genesis/app_state.rs index 2c6c7e6e52..018a9774ff 100644 --- a/crates/core/app/src/genesis/app_state.rs +++ b/crates/core/app/src/genesis/app_state.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; /// The application state at genesis. #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(try_from = "pb::GenesisAppState", into = "pb::GenesisAppState")] +#[allow(clippy::large_enum_variant)] pub enum AppState { /// The application state at genesis. Content(Content), diff --git a/crates/core/asset/Cargo.toml b/crates/core/asset/Cargo.toml index 6e3dc164b9..a725005768 100644 --- a/crates/core/asset/Cargo.toml +++ b/crates/core/asset/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-asset" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/asset/src/asset/denom_metadata.rs b/crates/core/asset/src/asset/denom_metadata.rs index ff83ad3cbf..3051219145 100644 --- a/crates/core/asset/src/asset/denom_metadata.rs +++ b/crates/core/asset/src/asset/denom_metadata.rs @@ -380,7 +380,7 @@ impl Eq for DenomMetadata {} impl PartialOrd for DenomMetadata { fn partial_cmp(&self, other: &Self) -> Option { - self.inner.base_denom.partial_cmp(&other.inner.base_denom) + Some(self.cmp(other)) } } diff --git a/crates/core/component/chain/Cargo.toml b/crates/core/component/chain/Cargo.toml index acd8e46cc8..0bcc6ca21c 100644 --- a/crates/core/component/chain/Cargo.toml +++ b/crates/core/component/chain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-chain" -version = "0.62.0" +version = "0.63.1" edition = "2021" [dependencies] diff --git a/crates/core/component/chain/src/lib.rs b/crates/core/component/chain/src/lib.rs index 54909993e0..74783b9704 100644 --- a/crates/core/component/chain/src/lib.rs +++ b/crates/core/component/chain/src/lib.rs @@ -35,9 +35,9 @@ pub mod test_keys { pub const SEED_PHRASE: &str = "comfort ten front cycle churn burger oak absent rice ice urge result art couple benefit cabbage frequent obscure hurry trick segment cool job debate"; /// These addresses both correspond to the test wallet above. - pub const ADDRESS_0_STR: &str = "penumbrav2t147mfall0zr6am5r45qkwht7xqqrdsp50czde7empv7yq2nk3z8yyfh9k9520ddgswkmzar22vhz9dwtuem7uxw0qytfpv7lk3q9dp8ccaw2fn5c838rfackazmgf3ahhvhypxd"; + pub const ADDRESS_0_STR: &str = "penumbra147mfall0zr6am5r45qkwht7xqqrdsp50czde7empv7yq2nk3z8yyfh9k9520ddgswkmzar22vhz9dwtuem7uxw0qytfpv7lk3q9dp8ccaw2fn5c838rfackazmgf3ahh09cxmz"; /// These addresses both correspond to the test wallet above. - pub const ADDRESS_1_STR: &str = "penumbrav2t1vmmz304hjlkjq6xv4al5dqumvgk3ek82rneagj07vdqkudjvl6y7zxzr5k6qq24yc7yyyekpu9qm7ef3acg2u8p950hs6hu3e73guq5pfmmvm63qudfx4qmg8h7fdweydr93ku"; + pub const ADDRESS_1_STR: &str = "penumbra1vmmz304hjlkjq6xv4al5dqumvgk3ek82rneagj07vdqkudjvl6y7zxzr5k6qq24yc7yyyekpu9qm7ef3acg2u8p950hs6hu3e73guq5pfmmvm63qudfx4qmg8h7fdweyw3ektn"; pub static ADDRESS_0: Lazy
= Lazy::new(|| { ADDRESS_0_STR diff --git a/crates/core/component/compact-block/Cargo.toml b/crates/core/component/compact-block/Cargo.toml index d801f82153..cfa8e9c17a 100644 --- a/crates/core/component/compact-block/Cargo.toml +++ b/crates/core/component/compact-block/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-compact-block" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/component/Cargo.toml b/crates/core/component/component/Cargo.toml index b401a5dcea..fcd9bc65bf 100644 --- a/crates/core/component/component/Cargo.toml +++ b/crates/core/component/component/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-component" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/dao/Cargo.toml b/crates/core/component/dao/Cargo.toml index 3747166983..a41aa8b530 100644 --- a/crates/core/component/dao/Cargo.toml +++ b/crates/core/component/dao/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-dao" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/dex/Cargo.toml b/crates/core/component/dex/Cargo.toml index d55544c877..904e9d3ac8 100644 --- a/crates/core/component/dex/Cargo.toml +++ b/crates/core/component/dex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-dex" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/distributions/Cargo.toml b/crates/core/component/distributions/Cargo.toml index 8e905fe8aa..5987949350 100644 --- a/crates/core/component/distributions/Cargo.toml +++ b/crates/core/component/distributions/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-distributions" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/fee/Cargo.toml b/crates/core/component/fee/Cargo.toml index 0b254cce67..c1cd1e3d2d 100644 --- a/crates/core/component/fee/Cargo.toml +++ b/crates/core/component/fee/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-fee" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/governance/Cargo.toml b/crates/core/component/governance/Cargo.toml index 158e95d439..dc250a7a01 100644 --- a/crates/core/component/governance/Cargo.toml +++ b/crates/core/component/governance/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-governance" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -59,6 +59,7 @@ ark-ff = { version = "0.4", default_features = false } ark-serialize = "0.4" ark-groth16 = { version = "0.4", default-features = false } ark-snark = "0.4" +async-stream = "0.2" metrics = "0.19.0" serde = { version = "1", features = ["derive"] } tracing = "0.1" diff --git a/crates/core/component/governance/src/component/rpc.rs b/crates/core/component/governance/src/component/rpc.rs index 925cca9458..9f13cf03f7 100644 --- a/crates/core/component/governance/src/component/rpc.rs +++ b/crates/core/component/governance/src/component/rpc.rs @@ -1,20 +1,35 @@ use std::pin::Pin; +use std::str::FromStr; +use anyhow::Context; +use async_stream::try_stream; use futures::{StreamExt, TryStreamExt}; use penumbra_chain::component::StateReadExt as _; +use penumbra_num::Amount; +use penumbra_proto::core::component::governance::v1alpha1::AllTalliedDelegatorVotesForProposalRequest; +use penumbra_proto::core::component::governance::v1alpha1::AllTalliedDelegatorVotesForProposalResponse; +use penumbra_proto::core::component::governance::v1alpha1::NextProposalIdRequest; +use penumbra_proto::core::component::governance::v1alpha1::NextProposalIdResponse; +use penumbra_proto::core::component::governance::v1alpha1::VotingPowerAtProposalStartRequest; +use penumbra_proto::core::component::governance::v1alpha1::VotingPowerAtProposalStartResponse; use penumbra_proto::{ core::component::governance::v1alpha1::{ - query_service_server::QueryService, ProposalInfoRequest, ProposalInfoResponse, - ProposalRateDataRequest, ProposalRateDataResponse, + query_service_server::QueryService, ProposalDataRequest, ProposalDataResponse, + ProposalInfoRequest, ProposalInfoResponse, ProposalListRequest, ProposalListResponse, + ProposalRateDataRequest, ProposalRateDataResponse, ValidatorVotesRequest, + ValidatorVotesResponse, }, StateReadProto, }; use penumbra_stake::rate::RateData; +use penumbra_stake::IdentityKey; use penumbra_storage::Storage; use tonic::Status; use tracing::instrument; use crate::state_key; +use crate::Tally; +use crate::Vote; use super::StateReadExt; @@ -40,20 +55,20 @@ impl QueryService for Server { state .check_chain_id(&request.get_ref().chain_id) .await - .map_err(|e| tonic::Status::unknown(format!("chain_id not OK: {e}")))?; + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; let proposal_id = request.into_inner().proposal_id; let start_block_height = state .proposal_voting_start(proposal_id) .await .map_err(|e| tonic::Status::internal(e.to_string()))? - .ok_or_else(|| tonic::Status::unknown(format!("proposal {proposal_id} not found")))?; + .ok_or_else(|| tonic::Status::not_found(format!("proposal {proposal_id} not found")))?; let start_position = state .proposal_voting_start_position(proposal_id) .await .map_err(|e| tonic::Status::internal(e.to_string()))? - .ok_or_else(|| tonic::Status::unknown(format!("proposal {proposal_id} not found")))?; + .ok_or_else(|| tonic::Status::not_found(format!("proposal {proposal_id} not found")))?; Ok(tonic::Response::new(ProposalInfoResponse { start_block_height, @@ -61,6 +76,97 @@ impl QueryService for Server { })) } + #[instrument(skip(self, request))] + async fn next_proposal_id( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + state + .check_chain_id(&request.get_ref().chain_id) + .await + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; + + let next_proposal_id: u64 = state + .get_proto(state_key::next_proposal_id()) + .await + .map_err(|e| tonic::Status::internal(format!("unable to fetch next proposal id: {e}")))? + .ok_or_else(|| tonic::Status::not_found("there are no proposals yet".to_string()))?; + + Ok(tonic::Response::new(NextProposalIdResponse { + next_proposal_id, + })) + } + + #[instrument(skip(self, request))] + async fn proposal_data( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + state + .check_chain_id(&request.get_ref().chain_id) + .await + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; + let proposal_id = request.into_inner().proposal_id; + + let start_block_height = state + .proposal_voting_start(proposal_id) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))? + .ok_or_else(|| tonic::Status::not_found(format!("proposal {proposal_id} not found")))?; + + let end_block_height = state + .proposal_voting_end(proposal_id) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))? + .ok_or_else(|| tonic::Status::not_found(format!("proposal {proposal_id} not found")))?; + + let start_position = state + .proposal_voting_start_position(proposal_id) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))? + .ok_or_else(|| tonic::Status::not_found(format!("proposal {proposal_id} not found")))?; + + let proposal = state + .proposal_definition(proposal_id) + .await + .map_err(|e| tonic::Status::internal(format!("unable to fetch proposal: {e}")))? + .ok_or_else(|| { + tonic::Status::not_found(format!("proposal {} not found", proposal_id)) + })?; + + let proposal_state = state + .proposal_state(proposal_id) + .await + .map_err(|e| tonic::Status::internal(format!("unable to fetch proposal state: {e}")))? + .ok_or_else(|| { + tonic::Status::not_found(format!("proposal {} state not found", proposal_id)) + })?; + + let proposal_deposit_amount: Amount = state + .get(&state_key::proposal_deposit_amount(proposal_id)) + .await + .map_err(|e| { + tonic::Status::internal(format!("unable to fetch proposal deposit amount: {e}")) + })? + .ok_or_else(|| { + tonic::Status::not_found(format!( + "deposit amount for proposal {} was not found", + proposal_id + )) + })?; + + Ok(tonic::Response::new(ProposalDataResponse { + start_block_height, + end_block_height, + start_position: start_position.into(), + state: Some(proposal_state.into()), + proposal: Some(proposal.into()), + proposal_deposit_amount: Some(proposal_deposit_amount.into()), + })) + } + type ProposalRateDataStream = Pin< Box> + Send>, >; @@ -74,7 +180,7 @@ impl QueryService for Server { state .check_chain_id(&request.get_ref().chain_id) .await - .map_err(|e| tonic::Status::unknown(format!("chain_id not OK: {e}")))?; + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; let proposal_id = request.into_inner().proposal_id; let s = state.prefix(&state_key::all_rate_data_at_proposal_start(proposal_id)); @@ -93,4 +199,240 @@ impl QueryService for Server { .boxed(), )) } + + type ProposalListStream = + Pin> + Send>>; + + #[instrument(skip(self, request))] + async fn proposal_list( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + state + .check_chain_id(&request.get_ref().chain_id) + .await + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; + + let proposal_id_list: Vec = if request.into_inner().inactive { + let next = state.next_proposal_id().await.map_err(|e| { + tonic::Status::internal(format!("unable to get next proposal id: {e}")) + })?; + + (0..next).collect() + } else { + state + .unfinished_proposals() + .await + .map_err(|e| { + tonic::Status::internal(format!("unable to fetch unfinished proposals: {e}")) + })? + .into_iter() + .collect::>() + }; + + let s = try_stream! { + for proposal_id in proposal_id_list { + let proposal = state + .proposal_definition(proposal_id) + .await + .map_err(|e| tonic::Status::internal(format!("unable to fetch proposal: {e}")))? + .ok_or_else(|| { + tonic::Status::not_found(format!("proposal {} not found", proposal_id)) + })?; + + let proposal_state = state + .proposal_state(proposal_id) + .await + .map_err(|e| tonic::Status::internal(format!("unable to fetch proposal state: {e}")))? + .ok_or_else(|| { + tonic::Status::not_found(format!("proposal {} state not found", proposal_id)) + })?; + + let proposal_voting_start_position = state + .proposal_voting_start_position(proposal_id) + .await + .map_err(|e| { + tonic::Status::internal(format!( + "unable to fetch proposal voting start position: {e}" + )) + })? + .ok_or_else(|| { + tonic::Status::not_found(format!( + "voting start position for proposal {} not found", + proposal_id + )) + })?; + + let start_block_height = state + .proposal_voting_start(proposal_id) + .await + .map_err(|e| { + tonic::Status::internal(format!( + "unable to fetch proposal voting start block: {e}" + )) + })? + .ok_or_else(|| { + tonic::Status::not_found(format!( + "voting start block for proposal {} not found", + proposal_id + )) + })?; + + let end_block_height = state + .proposal_voting_end(proposal_id) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))? + .ok_or_else(|| tonic::Status::not_found(format!("proposal {proposal_id} not found")))?; + + yield ProposalListResponse { + proposal: Some(proposal.into()), + start_block_height, + end_block_height, + start_position: proposal_voting_start_position.into(), + state: Some(proposal_state.into()), + } + }}; + + Ok(tonic::Response::new( + s.map_err(|e: anyhow::Error| { + tonic::Status::unavailable(format!( + "error getting position value from storage: {e}" + )) + }) + // TODO: how do we instrument a Stream + //.instrument(Span::current()) + .boxed(), + )) + } + + type ValidatorVotesStream = + Pin> + Send>>; + + #[instrument(skip(self, request))] + async fn validator_votes( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + state + .check_chain_id(&request.get_ref().chain_id) + .await + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; + + let proposal_id = request.into_inner().proposal_id; + + let s = state + .prefix::(&state_key::all_validator_votes_for_proposal(proposal_id)) + .and_then(|r| async move { + Ok(( + IdentityKey::from_str(r.0.rsplit('/').next().context("invalid key")?)?, + r.1, + )) + }) + .map_ok(|i: (IdentityKey, Vote)| ValidatorVotesResponse { + vote: Some(i.1.into()), + identity_key: Some(i.0.into()), + }); + + Ok(tonic::Response::new( + s.map_err(|e: anyhow::Error| { + tonic::Status::unavailable(format!( + "error getting validator votes from storage: {e}" + )) + }) + // TODO: how do we instrument a Stream + //.instrument(Span::current()) + .boxed(), + )) + } + + #[instrument(skip(self, request))] + async fn voting_power_at_proposal_start( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + state + .check_chain_id(&request.get_ref().chain_id) + .await + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; + let request = request.into_inner(); + let proposal_id = request.proposal_id; + let identity_key: IdentityKey = request + .identity_key + .ok_or_else(|| { + tonic::Status::invalid_argument("identity key not included in request".to_string()) + })? + .try_into() + .map_err(|_| { + tonic::Status::invalid_argument( + "identity key in request was bad protobuf".to_string(), + ) + })?; + + let voting_power = state + .get_proto::(&state_key::voting_power_at_proposal_start( + proposal_id, + identity_key, + )) + .await + .map_err(|e| tonic::Status::internal(format!("error accessing storage: {}", e)))?; + + if voting_power.is_none() { + return Err(tonic::Status::not_found(format!( + "validator did not exist at proposal creation: {}", + identity_key + ))); + } + + Ok(tonic::Response::new(VotingPowerAtProposalStartResponse { + voting_power: voting_power.expect("voting power should be set"), + })) + } + + type AllTalliedDelegatorVotesForProposalStream = Pin< + Box< + dyn futures::Stream< + Item = Result, + > + Send, + >, + >; + + #[instrument(skip(self, request))] + async fn all_tallied_delegator_votes_for_proposal( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + state + .check_chain_id(&request.get_ref().chain_id) + .await + .map_err(|e| tonic::Status::invalid_argument(format!("chain_id not OK: {e}")))?; + let proposal_id = request.into_inner().proposal_id; + + let s = state.prefix::(&state_key::all_tallied_delegator_votes_for_proposal( + proposal_id, + )); + Ok(tonic::Response::new( + s.and_then(|r| async move { + Ok(( + IdentityKey::from_str(r.0.rsplit('/').next().context("invalid key")?)?, + r.1, + )) + }) + .map_err(|e| { + tonic::Status::internal(format!("unable to retrieve tallied delegator votes: {e}")) + }) + .map_ok( + |i: (IdentityKey, Tally)| AllTalliedDelegatorVotesForProposalResponse { + tally: Some(i.1.into()), + identity_key: Some(i.0.into()), + }, + ) + // TODO: how do we instrument a Stream + //.instrument(Span::current()) + .boxed(), + )) + } } diff --git a/crates/core/component/governance/src/component/view.rs b/crates/core/component/governance/src/component/view.rs index fd8acd588c..41bfe8f526 100644 --- a/crates/core/component/governance/src/component/view.rs +++ b/crates/core/component/governance/src/component/view.rs @@ -52,6 +52,13 @@ pub trait StateReadExt: StateRead + penumbra_stake::StateReadExt { .unwrap_or_default()) } + /// Get the proposal definition for a proposal. + async fn proposal_definition(&self, proposal_id: u64) -> Result> { + Ok(self + .get(&state_key::proposal_definition(proposal_id)) + .await?) + } + /// Get the proposal payload for a proposal. async fn proposal_payload(&self, proposal_id: u64) -> Result> { Ok(self @@ -100,7 +107,7 @@ pub trait StateReadExt: StateRead + penumbra_stake::StateReadExt { .await?) } - /// Get the proposal voting end block for a given proposal. + /// Get the proposal voting start block for a given proposal. async fn proposal_voting_start(&self, proposal_id: u64) -> Result> { Ok(self .get_proto::(&state_key::proposal_voting_start(proposal_id)) @@ -114,7 +121,7 @@ pub trait StateReadExt: StateRead + penumbra_stake::StateReadExt { .await?) } - /// Get the proposal voting end block for a given proposal. + /// Get the proposal voting start block for a given proposal. async fn proposal_voting_start_position( &self, proposal_id: u64, diff --git a/crates/core/component/governance/src/lib.rs b/crates/core/component/governance/src/lib.rs index 5ed98c2f1f..019a870d32 100644 --- a/crates/core/component/governance/src/lib.rs +++ b/crates/core/component/governance/src/lib.rs @@ -1,5 +1,6 @@ #![deny(clippy::unwrap_used)] #![cfg_attr(docsrs, feature(doc_cfg))] +#![recursion_limit = "512"] pub mod delegator_vote; pub use delegator_vote::{ diff --git a/crates/core/component/ibc/Cargo.toml b/crates/core/component/ibc/Cargo.toml index 01775392aa..a870dd042e 100644 --- a/crates/core/component/ibc/Cargo.toml +++ b/crates/core/component/ibc/Cargo.toml @@ -1,17 +1,16 @@ [package] name = "penumbra-ibc" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] component = [ - "penumbra-component", "penumbra-storage", + "penumbra-component", "penumbra-proto/penumbra-storage", "penumbra-chain/component", - "penumbra-shielded-pool/component", ] default = ["component", "std"] std = ["ibc-types/std"] @@ -24,7 +23,6 @@ penumbra-proto = { path = "../../../proto", default-features = false } penumbra-storage = { path = "../../../storage", optional = true } penumbra-component = { path = "../component", optional = true } penumbra-chain = { path = "../chain", default-features = false } -penumbra-shielded-pool = { path = "../shielded-pool", default-features = false } penumbra-asset = { path = "../../../core/asset", default-features = false } penumbra-num = { path = "../../../core/num", default-features = false } penumbra-keys = { path = "../../../core/keys", default-features = false } diff --git a/crates/core/component/ibc/src/component.rs b/crates/core/component/ibc/src/component.rs index 0b5b7b5b2d..a681940504 100644 --- a/crates/core/component/ibc/src/component.rs +++ b/crates/core/component/ibc/src/component.rs @@ -1,5 +1,4 @@ mod action_handler; -mod app_handler; mod channel; mod client; mod client_counter; @@ -12,12 +11,14 @@ pub mod rpc; mod ibc_component; mod metrics; mod msg_handler; -mod packet; mod proof_verification; -mod state_key; -mod transfer; mod view; +pub mod app_handler; +pub mod ibc_action_with_handler; +pub mod packet; +pub mod state_key; + use msg_handler::MsgHandler; pub use self::metrics::register_metrics; diff --git a/crates/core/component/ibc/src/component/action_handler.rs b/crates/core/component/ibc/src/component/action_handler.rs index 2012fdab34..1749558ac4 100644 --- a/crates/core/component/ibc/src/component/action_handler.rs +++ b/crates/core/component/ibc/src/component/action_handler.rs @@ -1,2 +1 @@ mod ibc_action; -mod ics20_withdrawal; diff --git a/crates/core/component/ibc/src/component/action_handler/ibc_action.rs b/crates/core/component/ibc/src/component/action_handler/ibc_action.rs index 46fa414460..dc88b75834 100644 --- a/crates/core/component/ibc/src/component/action_handler/ibc_action.rs +++ b/crates/core/component/ibc/src/component/action_handler/ibc_action.rs @@ -5,30 +5,34 @@ use async_trait::async_trait; use penumbra_component::ActionHandler; use penumbra_storage::{StateRead, StateWrite}; -use crate::{component::MsgHandler as LocalActionHandler, IbcAction}; +use crate::{ + component::{app_handler::AppHandler, MsgHandler as _}, + IbcAction, IbcActionWithHandler, +}; #[async_trait] -impl ActionHandler for IbcAction { +impl ActionHandler for IbcActionWithHandler { type CheckStatelessContext = (); async fn check_stateless(&self, _context: ()) -> Result<()> { - match self { - IbcAction::CreateClient(msg) => msg.check_stateless().await?, - IbcAction::UpdateClient(msg) => msg.check_stateless().await?, - IbcAction::UpgradeClient(msg) => msg.check_stateless().await?, - IbcAction::SubmitMisbehavior(msg) => msg.check_stateless().await?, - IbcAction::ConnectionOpenInit(msg) => msg.check_stateless().await?, - IbcAction::ConnectionOpenTry(msg) => msg.check_stateless().await?, - IbcAction::ConnectionOpenAck(msg) => msg.check_stateless().await?, - IbcAction::ConnectionOpenConfirm(msg) => msg.check_stateless().await?, - IbcAction::ChannelOpenInit(msg) => msg.check_stateless().await?, - IbcAction::ChannelOpenTry(msg) => msg.check_stateless().await?, - IbcAction::ChannelOpenAck(msg) => msg.check_stateless().await?, - IbcAction::ChannelOpenConfirm(msg) => msg.check_stateless().await?, - IbcAction::ChannelCloseInit(msg) => msg.check_stateless().await?, - IbcAction::ChannelCloseConfirm(msg) => msg.check_stateless().await?, - IbcAction::RecvPacket(msg) => msg.check_stateless().await?, - IbcAction::Acknowledgement(msg) => msg.check_stateless().await?, - IbcAction::Timeout(msg) => msg.check_stateless().await?, + let action = self.action(); + match action { + IbcAction::CreateClient(msg) => msg.check_stateless::().await?, + IbcAction::UpdateClient(msg) => msg.check_stateless::().await?, + IbcAction::UpgradeClient(msg) => msg.check_stateless::().await?, + IbcAction::SubmitMisbehavior(msg) => msg.check_stateless::().await?, + IbcAction::ConnectionOpenInit(msg) => msg.check_stateless::().await?, + IbcAction::ConnectionOpenTry(msg) => msg.check_stateless::().await?, + IbcAction::ConnectionOpenAck(msg) => msg.check_stateless::().await?, + IbcAction::ConnectionOpenConfirm(msg) => msg.check_stateless::().await?, + IbcAction::ChannelOpenInit(msg) => msg.check_stateless::().await?, + IbcAction::ChannelOpenTry(msg) => msg.check_stateless::().await?, + IbcAction::ChannelOpenAck(msg) => msg.check_stateless::().await?, + IbcAction::ChannelOpenConfirm(msg) => msg.check_stateless::().await?, + IbcAction::ChannelCloseInit(msg) => msg.check_stateless::().await?, + IbcAction::ChannelCloseConfirm(msg) => msg.check_stateless::().await?, + IbcAction::RecvPacket(msg) => msg.check_stateless::().await?, + IbcAction::Acknowledgement(msg) => msg.check_stateless::().await?, + IbcAction::Timeout(msg) => msg.check_stateless::().await?, IbcAction::Unknown(msg) => { anyhow::bail!("unknown IBC message type: {}", msg.type_url) } @@ -43,75 +47,76 @@ impl ActionHandler for IbcAction { } async fn execute(&self, state: S) -> Result<()> { - match self { + let action = self.action(); + match action { IbcAction::CreateClient(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute CreateClient message")?, + .context("failed to execute MsgCreateClient")?, IbcAction::UpdateClient(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute UpdateClient message")?, + .context("failed to execute MsgUpdateClient")?, IbcAction::UpgradeClient(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute UpgradeClient message")?, + .context("failed to execute MsgUpgradeClient")?, IbcAction::SubmitMisbehavior(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute SubmitMisbehavior message")?, + .context("failed to execute MsgSubmitMisbehaviour")?, IbcAction::ConnectionOpenInit(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ConnectionOpenInit message")?, + .context("failed to execute MsgConnectionOpenInit")?, IbcAction::ConnectionOpenTry(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ConnectionOpenTry message")?, + .context("failed to execute MsgConnectionOpenTry")?, IbcAction::ConnectionOpenAck(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ConnectionOpenAck message")?, + .context("failed to execute MsgConnectionOpenAck")?, IbcAction::ConnectionOpenConfirm(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ConnectionOpenConfirm message")?, + .context("failed to execute MsgConnectionOpenConfirm")?, IbcAction::ChannelOpenInit(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ChannelOpenInit message")?, + .context("failed to execute MsgChannelOpenInit")?, IbcAction::ChannelOpenTry(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ChannelOpenTry message")?, + .context("failed to execute MsgChannelOpenTry")?, IbcAction::ChannelOpenAck(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ChannelOpenAck message")?, + .context("failed to execute MsgChannelOpenAck")?, IbcAction::ChannelOpenConfirm(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ChannelOpenConfirm message")?, + .context("failed to execute MsgChannelOpenConfirm")?, IbcAction::ChannelCloseInit(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ChannelCloseInit message")?, + .context("failed to execute MsgChannelCloseInit")?, IbcAction::ChannelCloseConfirm(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute ChannelCloseConfirm message")?, + .context("failed to execute MsgChannelCloseConfirm")?, IbcAction::RecvPacket(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute RecvPacket message")?, + .context("failed to execute MsgRecvPacket")?, IbcAction::Acknowledgement(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute Acknowledgement message")?, + .context("failed to execute MsgAcknowledgement")?, IbcAction::Timeout(msg) => msg - .try_execute(state) + .try_execute::(state) .await - .with_context(|| "Failed to execute Timeout message")?, + .context("failed to execute MsgTimeout")?, IbcAction::Unknown(msg) => { anyhow::bail!("unknown IBC message type: {}", msg.type_url) } diff --git a/crates/core/component/ibc/src/component/client.rs b/crates/core/component/ibc/src/component/client.rs index eda03481d2..8ba4337e20 100644 --- a/crates/core/component/ibc/src/component/client.rs +++ b/crates/core/component/ibc/src/component/client.rs @@ -363,8 +363,90 @@ mod tests { use std::str::FromStr; use tendermint::Time; + use crate::component::ibc_action_with_handler::IbcActionWithHandler; use crate::IbcAction; + use crate::component::app_handler::{AppHandler, AppHandlerCheck, AppHandlerExecute}; + use ibc_types::core::channel::msgs::{ + MsgAcknowledgement, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck, + MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, MsgRecvPacket, MsgTimeout, + }; + + struct MockAppHandler {} + + #[async_trait] + impl AppHandlerCheck for MockAppHandler { + async fn chan_open_init_check( + _state: S, + _msg: &MsgChannelOpenInit, + ) -> Result<()> { + Ok(()) + } + async fn chan_open_try_check( + _state: S, + _msg: &MsgChannelOpenTry, + ) -> Result<()> { + Ok(()) + } + async fn chan_open_ack_check( + _state: S, + _msg: &MsgChannelOpenAck, + ) -> Result<()> { + Ok(()) + } + async fn chan_open_confirm_check( + _state: S, + _msg: &MsgChannelOpenConfirm, + ) -> Result<()> { + Ok(()) + } + async fn chan_close_confirm_check( + _state: S, + _msg: &MsgChannelCloseConfirm, + ) -> Result<()> { + Ok(()) + } + async fn chan_close_init_check( + _state: S, + _msg: &MsgChannelCloseInit, + ) -> Result<()> { + Ok(()) + } + async fn recv_packet_check(_state: S, _msg: &MsgRecvPacket) -> Result<()> { + Ok(()) + } + async fn timeout_packet_check(_state: S, _msg: &MsgTimeout) -> Result<()> { + Ok(()) + } + async fn acknowledge_packet_check( + _state: S, + _msg: &MsgAcknowledgement, + ) -> Result<()> { + Ok(()) + } + } + + #[async_trait] + impl AppHandlerExecute for MockAppHandler { + async fn chan_open_init_execute(_state: S, _msg: &MsgChannelOpenInit) {} + async fn chan_open_try_execute(_state: S, _msg: &MsgChannelOpenTry) {} + async fn chan_open_ack_execute(_state: S, _msg: &MsgChannelOpenAck) {} + async fn chan_open_confirm_execute(_state: S, _msg: &MsgChannelOpenConfirm) { + } + async fn chan_close_confirm_execute( + _state: S, + _msg: &MsgChannelCloseConfirm, + ) { + } + async fn chan_close_init_execute(_state: S, _msg: &MsgChannelCloseInit) {} + async fn recv_packet_execute(_state: S, _msg: &MsgRecvPacket) {} + async fn timeout_packet_execute(_state: S, _msg: &MsgTimeout) {} + async fn acknowledge_packet_execute(_state: S, _msg: &MsgAcknowledgement) {} + } + + #[async_trait] + impl AppHandler for MockAppHandler {} + // test that we can create and update a light client. #[tokio::test] async fn test_create_and_update_light_client() -> anyhow::Result<()> { @@ -433,8 +515,12 @@ mod tests { msg_update_stargaze_client.client_id = ClientId::from_str("07-tendermint-0").unwrap(); - let create_client_action = IbcAction::CreateClient(msg_create_stargaze_client); - let update_client_action = IbcAction::UpdateClient(msg_update_stargaze_client); + let create_client_action = IbcActionWithHandler::::new( + IbcAction::CreateClient(msg_create_stargaze_client), + ); + let update_client_action = IbcActionWithHandler::::new( + IbcAction::UpdateClient(msg_update_stargaze_client), + ); create_client_action.check_stateless(()).await?; create_client_action.check_stateful(state.clone()).await?; @@ -459,7 +545,8 @@ mod tests { let mut second_update = MsgUpdateClient::decode(msg_update_second.as_slice()).unwrap(); second_update.client_id = ClientId::from_str("07-tendermint-0").unwrap(); - let second_update_client_action = IbcAction::UpdateClient(second_update); + let second_update_client_action = + IbcActionWithHandler::::new(IbcAction::UpdateClient(second_update)); second_update_client_action.check_stateless(()).await?; second_update_client_action diff --git a/crates/core/component/ibc/src/component/ibc_action_with_handler.rs b/crates/core/component/ibc/src/component/ibc_action_with_handler.rs new file mode 100644 index 0000000000..d693a01c1c --- /dev/null +++ b/crates/core/component/ibc/src/component/ibc_action_with_handler.rs @@ -0,0 +1,31 @@ +use crate::component::app_handler::AppHandler; +use crate::IbcAction; +use std::marker::PhantomData; + +pub struct IbcActionWithHandler(IbcAction, PhantomData); + +impl IbcActionWithHandler { + pub fn new(action: IbcAction) -> Self { + Self(action, PhantomData) + } + + pub fn action(&self) -> &IbcAction { + &self.0 + } + + pub fn into_inner(self) -> IbcAction { + self.0 + } +} + +impl From> for IbcAction { + fn from(value: IbcActionWithHandler) -> Self { + value.0 + } +} + +impl IbcAction { + pub fn with_handler(self) -> IbcActionWithHandler { + IbcActionWithHandler::new(self) + } +} diff --git a/crates/core/component/ibc/src/component/msg_handler.rs b/crates/core/component/ibc/src/component/msg_handler.rs index 77f854404c..711fd0b96f 100644 --- a/crates/core/component/ibc/src/component/msg_handler.rs +++ b/crates/core/component/ibc/src/component/msg_handler.rs @@ -16,6 +16,7 @@ mod timeout; mod update_client; mod upgrade_client; +use crate::component::app_handler::{AppHandlerCheck, AppHandlerExecute}; use anyhow::Result; use async_trait::async_trait; use penumbra_storage::StateWrite; @@ -24,6 +25,9 @@ use penumbra_storage::StateWrite; /// message types) and tweaked (removing the separate check_stateless step). #[async_trait] pub(crate) trait MsgHandler { - async fn check_stateless(&self) -> Result<()>; - async fn try_execute(&self, state: S) -> Result<()>; + async fn check_stateless(&self) -> Result<()>; + async fn try_execute( + &self, + state: S, + ) -> Result<()>; } diff --git a/crates/core/component/ibc/src/component/msg_handler/acknowledgement.rs b/crates/core/component/ibc/src/component/msg_handler/acknowledgement.rs index bdaba848b0..2d1038c305 100644 --- a/crates/core/component/ibc/src/component/msg_handler/acknowledgement.rs +++ b/crates/core/component/ibc/src/component/msg_handler/acknowledgement.rs @@ -12,19 +12,21 @@ use crate::component::{ channel::{StateReadExt as _, StateWriteExt as _}, connection::StateReadExt as _, proof_verification::{commit_packet, PacketProofVerifier}, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgAcknowledgement { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); let channel = state .get_channel(&self.packet.chan_on_a, &self.packet.port_on_a) @@ -73,7 +75,7 @@ impl MsgHandler for MsgAcknowledgement { let transfer = PortId::transfer(); if self.packet.port_on_b == transfer { - Ics20Transfer::acknowledge_packet_check(&mut state, self).await?; + H::acknowledge_packet_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -113,7 +115,7 @@ impl MsgHandler for MsgAcknowledgement { let transfer = PortId::transfer(); if self.packet.port_on_b == transfer { - Ics20Transfer::acknowledge_packet_execute(state, self).await; + H::acknowledge_packet_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/channel_close_confirm.rs b/crates/core/component/ibc/src/component/msg_handler/channel_close_confirm.rs index 67a8f5d789..3a39abaf60 100644 --- a/crates/core/component/ibc/src/component/msg_handler/channel_close_confirm.rs +++ b/crates/core/component/ibc/src/component/msg_handler/channel_close_confirm.rs @@ -14,19 +14,21 @@ use crate::component::{ channel::{StateReadExt as _, StateWriteExt as _}, connection::StateReadExt as _, proof_verification::ChannelProofVerifier, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgChannelCloseConfirm { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); // TODO: capability authentication? // @@ -83,7 +85,7 @@ impl MsgHandler for MsgChannelCloseConfirm { let transfer = PortId::transfer(); if self.port_id_on_b == transfer { - Ics20Transfer::chan_close_confirm_check(&mut state, self).await?; + H::chan_close_confirm_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -107,7 +109,7 @@ impl MsgHandler for MsgChannelCloseConfirm { let transfer = PortId::transfer(); if self.port_id_on_b == transfer { - Ics20Transfer::chan_close_confirm_execute(state, self).await; + H::chan_close_confirm_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/channel_close_init.rs b/crates/core/component/ibc/src/component/msg_handler/channel_close_init.rs index 09bf7ce85d..d64efc9344 100644 --- a/crates/core/component/ibc/src/component/msg_handler/channel_close_init.rs +++ b/crates/core/component/ibc/src/component/msg_handler/channel_close_init.rs @@ -10,19 +10,21 @@ use crate::component::{ app_handler::{AppHandlerCheck, AppHandlerExecute}, channel::{StateReadExt as _, StateWriteExt as _}, connection::StateReadExt as _, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgChannelCloseInit { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); // TODO: capability authentication? // @@ -46,7 +48,7 @@ impl MsgHandler for MsgChannelCloseInit { } let transfer = PortId::transfer(); if self.port_id_on_a == transfer { - Ics20Transfer::chan_close_init_check(&mut state, self).await?; + H::chan_close_init_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -71,7 +73,7 @@ impl MsgHandler for MsgChannelCloseInit { let transfer = PortId::transfer(); if self.port_id_on_a == transfer { - Ics20Transfer::chan_close_init_execute(state, self).await; + H::chan_close_init_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/channel_open_ack.rs b/crates/core/component/ibc/src/component/msg_handler/channel_open_ack.rs index b9e02deab8..af9e7c8d26 100644 --- a/crates/core/component/ibc/src/component/msg_handler/channel_open_ack.rs +++ b/crates/core/component/ibc/src/component/msg_handler/channel_open_ack.rs @@ -12,19 +12,21 @@ use crate::component::{ channel::{StateReadExt as _, StateWriteExt as _}, connection::StateReadExt as _, proof_verification::ChannelProofVerifier, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgChannelOpenAck { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); let mut channel = state .get_channel(&self.chan_id_on_a, &self.port_id_on_a) @@ -67,7 +69,7 @@ impl MsgHandler for MsgChannelOpenAck { let transfer = PortId::transfer(); if self.port_id_on_a == transfer { - Ics20Transfer::chan_open_ack_check(&mut state, self).await?; + H::chan_open_ack_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -94,7 +96,7 @@ impl MsgHandler for MsgChannelOpenAck { let transfer = PortId::transfer(); if self.port_id_on_a == transfer { - Ics20Transfer::chan_open_ack_execute(state, self).await; + H::chan_open_ack_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/channel_open_confirm.rs b/crates/core/component/ibc/src/component/msg_handler/channel_open_confirm.rs index 699a8693c1..99b0369cbd 100644 --- a/crates/core/component/ibc/src/component/msg_handler/channel_open_confirm.rs +++ b/crates/core/component/ibc/src/component/msg_handler/channel_open_confirm.rs @@ -12,19 +12,21 @@ use crate::component::{ channel::{StateReadExt as _, StateWriteExt as _}, connection::StateReadExt as _, proof_verification::ChannelProofVerifier, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgChannelOpenConfirm { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); let mut channel = state .get_channel(&self.chan_id_on_b, &self.port_id_on_b) @@ -78,7 +80,7 @@ impl MsgHandler for MsgChannelOpenConfirm { let transfer = PortId::transfer(); if self.port_id_on_b == transfer { - Ics20Transfer::chan_open_confirm_check(&mut state, self).await?; + H::chan_open_confirm_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -103,7 +105,7 @@ impl MsgHandler for MsgChannelOpenConfirm { let transfer = PortId::transfer(); if self.port_id_on_b == transfer { - Ics20Transfer::chan_open_confirm_execute(state, self).await; + H::chan_open_confirm_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/channel_open_init.rs b/crates/core/component/ibc/src/component/msg_handler/channel_open_init.rs index 63df755c0c..0f061eeeb0 100644 --- a/crates/core/component/ibc/src/component/msg_handler/channel_open_init.rs +++ b/crates/core/component/ibc/src/component/msg_handler/channel_open_init.rs @@ -10,19 +10,21 @@ use crate::component::{ app_handler::{AppHandlerCheck, AppHandlerExecute}, channel::{StateReadExt as _, StateWriteExt as _}, connection::StateReadExt as _, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgChannelOpenInit { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { connection_hops_eq_1(self)?; Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); let channel_id = get_channel_id(&state).await?; @@ -36,7 +38,7 @@ impl MsgHandler for MsgChannelOpenInit { let transfer = PortId::transfer(); if self.port_id_on_a == transfer { - Ics20Transfer::chan_open_init_check(&mut state, self).await?; + H::chan_open_init_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -70,7 +72,7 @@ impl MsgHandler for MsgChannelOpenInit { let transfer = PortId::transfer(); if self.port_id_on_a == transfer { - Ics20Transfer::chan_open_init_execute(state, self).await; + H::chan_open_init_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/channel_open_try.rs b/crates/core/component/ibc/src/component/msg_handler/channel_open_try.rs index cac3880a0e..f322268a86 100644 --- a/crates/core/component/ibc/src/component/msg_handler/channel_open_try.rs +++ b/crates/core/component/ibc/src/component/msg_handler/channel_open_try.rs @@ -14,19 +14,21 @@ use crate::component::{ channel::StateWriteExt, connection::StateReadExt, proof_verification::ChannelProofVerifier, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgChannelOpenTry { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { connection_hops_eq_1(self)?; Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); let connection_on_b = verify_connections_open(&state, self).await?; @@ -60,7 +62,7 @@ impl MsgHandler for MsgChannelOpenTry { let transfer = PortId::transfer(); if self.port_id_on_b == transfer { - Ics20Transfer::chan_open_try_check(&mut state, self).await?; + H::chan_open_try_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -100,7 +102,7 @@ impl MsgHandler for MsgChannelOpenTry { let transfer = PortId::transfer(); if self.port_id_on_b == transfer { - Ics20Transfer::chan_open_try_execute(state, self).await; + H::chan_open_try_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/connection_open_ack.rs b/crates/core/component/ibc/src/component/msg_handler/connection_open_ack.rs index a4405e41ad..5551d1a5ae 100644 --- a/crates/core/component/ibc/src/component/msg_handler/connection_open_ack.rs +++ b/crates/core/component/ibc/src/component/msg_handler/connection_open_ack.rs @@ -18,11 +18,11 @@ use crate::component::{ #[async_trait] impl MsgHandler for MsgConnectionOpenAck { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); // Validate a ConnectionOpenAck message, which is sent to us by a counterparty chain that // has committed a Connection to us expected to be in the TRYOPEN state. Before executing a diff --git a/crates/core/component/ibc/src/component/msg_handler/connection_open_confirm.rs b/crates/core/component/ibc/src/component/msg_handler/connection_open_confirm.rs index 3051b8806d..adf22bf595 100644 --- a/crates/core/component/ibc/src/component/msg_handler/connection_open_confirm.rs +++ b/crates/core/component/ibc/src/component/msg_handler/connection_open_confirm.rs @@ -17,14 +17,14 @@ use crate::component::{ #[async_trait] impl MsgHandler for MsgConnectionOpenConfirm { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: other than that the message is a well formed ConnectionOpenConfirm, // there is no other stateless validation to perform. Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); // Validate a ConnectionOpenConfirm message, completing the IBC connection handshake. // diff --git a/crates/core/component/ibc/src/component/msg_handler/connection_open_init.rs b/crates/core/component/ibc/src/component/msg_handler/connection_open_init.rs index 3433c54920..6c6d1c8978 100644 --- a/crates/core/component/ibc/src/component/msg_handler/connection_open_init.rs +++ b/crates/core/component/ibc/src/component/msg_handler/connection_open_init.rs @@ -16,13 +16,13 @@ use penumbra_storage::StateWrite; #[async_trait] impl MsgHandler for MsgConnectionOpenInit { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { version_is_supported(self)?; Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); // check that the client with the specified ID exists diff --git a/crates/core/component/ibc/src/component/msg_handler/connection_open_try.rs b/crates/core/component/ibc/src/component/msg_handler/connection_open_try.rs index 49547223fa..04f245e124 100644 --- a/crates/core/component/ibc/src/component/msg_handler/connection_open_try.rs +++ b/crates/core/component/ibc/src/component/msg_handler/connection_open_try.rs @@ -24,12 +24,12 @@ use crate::component::{ #[async_trait] impl MsgHandler for MsgConnectionOpenTry { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // basic checks are performed by the ibc-rs crate when deserializing domain types. Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); // Validate a ConnectionOpenTry message, which is sent to us by a counterparty chain that diff --git a/crates/core/component/ibc/src/component/msg_handler/create_client.rs b/crates/core/component/ibc/src/component/msg_handler/create_client.rs index 2e386c37f1..c3df391385 100644 --- a/crates/core/component/ibc/src/component/msg_handler/create_client.rs +++ b/crates/core/component/ibc/src/component/msg_handler/create_client.rs @@ -14,7 +14,7 @@ use crate::component::{ #[async_trait] impl MsgHandler for MsgCreateClient { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { client_state_is_tendermint(self)?; consensus_state_is_tendermint(self)?; @@ -28,7 +28,7 @@ impl MsgHandler for MsgCreateClient { // - client type // - consensus state // - processed time and height - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); let client_state = ics02_validation::get_tendermint_client_state(self.client_state.clone())?; diff --git a/crates/core/component/ibc/src/component/msg_handler/misbehavior.rs b/crates/core/component/ibc/src/component/msg_handler/misbehavior.rs index f8c51d05dd..26c7c57a3f 100644 --- a/crates/core/component/ibc/src/component/msg_handler/misbehavior.rs +++ b/crates/core/component/ibc/src/component/msg_handler/misbehavior.rs @@ -22,7 +22,7 @@ use super::MsgHandler; #[async_trait] impl MsgHandler for MsgSubmitMisbehaviour { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { misbehavior_is_tendermint(self)?; let untrusted_misbehavior = ics02_validation::get_tendermint_misbehavior(self.misbehaviour.clone())?; @@ -38,7 +38,7 @@ impl MsgHandler for MsgSubmitMisbehaviour { Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); let untrusted_misbehavior = diff --git a/crates/core/component/ibc/src/component/msg_handler/recv_packet.rs b/crates/core/component/ibc/src/component/msg_handler/recv_packet.rs index 08d7d7e1da..1b754bb3b3 100644 --- a/crates/core/component/ibc/src/component/msg_handler/recv_packet.rs +++ b/crates/core/component/ibc/src/component/msg_handler/recv_packet.rs @@ -18,19 +18,21 @@ use crate::component::{ channel::{StateReadExt as _, StateWriteExt}, connection::StateReadExt as _, proof_verification::PacketProofVerifier, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgRecvPacket { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); tracing::debug!(data = ?String::from_utf8_lossy(&self.packet.data)); let channel = state @@ -105,7 +107,7 @@ impl MsgHandler for MsgRecvPacket { let transfer = PortId::transfer(); if self.packet.port_on_b == transfer { - Ics20Transfer::recv_packet_check(&mut state, self).await?; + H::recv_packet_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -146,7 +148,7 @@ impl MsgHandler for MsgRecvPacket { let transfer = PortId::transfer(); if self.packet.port_on_b == transfer { - Ics20Transfer::recv_packet_execute(state, self).await; + H::recv_packet_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/timeout.rs b/crates/core/component/ibc/src/component/msg_handler/timeout.rs index 24d96dcb85..58c394b0b6 100644 --- a/crates/core/component/ibc/src/component/msg_handler/timeout.rs +++ b/crates/core/component/ibc/src/component/msg_handler/timeout.rs @@ -14,19 +14,21 @@ use crate::component::{ client::StateReadExt, connection::StateReadExt as _, proof_verification::{commit_packet, PacketProofVerifier}, - transfer::Ics20Transfer, MsgHandler, }; #[async_trait] impl MsgHandler for MsgTimeout { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { // NOTE: no additional stateless validation is possible Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute( + &self, + mut state: S, + ) -> Result<()> { tracing::debug!(msg = ?self); let mut channel = state .get_channel(&self.packet.chan_on_a, &self.packet.port_on_a) @@ -91,7 +93,7 @@ impl MsgHandler for MsgTimeout { let transfer = PortId::transfer(); if self.packet.port_on_b == transfer { - Ics20Transfer::timeout_packet_check(&mut state, self).await?; + H::timeout_packet_check(&mut state, self).await?; } else { anyhow::bail!("invalid port id"); } @@ -128,7 +130,7 @@ impl MsgHandler for MsgTimeout { let transfer = PortId::transfer(); if self.packet.port_on_b == transfer { - Ics20Transfer::timeout_packet_execute(state, self).await; + H::timeout_packet_execute(state, self).await; } else { anyhow::bail!("invalid port id"); } diff --git a/crates/core/component/ibc/src/component/msg_handler/update_client.rs b/crates/core/component/ibc/src/component/msg_handler/update_client.rs index ad48d18f4a..f93bdfd442 100644 --- a/crates/core/component/ibc/src/component/msg_handler/update_client.rs +++ b/crates/core/component/ibc/src/component/msg_handler/update_client.rs @@ -24,13 +24,13 @@ use crate::component::{ #[async_trait] impl MsgHandler for MsgUpdateClient { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { header_is_tendermint(self)?; Ok(()) } - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { // Optimization: no-op if the update is already committed. We no-op // to Ok(()) rather than erroring to avoid having two "racing" relay // transactions fail just because they both contain the same client @@ -61,8 +61,6 @@ impl MsgHandler for MsgUpdateClient { .get_verified_consensus_state(trusted_height, self.client_id.clone()) .await?; - let last_trusted_consensus_state = last_trusted_consensus_state; - // We also have to convert from an IBC height, which has two // components, to a Tendermint height, which has only one. let trusted_height = trusted_height diff --git a/crates/core/component/ibc/src/component/msg_handler/upgrade_client.rs b/crates/core/component/ibc/src/component/msg_handler/upgrade_client.rs index ed93595a33..e96c87e9ff 100644 --- a/crates/core/component/ibc/src/component/msg_handler/upgrade_client.rs +++ b/crates/core/component/ibc/src/component/msg_handler/upgrade_client.rs @@ -22,7 +22,7 @@ static SENTINEL_UPGRADE_ROOT: &str = "sentinel_root"; #[async_trait] impl MsgHandler for MsgUpgradeClient { - async fn check_stateless(&self) -> Result<()> { + async fn check_stateless(&self) -> Result<()> { Ok(()) } @@ -37,7 +37,7 @@ impl MsgHandler for MsgUpgradeClient { // // the first consensus state of the upgraded client uses a sentinel root, against which no // proofs will verify. subsequent client updates, post-upgrade, will provide usable roots. - async fn try_execute(&self, mut state: S) -> Result<()> { + async fn try_execute(&self, mut state: S) -> Result<()> { tracing::debug!(msg = ?self); let upgraded_client_state_tm = TendermintClientState::try_from(self.client_state.clone()) diff --git a/crates/core/component/ibc/src/component/packet.rs b/crates/core/component/ibc/src/component/packet.rs index 84db18189a..79d8b5bced 100644 --- a/crates/core/component/ibc/src/component/packet.rs +++ b/crates/core/component/ibc/src/component/packet.rs @@ -6,13 +6,10 @@ use ibc_types::core::{ }; use penumbra_storage::{StateRead, StateWrite}; -use crate::{ - component::{ - channel::{StateReadExt as _, StateWriteExt as _}, - client::StateReadExt as _, - connection::StateReadExt as _, - }, - Ics20Withdrawal, +use crate::component::{ + channel::{StateReadExt as _, StateWriteExt as _}, + client::StateReadExt as _, + connection::StateReadExt as _, }; pub trait CheckStatus: private::Sealed {} @@ -44,6 +41,23 @@ pub struct IBCPacket { } impl IBCPacket { + pub fn new( + source_port: PortId, + source_channel: ChannelId, + timeout_height: Height, + timeout_timestamp: u64, + data: Vec, + ) -> Self { + Self { + source_port, + source_channel, + timeout_height, + timeout_timestamp, + data, + m: std::marker::PhantomData, + } + } + pub fn assume_checked(self) -> IBCPacket { IBCPacket { source_port: self.source_port, @@ -56,20 +70,6 @@ impl IBCPacket { } } -impl From for IBCPacket { - fn from(withdrawal: Ics20Withdrawal) -> Self { - Self { - source_port: PortId::transfer(), - source_channel: withdrawal.source_channel.clone(), - timeout_height: withdrawal.timeout_height, - timeout_timestamp: withdrawal.timeout_time, - data: withdrawal.packet_data(), - - m: std::marker::PhantomData, - } - } -} - #[async_trait] pub trait SendPacketRead: StateRead { /// send_packet_check verifies that a packet can be sent using the provided parameters. diff --git a/crates/core/component/ibc/src/component/rpc/client_query.rs b/crates/core/component/ibc/src/component/rpc/client_query.rs index f64b45314b..e0cd1089c2 100644 --- a/crates/core/component/ibc/src/component/rpc/client_query.rs +++ b/crates/core/component/ibc/src/component/rpc/client_query.rs @@ -14,7 +14,9 @@ use ibc_proto::ibc::core::client::v1::{ use ibc_types::core::client::ClientId; use ibc_types::lightclients::tendermint::client_state::ClientState as TendermintClientState; +use ibc_types::path::ClientStatePath; use ibc_types::TypeUrl; +use penumbra_chain::component::StateReadExt; use std::str::FromStr; use tonic::{Response, Status}; @@ -34,13 +36,21 @@ impl ClientQuery for IbcQuery { let client_id = ClientId::from_str(&request.get_ref().client_id) .map_err(|e| tonic::Status::invalid_argument(format!("invalid client id: {e}")))?; let height = Height { - revision_number: 0, + revision_number: snapshot + .get_revision_number() + .await + .map_err(|e| tonic::Status::aborted(e.to_string()))?, revision_height: snapshot.version(), }; // Query for client_state and associated proof. let (cs_opt, proof) = snapshot - .get_with_proof(client_id.to_string().as_bytes().to_vec()) + .get_with_proof( + ClientStatePath(client_id.clone()) + .to_string() + .as_bytes() + .to_vec(), + ) .await .map_err(|e| tonic::Status::aborted(format!("couldn't get client: {e}")))?; diff --git a/crates/core/component/ibc/src/lib.rs b/crates/core/component/ibc/src/lib.rs index 91e980e725..f4a8483c35 100644 --- a/crates/core/component/ibc/src/lib.rs +++ b/crates/core/component/ibc/src/lib.rs @@ -8,17 +8,17 @@ #[cfg_attr(docsrs, doc(cfg(feature = "component")))] #[cfg(feature = "component")] pub mod component; +#[cfg(feature = "component")] +pub use component::ibc_action_with_handler::IbcActionWithHandler; pub mod genesis; mod ibc_action; mod ibc_token; -mod ics20_withdrawal; pub mod params; mod version; pub use ibc_action::IbcAction; pub use ibc_token::IbcToken; -pub use ics20_withdrawal::Ics20Withdrawal; #[cfg_attr(docsrs, doc(cfg(feature = "component")))] #[cfg(feature = "component")] diff --git a/crates/core/component/sct/Cargo.toml b/crates/core/component/sct/Cargo.toml index 0226ef11f5..0bcd31f04c 100644 --- a/crates/core/component/sct/Cargo.toml +++ b/crates/core/component/sct/Cargo.toml @@ -1,7 +1,7 @@ [package] # TODO: merge with tct crate under a `component` feature flag? name = "penumbra-sct" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/shielded-pool/Cargo.toml b/crates/core/component/shielded-pool/Cargo.toml index 51452fdf56..c2f33bea58 100644 --- a/crates/core/component/shielded-pool/Cargo.toml +++ b/crates/core/component/shielded-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-shielded-pool" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,8 +10,10 @@ component = [ "penumbra-component", "penumbra-storage", "penumbra-proto/penumbra-storage", + "penumbra-ibc/component", "penumbra-chain/component", "penumbra-sct/component", + "tonic" ] proving-keys = ["penumbra-proof-params/proving-keys"] default = ["std", "component", "proving-keys"] @@ -36,6 +38,7 @@ penumbra-tct = { path = "../../../crypto/tct" } penumbra-proof-params = { path = "../../../crypto/proof-params", default-features = false } penumbra-sct = { path = "../sct", default-features = false } penumbra-component = { path = "../component", optional = true } +penumbra-ibc = { path = "../ibc", default-features = false } penumbra-chain = { path = "../chain", default-features = false } penumbra-asset = { path = "../../../core/asset", default-features = false } penumbra-num = { path = "../../../core/num", default-features = false } @@ -44,6 +47,7 @@ decaf377-ka = { path = "../../../crypto/decaf377-ka/" } decaf377-fmd = { path = "../../../crypto/decaf377-fmd/" } # Penumbra dependencies +ibc-types = { version = "0.7.0", default-features = false } decaf377-rdsa = { version = "0.7" } decaf377 = { version = "0.5", features = ["r1cs"] } poseidon377 = { version = "0.6", features = ["r1cs"] } @@ -59,7 +63,9 @@ ark-serialize = "0.4" ark-groth16 = { version = "0.4", default-features = false } ark-snark = "0.4" metrics = "0.19.0" +prost = "0.12" serde = { version = "1", features = ["derive"] } +serde_json = "1" tracing = "0.1" anyhow = "1" async-trait = "0.1.52" @@ -75,6 +81,5 @@ hex = "0.4" # Component dependencies tonic = { version = "0.10", optional = true } - [dev-dependencies] proptest = "1" diff --git a/crates/core/component/shielded-pool/src/component.rs b/crates/core/component/shielded-pool/src/component.rs index f907ac3826..228f7c4970 100644 --- a/crates/core/component/shielded-pool/src/component.rs +++ b/crates/core/component/shielded-pool/src/component.rs @@ -5,10 +5,12 @@ mod metrics; mod note_manager; mod shielded_pool; mod supply; +mod transfer; pub use self::metrics::register_metrics; pub use note_manager::NoteManager; pub use shielded_pool::{ShieldedPool, StateReadExt}; pub use supply::{SupplyRead, SupplyWrite}; +pub use transfer::Ics20Transfer; pub mod rpc; diff --git a/crates/core/component/shielded-pool/src/component/action_handler.rs b/crates/core/component/shielded-pool/src/component/action_handler.rs index c5dbcf860a..7eedb7cf3c 100644 --- a/crates/core/component/shielded-pool/src/component/action_handler.rs +++ b/crates/core/component/shielded-pool/src/component/action_handler.rs @@ -1,2 +1,3 @@ +mod ics20_withdrawal; mod output; mod spend; diff --git a/crates/core/component/ibc/src/component/action_handler/ics20_withdrawal.rs b/crates/core/component/shielded-pool/src/component/action_handler/ics20_withdrawal.rs similarity index 100% rename from crates/core/component/ibc/src/component/action_handler/ics20_withdrawal.rs rename to crates/core/component/shielded-pool/src/component/action_handler/ics20_withdrawal.rs diff --git a/crates/core/component/ibc/src/component/transfer.rs b/crates/core/component/shielded-pool/src/component/transfer.rs similarity index 98% rename from crates/core/component/ibc/src/component/transfer.rs rename to crates/core/component/shielded-pool/src/component/transfer.rs index 052cebb589..0c5bd94650 100644 --- a/crates/core/component/ibc/src/component/transfer.rs +++ b/crates/core/component/shielded-pool/src/component/transfer.rs @@ -1,5 +1,9 @@ use std::str::FromStr; +use crate::{ + component::{NoteManager, SupplyWrite}, + Ics20Withdrawal, +}; use anyhow::{Context, Result}; use async_trait::async_trait; use ibc_types::{ @@ -21,20 +25,15 @@ use penumbra_proto::{ penumbra::core::component::ibc::v1alpha1::FungibleTokenPacketData, StateReadProto, StateWriteProto, }; -use penumbra_shielded_pool::component::{NoteManager, SupplyWrite}; use penumbra_storage::{StateRead, StateWrite}; use prost::Message; -use crate::{ - component::{ - app_handler::{AppHandler, AppHandlerCheck, AppHandlerExecute}, - packet::{ - IBCPacket, SendPacketRead as _, SendPacketWrite as _, Unchecked, - WriteAcknowledgement as _, - }, - state_key, +use penumbra_ibc::component::{ + app_handler::{AppHandler, AppHandlerCheck, AppHandlerExecute}, + packet::{ + IBCPacket, SendPacketRead as _, SendPacketWrite as _, Unchecked, WriteAcknowledgement as _, }, - Ics20Withdrawal, + state_key, }; // returns a bool indicating if the provided denom was issued locally or if it was bridged in. diff --git a/crates/core/component/ibc/src/ics20_withdrawal.rs b/crates/core/component/shielded-pool/src/ics20_withdrawal.rs similarity index 89% rename from crates/core/component/ibc/src/ics20_withdrawal.rs rename to crates/core/component/shielded-pool/src/ics20_withdrawal.rs index 9a0b0dd423..9574c46b53 100644 --- a/crates/core/component/ibc/src/ics20_withdrawal.rs +++ b/crates/core/component/shielded-pool/src/ics20_withdrawal.rs @@ -1,4 +1,4 @@ -use ibc_types::core::{channel::ChannelId, client::Height as IbcHeight}; +use ibc_types::core::{channel::ChannelId, channel::PortId, client::Height as IbcHeight}; use penumbra_asset::{ asset::{self, DenomMetadata}, Balance, Value, @@ -12,6 +12,9 @@ use penumbra_proto::{ use serde::{Deserialize, Serialize}; use std::str::FromStr; +#[cfg(feature = "component")] +use penumbra_ibc::component::packet::{IBCPacket, Unchecked}; + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(try_from = "pb::Ics20Withdrawal", into = "pb::Ics20Withdrawal")] pub struct Ics20Withdrawal { @@ -35,6 +38,19 @@ pub struct Ics20Withdrawal { pub source_channel: ChannelId, } +#[cfg(feature = "component")] +impl From for IBCPacket { + fn from(withdrawal: Ics20Withdrawal) -> Self { + Self::new( + PortId::transfer(), + withdrawal.source_channel.clone(), + withdrawal.timeout_height, + withdrawal.timeout_time, + withdrawal.packet_data(), + ) + } +} + impl Ics20Withdrawal { pub fn value(&self) -> Value { Value { diff --git a/crates/core/component/shielded-pool/src/lib.rs b/crates/core/component/shielded-pool/src/lib.rs index 61d8ff5647..49ac0a67e5 100644 --- a/crates/core/component/shielded-pool/src/lib.rs +++ b/crates/core/component/shielded-pool/src/lib.rs @@ -5,6 +5,9 @@ #[cfg(feature = "component")] pub mod component; +pub mod ics20_withdrawal; +pub use ics20_withdrawal::Ics20Withdrawal; + pub mod event; pub mod genesis; pub mod state_key; diff --git a/crates/core/component/shielded-pool/src/output/plan.rs b/crates/core/component/shielded-pool/src/output/plan.rs index 26b9dad749..10d8393ab1 100644 --- a/crates/core/component/shielded-pool/src/output/plan.rs +++ b/crates/core/component/shielded-pool/src/output/plan.rs @@ -194,7 +194,7 @@ mod test { let dummy_memo_key: PayloadKey = [0; 32].into(); let value: Value = "1234.02penumbra".parse().unwrap(); - let dest_address = "penumbrav2t1f5h060qspaga3vvwf2mwak2dj6ugymxd2et5h6l3n0u2y57lcv4t7j2m8n75nm7qmhg4v3csexl5slm6tm5hg5wyw39fv2q0jnpwdjn3llduzgmg5d3efuqq6ymn76t0hvgage".parse().unwrap(); + let dest_address = "penumbra1rqcd3hfvkvc04c4c9vc0ac87lh4y0z8l28k4xp6d0cnd5jc6f6k0neuzp6zdwtpwyfpswtdzv9jzqtpjn5t6wh96pfx3flq2dhqgc42u7c06kj57dl39w2xm6tg0wh4zc8kjjk".parse().unwrap(); let output_plan = OutputPlan::new(&mut rng, value, dest_address); let blinding_factor = output_plan.value_blinding; diff --git a/crates/core/component/stake/Cargo.toml b/crates/core/component/stake/Cargo.toml index ef8f5557f7..1035d6fdd8 100644 --- a/crates/core/component/stake/Cargo.toml +++ b/crates/core/component/stake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-stake" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/component/stake/src/component.rs b/crates/core/component/stake/src/component.rs index 19c73e8a84..fbfbbb0a79 100644 --- a/crates/core/component/stake/src/component.rs +++ b/crates/core/component/stake/src/component.rs @@ -314,13 +314,13 @@ pub(crate) trait StakingImpl: StateWriteExt { for d in changes.delegations { delegations_by_validator .entry(d.validator_identity.clone()) - .or_insert_with(Vec::new) + .or_default() .push(d); } for u in changes.undelegations { undelegations_by_validator .entry(u.validator_identity.clone()) - .or_insert_with(Vec::new) + .or_default() .push(u); } } diff --git a/crates/core/keys/Cargo.toml b/crates/core/keys/Cargo.toml index 3c36d48ae1..d4ce5e70c2 100644 --- a/crates/core/keys/Cargo.toml +++ b/crates/core/keys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-keys" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/keys/src/keys/diversifier.rs b/crates/core/keys/src/keys/diversifier.rs index fe907f833f..1f458978d7 100644 --- a/crates/core/keys/src/keys/diversifier.rs +++ b/crates/core/keys/src/keys/diversifier.rs @@ -79,7 +79,7 @@ impl TryFrom for Diversifier { } #[derive(Clone, Derivative)] -#[derivative(Debug)] +#[derivative(Debug, PartialEq, Eq)] pub struct DiversifierKey( #[derivative(Debug(bound = "", format_with = "crate::fmt_hex"))] pub(super) [u8; 16], ); diff --git a/crates/core/keys/src/keys/fvk.rs b/crates/core/keys/src/keys/fvk.rs index 6f2918e0d2..e2f8d4eb4d 100644 --- a/crates/core/keys/src/keys/fvk.rs +++ b/crates/core/keys/src/keys/fvk.rs @@ -30,7 +30,7 @@ static ACCOUNT_ID_DOMAIN_SEP: Lazy = Lazy::new(|| Fq::from_le_bytes_mod_order(b"Penumbra_HashFVK")); /// The root viewing capability for all data related to a given spend authority. -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(try_from = "pb::FullViewingKey", into = "pb::FullViewingKey")] pub struct FullViewingKey { ak: VerificationKey, diff --git a/crates/core/keys/src/keys/ivk.rs b/crates/core/keys/src/keys/ivk.rs index 35163b03fd..190ea4c0b7 100644 --- a/crates/core/keys/src/keys/ivk.rs +++ b/crates/core/keys/src/keys/ivk.rs @@ -20,7 +20,7 @@ const MOD_R_QUOTIENT: usize = 4; /// Allows viewing incoming notes, i.e., notes sent to the spending key this /// key is derived from. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct IncomingViewingKey { pub(super) ivk: ka::Secret, pub(super) dk: DiversifierKey, diff --git a/crates/core/keys/src/keys/ovk.rs b/crates/core/keys/src/keys/ovk.rs index abdd7dab5d..b449b5a195 100644 --- a/crates/core/keys/src/keys/ovk.rs +++ b/crates/core/keys/src/keys/ovk.rs @@ -2,7 +2,7 @@ pub const OVK_LEN_BYTES: usize = 32; /// Allows viewing outgoing notes, i.e., notes sent from the spending key this /// key is derived from. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct OutgoingViewingKey(pub(crate) [u8; OVK_LEN_BYTES]); impl OutgoingViewingKey { diff --git a/crates/core/num/Cargo.toml b/crates/core/num/Cargo.toml index 04085de701..0703b737b8 100644 --- a/crates/core/num/Cargo.toml +++ b/crates/core/num/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-num" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/core/transaction/Cargo.toml b/crates/core/transaction/Cargo.toml index 5e89377799..0d1557e813 100644 --- a/crates/core/transaction/Cargo.toml +++ b/crates/core/transaction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-transaction" -version = "0.62.0" +version = "0.63.1" edition = "2021" [dependencies] diff --git a/crates/core/transaction/src/action.rs b/crates/core/transaction/src/action.rs index e7035aa24e..fa75a3200a 100644 --- a/crates/core/transaction/src/action.rs +++ b/crates/core/transaction/src/action.rs @@ -33,7 +33,7 @@ pub enum Action { Undelegate(penumbra_stake::Undelegate), UndelegateClaim(penumbra_stake::UndelegateClaim), - Ics20Withdrawal(penumbra_ibc::Ics20Withdrawal), + Ics20Withdrawal(penumbra_shielded_pool::Ics20Withdrawal), DaoSpend(penumbra_dao::DaoSpend), DaoOutput(penumbra_dao::DaoOutput), diff --git a/crates/core/transaction/src/effect_hash.rs b/crates/core/transaction/src/effect_hash.rs index 0c69295592..82639c6528 100644 --- a/crates/core/transaction/src/effect_hash.rs +++ b/crates/core/transaction/src/effect_hash.rs @@ -11,7 +11,6 @@ use penumbra_governance::{ DelegatorVote, DelegatorVoteBody, Proposal, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote, ValidatorVoteBody, Vote, }; -use penumbra_ibc::Ics20Withdrawal; use penumbra_keys::{FullViewingKey, PayloadKey}; use penumbra_proto::{ core::component::dex::v1alpha1 as pbd, core::component::fee::v1alpha1 as pbf, @@ -20,7 +19,7 @@ use penumbra_proto::{ core::transaction::v1alpha1 as pbt, crypto::decaf377_fmd::v1alpha1 as pb_fmd, Message, }; use penumbra_proto::{DomainType, TypeUrl}; -use penumbra_shielded_pool::{output, spend}; +use penumbra_shielded_pool::{output, spend, Ics20Withdrawal}; use penumbra_stake::{Delegate, Undelegate, UndelegateClaimBody}; use crate::{ diff --git a/crates/core/transaction/src/gas.rs b/crates/core/transaction/src/gas.rs index ef99bad521..5f4b289ebf 100644 --- a/crates/core/transaction/src/gas.rs +++ b/crates/core/transaction/src/gas.rs @@ -6,9 +6,9 @@ use penumbra_dex::{ SwapClaim, }; use penumbra_fee::Gas; -use penumbra_ibc::{IbcAction, Ics20Withdrawal}; +use penumbra_ibc::IbcAction; use penumbra_sct::Nullifier; -use penumbra_shielded_pool::{Output, Spend}; +use penumbra_shielded_pool::{Ics20Withdrawal, Output, Spend}; use penumbra_stake::{ validator::Definition as ValidatorDefinition, Delegate, Undelegate, UndelegateClaim, }; diff --git a/crates/core/transaction/src/is_action.rs b/crates/core/transaction/src/is_action.rs index 9bd137a51b..31a43ff9f9 100644 --- a/crates/core/transaction/src/is_action.rs +++ b/crates/core/transaction/src/is_action.rs @@ -14,8 +14,8 @@ use penumbra_governance::{ DelegatorVote, DelegatorVoteView, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote, VotingReceiptToken, }; -use penumbra_ibc::{IbcAction, Ics20Withdrawal}; -use penumbra_shielded_pool::{Note, Output, OutputView, Spend, SpendView}; +use penumbra_ibc::IbcAction; +use penumbra_shielded_pool::{Ics20Withdrawal, Note, Output, OutputView, Spend, SpendView}; use penumbra_stake::{Delegate, Undelegate, UndelegateClaim}; use crate::{Action, ActionView, TransactionPerspective}; diff --git a/crates/core/transaction/src/memo.rs b/crates/core/transaction/src/memo.rs index 6ba0cd1b8d..e510abde7f 100644 --- a/crates/core/transaction/src/memo.rs +++ b/crates/core/transaction/src/memo.rs @@ -4,7 +4,6 @@ use std::{ }; use anyhow::anyhow; -use rand_core::OsRng; use decaf377_ka as ka; use penumbra_asset::balance; @@ -59,20 +58,17 @@ impl TryFrom> for MemoPlaintext { } } -impl Default for MemoPlaintext { - fn default() -> Self { - let mut rng = OsRng; - MemoPlaintext { - sender: Address::dummy(&mut rng), - text: String::new(), - } - } -} - impl MemoPlaintext { pub fn to_vec(&self) -> Vec { self.into() } + + pub fn blank_memo(address: Address) -> MemoPlaintext { + MemoPlaintext { + sender: address, + text: String::new(), + } + } } impl MemoCiphertext { diff --git a/crates/core/transaction/src/plan.rs b/crates/core/transaction/src/plan.rs index 78a8b5e9eb..66f6162360 100644 --- a/crates/core/transaction/src/plan.rs +++ b/crates/core/transaction/src/plan.rs @@ -13,10 +13,10 @@ use penumbra_fee::Fee; use penumbra_governance::{ DelegatorVotePlan, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote, }; -use penumbra_ibc::{IbcAction, Ics20Withdrawal}; +use penumbra_ibc::IbcAction; use penumbra_keys::Address; use penumbra_proto::{core::transaction::v1alpha1 as pb, DomainType, TypeUrl}; -use penumbra_shielded_pool::{OutputPlan, SpendPlan}; +use penumbra_shielded_pool::{Ics20Withdrawal, OutputPlan, SpendPlan}; use penumbra_stake::{Delegate, Undelegate, UndelegateClaimPlan}; use rand::{CryptoRng, Rng}; use serde::{Deserialize, Serialize}; diff --git a/crates/core/transaction/src/plan/action.rs b/crates/core/transaction/src/plan/action.rs index 4a0426f341..66384693f8 100644 --- a/crates/core/transaction/src/plan/action.rs +++ b/crates/core/transaction/src/plan/action.rs @@ -18,7 +18,7 @@ use penumbra_governance::{ use penumbra_ibc::{IbcAction, Ics20Withdrawal}; use penumbra_keys::{symmetric::PayloadKey, FullViewingKey}; use penumbra_proto::{core::transaction::v1alpha1 as pb_t, DomainType, TypeUrl}; -use penumbra_shielded_pool::{OutputPlan, SpendPlan}; +use penumbra_shielded_pool::{Ics20Withdrawal, OutputPlan, SpendPlan}; use penumbra_stake::{Delegate, Undelegate, UndelegateClaimPlan}; use serde::{Deserialize, Serialize}; diff --git a/crates/core/transaction/src/view/action_view.rs b/crates/core/transaction/src/view/action_view.rs index 80630377d7..df536405bc 100644 --- a/crates/core/transaction/src/view/action_view.rs +++ b/crates/core/transaction/src/view/action_view.rs @@ -5,8 +5,9 @@ use penumbra_dex::{ swap_claim::SwapClaimView, }; use penumbra_governance::{ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote}; -use penumbra_ibc::{IbcAction, Ics20Withdrawal}; +use penumbra_ibc::IbcAction; use penumbra_proto::{core::transaction::v1alpha1 as pbt, DomainType, TypeUrl}; +use penumbra_shielded_pool::Ics20Withdrawal; use penumbra_stake::{Delegate, Undelegate, UndelegateClaim}; use serde::{Deserialize, Serialize}; diff --git a/crates/crypto/decaf377-fmd/Cargo.toml b/crates/crypto/decaf377-fmd/Cargo.toml index db3c57de1e..28e2b4b137 100644 --- a/crates/crypto/decaf377-fmd/Cargo.toml +++ b/crates/crypto/decaf377-fmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "decaf377-fmd" -version = "0.62.0" +version = "0.63.1" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/crypto/decaf377-ka/Cargo.toml b/crates/crypto/decaf377-ka/Cargo.toml index 2802d44f32..2fe1b7c9a9 100644 --- a/crates/crypto/decaf377-ka/Cargo.toml +++ b/crates/crypto/decaf377-ka/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "decaf377-ka" -version = "0.62.0" +version = "0.63.1" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/crypto/decaf377-ka/src/lib.rs b/crates/crypto/decaf377-ka/src/lib.rs index 2620a56545..dc8ca30a32 100644 --- a/crates/crypto/decaf377-ka/src/lib.rs +++ b/crates/crypto/decaf377-ka/src/lib.rs @@ -15,7 +15,7 @@ use zeroize::Zeroize; pub struct Public(pub [u8; 32]); /// A secret key used to perform key agreement using the counterparty's public key. -#[derive(Clone, Zeroize)] +#[derive(Clone, Zeroize, PartialEq, Eq)] #[zeroize(drop)] pub struct Secret(decaf377::Fr); diff --git a/crates/crypto/eddy/Cargo.toml b/crates/crypto/eddy/Cargo.toml index c12761ff6a..21d89aa322 100644 --- a/crates/crypto/eddy/Cargo.toml +++ b/crates/crypto/eddy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-eddy" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/crypto/proof-params/Cargo.toml b/crates/crypto/proof-params/Cargo.toml index 0664c02fba..b725c480f4 100644 --- a/crates/crypto/proof-params/Cargo.toml +++ b/crates/crypto/proof-params/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-proof-params" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/crypto/proof-params/build.rs b/crates/crypto/proof-params/build.rs index 89546e9d1e..ab77ae66dc 100644 --- a/crates/crypto/proof-params/build.rs +++ b/crates/crypto/proof-params/build.rs @@ -77,7 +77,7 @@ pub fn check_proving_key(file: &str) -> anyhow::Result<()> { #[cfg(not(feature = "download-proving-keys"))] { anyhow::bail!( - "proving key is too small; please enable the download-proving-keys feature" + "proving key is too small; please enable the download-proving-keys feature on the `penumbra-proof-params` crate, adding a direct dependency to enable the feature if necessary." ); } } diff --git a/crates/crypto/proof-setup/Cargo.toml b/crates/crypto/proof-setup/Cargo.toml index e9c6659d83..3a9bb69e8c 100644 --- a/crates/crypto/proof-setup/Cargo.toml +++ b/crates/crypto/proof-setup/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-proof-setup" -version = "0.62.0" +version = "0.63.1" edition = "2021" [dependencies] diff --git a/crates/crypto/proof-setup/benches/all.rs b/crates/crypto/proof-setup/benches/all.rs index 7326b74dbe..0ef7a756ca 100644 --- a/crates/crypto/proof-setup/benches/all.rs +++ b/crates/crypto/proof-setup/benches/all.rs @@ -66,7 +66,7 @@ fn benchmarks(c: &mut Criterion) { criterion_group! { name = benches; - config = Criterion::default().sample_size(2); + config = Criterion::default().sample_size(10); targets = benchmarks } criterion_main!(benches); diff --git a/crates/crypto/proof-setup/src/all.rs b/crates/crypto/proof-setup/src/all.rs index f802c0c5bf..7d86239d28 100644 --- a/crates/crypto/proof-setup/src/all.rs +++ b/crates/crypto/proof-setup/src/all.rs @@ -5,16 +5,19 @@ use std::array; use crate::parallel_utils::{flatten_results, transform, transform_parallel}; +use crate::single::group::GroupHasher; use crate::single::{ - self, circuit_degree, group::F, log::ContributionHash, DLogProof, ExtraTransitionInformation, - LinkingProof, Phase1CRSElements, Phase1Contribution, Phase1RawCRSElements, - Phase1RawContribution, Phase2CRSElements, Phase2Contribution, Phase2RawCRSElements, - Phase2RawContribution, + self, circuit_degree, + group::F, + log::{ContributionHash, Hashable}, + DLogProof, ExtraTransitionInformation, LinkingProof, Phase1CRSElements, Phase1Contribution, + Phase1RawCRSElements, Phase1RawContribution, Phase2CRSElements, Phase2Contribution, + Phase2RawCRSElements, Phase2RawContribution, }; use anyhow::{anyhow, Result}; use ark_groth16::ProvingKey; use ark_relations::r1cs::ConstraintMatrices; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate}; use decaf377::Bls12_377; use penumbra_dex::{swap::proof::SwapCircuit, swap_claim::proof::SwapClaimCircuit}; use penumbra_governance::DelegatorVoteCircuit; @@ -27,18 +30,28 @@ use rand_core::OsRng; // Some helper functions since we have to use these seventeen billion times +const SERIALIZATION_COMPRESSION: Compress = Compress::No; + fn to_bytes(t: &T) -> Result> { let mut out = Vec::new(); - t.serialize_uncompressed(&mut out)?; + t.serialize_with_mode(&mut out, SERIALIZATION_COMPRESSION)?; Ok(out) } fn from_bytes(data: &[u8]) -> Result { - Ok(T::deserialize_uncompressed(data)?) + Ok(T::deserialize_with_mode( + data, + SERIALIZATION_COMPRESSION, + Validate::Yes, + )?) } fn from_bytes_unchecked(data: &[u8]) -> Result { - Ok(T::deserialize_uncompressed_unchecked(data)?) + Ok(T::deserialize_with_mode( + data, + SERIALIZATION_COMPRESSION, + Validate::No, + )?) } pub const NUM_CIRCUITS: usize = 7; @@ -236,7 +249,10 @@ impl TryFrom for Phase2RawCeremonyContrib let out = transform_parallel(data, |(parent_hash, updated, update_proof)| { Ok::<_, anyhow::Error>(Phase2RawContribution { parent: ContributionHash::try_from(parent_hash.as_slice())?, - new_elements: from_bytes::(updated.as_slice())?, + new_elements: Phase2RawCRSElements::checked_deserialize_parallel( + SERIALIZATION_COMPRESSION, + updated.as_slice(), + )?, linking_proof: from_bytes::(update_proof.as_slice())?, }) }); @@ -357,11 +373,22 @@ impl Phase2CeremonyContribution { &old.0[0], &old.0[1], &old.0[2], &old.0[3], &old.0[4], &old.0[5], &old.0[6], ]; Self(transform_parallel(data, |old_i| { - Phase2Contribution::make(&mut OsRng, ContributionHash::dummy(), &old_i) + Phase2Contribution::make(&mut OsRng, ContributionHash::dummy(), old_i) })) } } +impl Hashable for Phase2CeremonyContribution { + fn hash(&self) -> ContributionHash { + let hashes = transform(self.0.clone(), |x| x.hash()); + let mut hasher = GroupHasher::new(b"phase2contr"); + for h in hashes { + hasher.eat_bytes(h.as_ref()); + } + ContributionHash(hasher.finalize_bytes()) + } +} + // TODO: Make the phase 1 and phase 2 functionality generic /// Holds all of the CRS elements for phase1 in one struct, before validation. @@ -545,7 +572,10 @@ impl TryFrom for Phase1RawCeremonyContrib let out = transform_parallel(data, |(parent_hash, updated, update_proof)| { Ok::<_, anyhow::Error>(Phase1RawContribution { parent: ContributionHash::try_from(parent_hash.as_slice())?, - new_elements: from_bytes::(updated.as_slice())?, + new_elements: Phase1RawCRSElements::checked_deserialize_parallel( + SERIALIZATION_COMPRESSION, + updated.as_slice(), + )?, linking_proof: from_bytes::(update_proof.as_slice())?, }) }); @@ -668,11 +698,22 @@ impl Phase1CeremonyContribution { &old.0[0], &old.0[1], &old.0[2], &old.0[3], &old.0[4], &old.0[5], &old.0[6], ]; Self(transform_parallel(data, |old_i| { - Phase1Contribution::make(&mut OsRng, ContributionHash::dummy(), &old_i) + Phase1Contribution::make(&mut OsRng, ContributionHash::dummy(), old_i) })) } } +impl Hashable for Phase1CeremonyContribution { + fn hash(&self) -> ContributionHash { + let hashes = transform(self.0.clone(), |x| x.hash()); + let mut hasher = GroupHasher::new(b"phase1contr"); + for h in hashes { + hasher.eat_bytes(h.as_ref()); + } + ContributionHash(hasher.finalize_bytes()) + } +} + #[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct AllExtraTransitionInformation([ExtraTransitionInformation; NUM_CIRCUITS]); @@ -682,7 +723,7 @@ impl AllExtraTransitionInformation { } pub fn from_bytes(data: &[u8]) -> Result { - Ok(from_bytes_unchecked::(data)?) + from_bytes_unchecked::(data) } } diff --git a/crates/crypto/proof-setup/src/parallel_utils.rs b/crates/crypto/proof-setup/src/parallel_utils.rs index 96dd9d8361..f7b2d1b579 100644 --- a/crates/crypto/proof-setup/src/parallel_utils.rs +++ b/crates/crypto/proof-setup/src/parallel_utils.rs @@ -34,3 +34,26 @@ pub fn flatten_results(data: [Result; N]) -> Result< _ => panic!("The size of the iterator should not have changed"), } } + +#[cfg(not(feature = "parallel"))] +pub fn zip_map_parallel( + a: &mut [A], + b: &mut [B], + f: impl Fn(&A, &B) -> C + Send + Sync, +) -> Vec { + a.iter().zip(b.iter()).map(|(a, b)| f(a, b)).collect() +} + +#[cfg(feature = "parallel")] +pub fn zip_map_parallel( + a: &mut [A], + b: &mut [B], + f: impl Fn(&A, &B) -> C + Send + Sync, +) -> Vec { + use rayon::prelude::*; + + a.into_par_iter() + .zip(b.into_par_iter()) + .map(|(a, b)| f(a, b)) + .collect() +} diff --git a/crates/crypto/proof-setup/src/single/phase1.rs b/crates/crypto/proof-setup/src/single/phase1.rs index 129ad2ff34..60e1c6dea4 100644 --- a/crates/crypto/proof-setup/src/single/phase1.rs +++ b/crates/crypto/proof-setup/src/single/phase1.rs @@ -1,12 +1,13 @@ +use anyhow::anyhow; use ark_ec::Group; use ark_ff::{One, UniformRand, Zero}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Valid, Validate}; use rand_core::{CryptoRngCore, OsRng}; -use crate::parallel_utils::transform_parallel; +use crate::parallel_utils::{flatten_results, transform_parallel, zip_map_parallel}; use crate::single::dlog; use crate::single::group::{ - pairing, BatchedPairingChecker11, BatchedPairingChecker12, GroupHasher, F, G1, G2, + BatchedPairingChecker11, BatchedPairingChecker12, GroupHasher, F, G1, G2, }; use crate::single::log::{ContributionHash, Hashable, Phase}; @@ -71,10 +72,11 @@ impl RawCRSElements { /// /// This checks if the structure of the elements uses the secret scalars /// hidden behind the group elements correctly. - #[must_use] - pub fn validate(self) -> Option { + pub fn validate(self) -> anyhow::Result { // 0. Check that we can extract a valid degree out of these elements. - let d = self.get_degree()?; + let d = self + .get_degree() + .ok_or_else(|| anyhow!("failed to get degree"))?; // 1. Check that the elements committing to the secret values are not 0. if self.alpha_1.is_zero() || self.beta_1.is_zero() @@ -82,7 +84,7 @@ impl RawCRSElements { || self.x_1[1].is_zero() || self.x_2[1].is_zero() { - return None; + anyhow::bail!("one of alpha_1, beta_1, beta_2, x_1[1], x_2[1] was zero"); } // 2. Check that the two beta commitments match. // 3. Check that the x values match on both groups. @@ -103,34 +105,33 @@ impl RawCRSElements { for (&beta_x_i, &x_i) in self.beta_x_1.iter().zip(self.x_2.iter()) { checker2.add(beta_x_i, x_i); } - if !self - .x_2 - .iter() - .zip(self.beta_x_1.iter()) - .all(|(x_i, beta_x_i)| pairing(self.beta_1, x_i) == pairing(beta_x_i, G2::generator())) - { - return None; - } // 6. Check that the x_i are the correct powers of x. let mut checker3 = BatchedPairingChecker11::new(self.x_2[1], G2::generator()); for (&x_i, &x_i_plus_1) in self.x_1.iter().zip(self.x_1.iter().skip(1)) { checker3.add(x_i, x_i_plus_1); } // "神だけが私を裁ける" - if transform_parallel( - [Ok(checker0), Ok(checker1), Ok(checker2), Err(checker3)], - |x| match x { - Ok(x) => x.check(&mut OsRng), - Err(x) => x.check(&mut OsRng), + flatten_results(transform_parallel( + [ + (0, Ok(checker0)), + (1, Ok(checker1)), + (2, Ok(checker2)), + (3, Err(checker3)), + ], + |(i, x)| { + let ok = match x { + Ok(x) => x.check(&mut OsRng), + Err(x) => x.check(&mut OsRng), + }; + if ok { + Ok(()) + } else { + Err(anyhow!("checker {} failed", i)) + } }, - ) - .iter() - .any(|x| !x) - { - return None; - } + ))?; - Some(CRSElements { + Ok(CRSElements { degree: d, raw: self, }) @@ -147,6 +148,76 @@ impl RawCRSElements { raw: self, } } + + /// This is a replacement for the CanonicalDeserialize trait impl (more or less). + #[cfg(not(feature = "parallel"))] + pub(crate) fn checked_deserialize_parallel(compress: Compress, data: &[u8]) -> Self { + Self::deserialize_with_mode(data, compress, Validate::Yes) + } + + /// This is a replacement for the CanonicalDeserialize trait impl (more or less). + #[cfg(feature = "parallel")] + pub(crate) fn checked_deserialize_parallel( + compress: Compress, + data: &[u8], + ) -> anyhow::Result { + use rayon::prelude::*; + + let out = Self::deserialize_with_mode(data, compress, Validate::No)?; + out.alpha_1.check()?; + out.beta_1.check()?; + out.beta_2.check()?; + + let mut check_x_1 = Ok(()); + let mut check_x_2 = Ok(()); + let mut check_alpha_x_1 = Ok(()); + let mut check_beta_x_1 = Ok(()); + + rayon::join( + || { + rayon::join( + || { + check_x_1 = out + .x_1 + .par_iter() + .map(|x| x.check()) + .collect::>(); + }, + || { + check_x_2 = out + .x_2 + .par_iter() + .map(|x| x.check()) + .collect::>(); + }, + ) + }, + || { + rayon::join( + || { + check_alpha_x_1 = out + .alpha_x_1 + .par_iter() + .map(|x| x.check()) + .collect::>(); + }, + || { + check_beta_x_1 = out + .beta_x_1 + .par_iter() + .map(|x| x.check()) + .collect::>(); + }, + ) + }, + ); + + check_x_1?; + check_x_2?; + check_alpha_x_1?; + check_beta_x_1?; + Ok(out) + } } impl Hashable for RawCRSElements { @@ -246,6 +317,7 @@ impl RawContribution { self.new_elements .to_owned() .validate() + .ok() .map(|new_elements| Contribution { parent: self.parent, new_elements, @@ -321,38 +393,11 @@ impl Contribution { let beta = F::rand(rng); let x = F::rand(rng); - let mut new = old.clone(); - - new.raw.alpha_1 *= alpha; - new.raw.beta_1 *= beta; - new.raw.beta_2 *= beta; - - let mut x_i = F::one(); - let mut alpha_x_i = alpha; - let mut beta_x_i = beta; - - let d = old.degree; - for i in 0..short_len(d) { - new.raw.x_1[i] *= x_i; - new.raw.x_2[i] *= x_i; - new.raw.alpha_x_1[i] *= alpha_x_i; - new.raw.beta_x_1[i] *= beta_x_i; - - x_i *= x; - alpha_x_i *= x; - beta_x_i *= x; - } - for i in short_len(d)..long_len(d) { - new.raw.x_1[i] *= x_i; - - x_i *= x; - } - let alpha_proof = dlog::prove( rng, b"phase1 alpha proof", dlog::Statement { - result: new.raw.alpha_1, + result: old.raw.alpha_1 * alpha, base: old.raw.alpha_1, }, dlog::Witness { dlog: alpha }, @@ -361,7 +406,7 @@ impl Contribution { rng, b"phase1 beta proof", dlog::Statement { - result: new.raw.beta_1, + result: old.raw.beta_1 * beta, base: old.raw.beta_1, }, dlog::Witness { dlog: beta }, @@ -370,15 +415,64 @@ impl Contribution { rng, b"phase1 x proof", dlog::Statement { - result: new.raw.x_1[1], + result: old.raw.x_1[1] * x, base: old.raw.x_1[1], }, dlog::Witness { dlog: x }, ); + let d = old.degree; + + let (mut x_i_tweaks, mut alpha_x_i_tweaks, mut beta_x_i_tweaks) = { + let mut x_i_tweaks = Vec::new(); + let mut alpha_x_i_tweaks = Vec::new(); + let mut beta_x_i_tweaks = Vec::new(); + + let mut x_i = F::one(); + let mut alpha_x_i = alpha; + let mut beta_x_i = beta; + + for _ in 0..short_len(d) { + x_i_tweaks.push(x_i); + alpha_x_i_tweaks.push(alpha_x_i); + beta_x_i_tweaks.push(beta_x_i); + + x_i *= x; + alpha_x_i *= x; + beta_x_i *= x; + } + for _ in short_len(d)..long_len(d) { + x_i_tweaks.push(x_i); + + x_i *= x; + } + + (x_i_tweaks, alpha_x_i_tweaks, beta_x_i_tweaks) + }; + + let mut old = old.clone(); + let x_1 = zip_map_parallel(&mut old.raw.x_1, &mut x_i_tweaks, |g, x| *g * x); + let x_2 = zip_map_parallel(&mut old.raw.x_2, &mut x_i_tweaks, |g, x| *g * x); + let alpha_x_1 = + zip_map_parallel(&mut old.raw.alpha_x_1, &mut alpha_x_i_tweaks, |g, x| *g * x); + let beta_x_1 = zip_map_parallel(&mut old.raw.beta_x_1, &mut beta_x_i_tweaks, |g, x| *g * x); + + let new_elements = CRSElements { + degree: d, + raw: RawCRSElements { + alpha_1: old.raw.alpha_1 * alpha, + beta_1: old.raw.beta_1 * beta, + beta_2: old.raw.beta_2 * beta, + x_1, + x_2, + alpha_x_1, + beta_x_1, + }, + }; + Self { parent, - new_elements: new, + new_elements, linking_proof: LinkingProof { alpha_proof, beta_proof, @@ -504,27 +598,27 @@ mod test { #[test] fn test_root_crs_is_valid() { let root = CRSElements::root(D); - assert!(root.raw.validate().is_some()); + assert!(root.raw.validate().is_ok()); } #[test] fn test_nontrivial_crs_is_valid() { let crs = non_trivial_crs(); - assert!(crs.validate().is_some()); + assert!(crs.validate().is_ok()); } #[test] fn test_changing_alpha_makes_crs_invalid() { let mut crs = non_trivial_crs(); crs.alpha_1 = G1::generator(); - assert!(crs.validate().is_none()); + assert!(crs.validate().is_err()); } #[test] fn test_changing_beta_makes_crs_invalid() { let mut crs = non_trivial_crs(); crs.beta_1 = G1::generator(); - assert!(crs.validate().is_none()); + assert!(crs.validate().is_err()); } #[test] @@ -534,11 +628,11 @@ mod test { let x = F::rand(&mut OsRng); let crs0 = make_crs(F::zero(), beta, x); - assert!(crs0.validate().is_none()); + assert!(crs0.validate().is_err()); let crs1 = make_crs(alpha, F::zero(), x); - assert!(crs1.validate().is_none()); + assert!(crs1.validate().is_err()); let crs2 = make_crs(alpha, beta, F::zero()); - assert!(crs2.validate().is_none()); + assert!(crs2.validate().is_err()); } #[test] @@ -561,7 +655,7 @@ mod test { alpha_x_1: vec![G1::generator() * alpha, G1::generator() * (alpha * x)], beta_x_1: vec![G1::generator() * beta, G1::generator() * (beta * x)], }; - assert!(crs.validate().is_none()); + assert!(crs.validate().is_err()); } #[test] @@ -572,7 +666,7 @@ mod test { ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]), &root, ); - assert!(contribution.new_elements.raw.validate().is_some()); + assert!(contribution.new_elements.raw.validate().is_ok()); } #[test] diff --git a/crates/crypto/proof-setup/src/single/phase2.rs b/crates/crypto/proof-setup/src/single/phase2.rs index 403b86f617..ad08980730 100644 --- a/crates/crypto/proof-setup/src/single/phase2.rs +++ b/crates/crypto/proof-setup/src/single/phase2.rs @@ -1,7 +1,7 @@ //! This module is very similar to the one for phase1, so reading that one might be useful. use ark_ec::Group; use ark_ff::{fields::Field, UniformRand, Zero}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Valid, Validate}; use rand_core::{CryptoRngCore, OsRng}; use crate::single::log::{ContributionHash, Hashable, Phase}; @@ -59,6 +59,49 @@ impl RawCRSElements { pub(crate) fn assume_valid(self) -> CRSElements { CRSElements { raw: self } } + + /// This is a replacement for the CanonicalDeserialize trait impl (more or less). + #[cfg(not(feature = "parallel"))] + pub(crate) fn checked_deserialize_parallel(compress: Compress, data: &[u8]) -> Self { + Self::deserialize_with_mode(data, compress, Validate::Yes) + } + + /// This is a replacement for the CanonicalDeserialize trait impl (more or less). + #[cfg(feature = "parallel")] + pub(crate) fn checked_deserialize_parallel( + compress: Compress, + data: &[u8], + ) -> anyhow::Result { + use rayon::prelude::*; + + let out = Self::deserialize_with_mode(data, compress, Validate::No)?; + out.delta_1.check()?; + out.delta_2.check()?; + + let mut check_inv_delta_p_1 = Ok(()); + let mut check_inv_delta_t_1 = Ok(()); + + rayon::join( + || { + check_inv_delta_p_1 = out + .inv_delta_p_1 + .par_iter() + .map(|x| x.check()) + .collect::>(); + }, + || { + check_inv_delta_t_1 = out + .inv_delta_t_1 + .par_iter() + .map(|x| x.check()) + .collect::>(); + }, + ); + + check_inv_delta_p_1?; + check_inv_delta_t_1?; + Ok(out) + } } impl Hashable for RawCRSElements { diff --git a/crates/crypto/tct/Cargo.toml b/crates/crypto/tct/Cargo.toml index 6b78d92808..ed425909cb 100644 --- a/crates/crypto/tct/Cargo.toml +++ b/crates/crypto/tct/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-tct" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/custody/Cargo.toml b/crates/custody/Cargo.toml index b5c057b204..f5777e1a6c 100644 --- a/crates/custody/Cargo.toml +++ b/crates/custody/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-custody" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/custody/src/lib.rs b/crates/custody/src/lib.rs index 2f81383a7b..ae26f7a168 100644 --- a/crates/custody/src/lib.rs +++ b/crates/custody/src/lib.rs @@ -12,6 +12,7 @@ mod client; mod pre_auth; mod request; +pub mod null_kms; pub mod policy; pub mod soft_kms; diff --git a/crates/custody/src/null_kms.rs b/crates/custody/src/null_kms.rs new file mode 100644 index 0000000000..ed0cf9db4a --- /dev/null +++ b/crates/custody/src/null_kms.rs @@ -0,0 +1,23 @@ +//! A basic software key management system that stores keys in memory but +//! presents as an asynchronous signer. + +use penumbra_proto::custody::v1alpha1::{self as pb, AuthorizeResponse}; +use tonic::{async_trait, Request, Response, Status}; + +/// A "null KMS" that has no keys and errors on any requests. +/// +/// Useful when operating in "view-only" mode. +#[derive(Debug, Default)] +pub struct NullKms {} + +#[async_trait] +impl pb::custody_protocol_service_server::CustodyProtocolService for NullKms { + async fn authorize( + &self, + _request: Request, + ) -> Result, Status> { + Err(tonic::Status::failed_precondition( + "Got authorization request in view-only mode to null KMS.", + )) + } +} diff --git a/crates/misc/measure/Cargo.toml b/crates/misc/measure/Cargo.toml index a4241cbfc2..863cb3da4d 100644 --- a/crates/misc/measure/Cargo.toml +++ b/crates/misc/measure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-measure" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/misc/tct-visualize/Cargo.toml b/crates/misc/tct-visualize/Cargo.toml index 307477b22b..f58ed8d100 100644 --- a/crates/misc/tct-visualize/Cargo.toml +++ b/crates/misc/tct-visualize/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-tct-visualize" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/narsil/narsil/Cargo.toml b/crates/narsil/narsil/Cargo.toml index 78f9fa8d95..ab7de79fae 100644 --- a/crates/narsil/narsil/Cargo.toml +++ b/crates/narsil/narsil/Cargo.toml @@ -2,7 +2,7 @@ name = "narsil" authors = ["Penumbra Labs "] description = "The software for the Narsil sharded custody implementation" -version = "0.62.0" +version = "0.63.1" edition = "2021" repository = "https://github.com/penumbra-zone/penumbra/" homepage = "https://penumbra.zone" diff --git a/crates/narsil/narsil/src/bin/narsild.rs b/crates/narsil/narsil/src/bin/narsild.rs index 51113594c9..14f856f683 100644 --- a/crates/narsil/narsil/src/bin/narsild.rs +++ b/crates/narsil/narsil/src/bin/narsild.rs @@ -37,7 +37,7 @@ use url::Url; )] struct Opt { /// Enable Tokio Console support. - #[clap(long, default_value = "false")] + #[clap(long)] tokio_console: bool, /// Command to run. #[clap(subcommand)] diff --git a/crates/proto/Cargo.toml b/crates/proto/Cargo.toml index 0f3804d058..1a0ce3bdca 100644 --- a/crates/proto/Cargo.toml +++ b/crates/proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-proto" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.rs b/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.rs index f8f0e2e3a5..7346a97313 100644 --- a/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.rs +++ b/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.rs @@ -14,3 +14,338 @@ pub struct GenesisContent { #[prost(message, optional, tag = "1")] pub dao_params: ::core::option::Option, } +/// Requests the list of all asset balances associated with the DAO. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DaoAssetBalancesRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + /// (Optional): The specific asset balances to retrieve, if excluded all will be returned. + #[prost(message, repeated, tag = "2")] + pub asset_ids: ::prost::alloc::vec::Vec< + super::super::super::asset::v1alpha1::AssetId, + >, +} +/// The DAO's balance of a single asset. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DaoAssetBalancesResponse { + /// The balance for a single asset. + #[prost(message, optional, tag = "1")] + pub balance: ::core::option::Option, +} +/// Generated client implementations. +#[cfg(feature = "rpc")] +pub mod query_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Query operations for the dao component. + #[derive(Debug, Clone)] + pub struct QueryServiceClient { + inner: tonic::client::Grpc, + } + impl QueryServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl QueryServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> QueryServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + QueryServiceClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + pub async fn dao_asset_balances( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.dao.v1alpha1.QueryService/DaoAssetBalances", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.dao.v1alpha1.QueryService", + "DaoAssetBalances", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "rpc")] +pub mod query_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with QueryServiceServer. + #[async_trait] + pub trait QueryService: Send + Sync + 'static { + /// Server streaming response type for the DaoAssetBalances method. + type DaoAssetBalancesStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::DaoAssetBalancesResponse, + tonic::Status, + >, + > + + Send + + 'static; + async fn dao_asset_balances( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Query operations for the dao component. + #[derive(Debug)] + pub struct QueryServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl QueryServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for QueryServiceServer + where + T: QueryService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/penumbra.core.component.dao.v1alpha1.QueryService/DaoAssetBalances" => { + #[allow(non_camel_case_types)] + struct DaoAssetBalancesSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::ServerStreamingService< + super::DaoAssetBalancesRequest, + > for DaoAssetBalancesSvc { + type Response = super::DaoAssetBalancesResponse; + type ResponseStream = T::DaoAssetBalancesStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::dao_asset_balances(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DaoAssetBalancesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for QueryServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for QueryServiceServer { + const NAME: &'static str = "penumbra.core.component.dao.v1alpha1.QueryService"; + } +} diff --git a/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.serde.rs b/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.serde.rs index 9ce10c4b9b..ca0ba6c6a3 100644 --- a/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.dao.v1alpha1.serde.rs @@ -1,3 +1,204 @@ +impl serde::Serialize for DaoAssetBalancesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.chain_id.is_empty() { + len += 1; + } + if !self.asset_ids.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.dao.v1alpha1.DaoAssetBalancesRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; + } + if !self.asset_ids.is_empty() { + struct_ser.serialize_field("assetIds", &self.asset_ids)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for DaoAssetBalancesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "chain_id", + "chainId", + "asset_ids", + "assetIds", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ChainId, + AssetIds, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + "assetIds" | "asset_ids" => Ok(GeneratedField::AssetIds), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = DaoAssetBalancesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.dao.v1alpha1.DaoAssetBalancesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + let mut asset_ids__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + GeneratedField::AssetIds => { + if asset_ids__.is_some() { + return Err(serde::de::Error::duplicate_field("assetIds")); + } + asset_ids__ = Some(map_.next_value()?); + } + } + } + Ok(DaoAssetBalancesRequest { + chain_id: chain_id__.unwrap_or_default(), + asset_ids: asset_ids__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.dao.v1alpha1.DaoAssetBalancesRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for DaoAssetBalancesResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.balance.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.dao.v1alpha1.DaoAssetBalancesResponse", len)?; + if let Some(v) = self.balance.as_ref() { + struct_ser.serialize_field("balance", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for DaoAssetBalancesResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "balance", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Balance, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "balance" => Ok(GeneratedField::Balance), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = DaoAssetBalancesResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.dao.v1alpha1.DaoAssetBalancesResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut balance__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Balance => { + if balance__.is_some() { + return Err(serde::de::Error::duplicate_field("balance")); + } + balance__ = map_.next_value()?; + } + } + } + Ok(DaoAssetBalancesResponse { + balance: balance__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.dao.v1alpha1.DaoAssetBalancesResponse", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for DaoParameters { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.rs b/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.rs index 25fc36ffd4..31d0fbd602 100644 --- a/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.rs +++ b/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.rs @@ -488,6 +488,40 @@ pub struct ProposalInfoResponse { #[prost(uint64, tag = "2")] pub start_position: u64, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalDataRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + /// The proposal id to request information on. + #[prost(uint64, tag = "2")] + pub proposal_id: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalDataResponse { + /// The proposal metadata. + #[prost(message, optional, tag = "1")] + pub proposal: ::core::option::Option, + /// The block height at which the proposal started voting. + #[prost(uint64, tag = "2")] + pub start_block_height: u64, + /// The block height at which the proposal ends voting. + #[prost(uint64, tag = "3")] + pub end_block_height: u64, + /// The position of the state commitment tree at which the proposal is considered to have started voting. + #[prost(uint64, tag = "4")] + pub start_position: u64, + /// The current state of the proposal. + #[prost(message, optional, tag = "5")] + pub state: ::core::option::Option, + /// The deposit amount paid for the proposal. + #[prost(message, optional, tag = "6")] + pub proposal_deposit_amount: ::core::option::Option< + super::super::super::num::v1alpha1::Amount, + >, +} /// Requests the validator rate data for a proposal. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -506,6 +540,63 @@ pub struct ProposalRateDataResponse { #[prost(message, optional, tag = "1")] pub rate_data: ::core::option::Option, } +/// Requests the list of all proposals. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalListRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + /// Whether to include proposals that are no longer active.; + /// + /// TODO: we could filter by starting block height here? + #[prost(bool, tag = "2")] + pub inactive: bool, +} +/// The data for a single proposal. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalListResponse { + /// The proposal metadata. + #[prost(message, optional, tag = "1")] + pub proposal: ::core::option::Option, + /// The block height at which the proposal started voting. + #[prost(uint64, tag = "2")] + pub start_block_height: u64, + /// The block height at which the proposal ends voting. + #[prost(uint64, tag = "3")] + pub end_block_height: u64, + /// The position of the state commitment tree at which the proposal is considered to have started voting. + #[prost(uint64, tag = "4")] + pub start_position: u64, + /// The current state of the proposal. + #[prost(message, optional, tag = "5")] + pub state: ::core::option::Option, +} +/// Requests the list of all validator votes for a given proposal. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ValidatorVotesRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + /// The proposal id to request information on. + #[prost(uint64, tag = "2")] + pub proposal_id: u64, +} +/// The data for a single validator vote. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ValidatorVotesResponse { + /// The vote. + #[prost(message, optional, tag = "1")] + pub vote: ::core::option::Option, + /// The validator identity. + #[prost(message, optional, tag = "2")] + pub identity_key: ::core::option::Option< + super::super::super::keys::v1alpha1::IdentityKey, + >, +} /// Governance configuration data. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -576,6 +667,67 @@ pub struct ChangedAppParametersSet { #[prost(message, optional, tag = "2")] pub new: ::core::option::Option, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VotingPowerAtProposalStartRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + /// The proposal id to request information on. + #[prost(uint64, tag = "2")] + pub proposal_id: u64, + /// The validator identity key to request information on. + #[prost(message, optional, tag = "3")] + pub identity_key: ::core::option::Option< + super::super::super::keys::v1alpha1::IdentityKey, + >, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VotingPowerAtProposalStartResponse { + /// The voting power for the given identity key at the start of the proposal. + /// TODO: since we don't support optional fields in our protos any more, + /// this will be set to 0 if the validator was not active at the start of the proposal. + /// Is this potentially an issue? + #[prost(uint64, tag = "1")] + pub voting_power: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AllTalliedDelegatorVotesForProposalRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + /// The proposal id to request information on. + #[prost(uint64, tag = "2")] + pub proposal_id: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AllTalliedDelegatorVotesForProposalResponse { + /// The tally of delegator votes for a given validator for the proposal. + #[prost(message, optional, tag = "1")] + pub tally: ::core::option::Option, + /// The validator identity associated with the tally. + #[prost(message, optional, tag = "2")] + pub identity_key: ::core::option::Option< + super::super::super::keys::v1alpha1::IdentityKey, + >, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NextProposalIdRequest { + /// The expected chain id (empty string if no expectation). + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NextProposalIdResponse { + /// The next proposal ID. + #[prost(uint64, tag = "1")] + pub next_proposal_id: u64, +} /// Generated client implementations. #[cfg(feature = "rpc")] pub mod query_service_client { @@ -693,12 +845,11 @@ pub mod query_service_client { ); self.inner.unary(req, path, codec).await } - /// Used for computing voting power ? - pub async fn proposal_rate_data( + pub async fn proposal_list( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< - tonic::Response>, + tonic::Response>, tonic::Status, > { self.inner @@ -712,95 +863,345 @@ pub mod query_service_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalRateData", + "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalList", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "penumbra.core.component.governance.v1alpha1.QueryService", - "ProposalRateData", + "ProposalList", ), ); self.inner.server_streaming(req, path, codec).await } - } -} -/// Generated server implementations. -#[cfg(feature = "rpc")] -pub mod query_service_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with QueryServiceServer. - #[async_trait] - pub trait QueryService: Send + Sync + 'static { - async fn proposal_info( - &self, - request: tonic::Request, + pub async fn proposal_data( + &mut self, + request: impl tonic::IntoRequest, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, - >; - /// Server streaming response type for the ProposalRateData method. - type ProposalRateDataStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::ProposalRateDataResponse, - tonic::Status, - >, - > - + Send - + 'static; - /// Used for computing voting power ? - async fn proposal_rate_data( - &self, - request: tonic::Request, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalData", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.governance.v1alpha1.QueryService", + "ProposalData", + ), + ); + self.inner.unary(req, path, codec).await + } + pub async fn next_proposal_id( + &mut self, + request: impl tonic::IntoRequest, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, - >; - } - /// Query operations for the governance component. - #[derive(Debug)] - pub struct QueryServiceServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl QueryServiceServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.governance.v1alpha1.QueryService/NextProposalId", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.governance.v1alpha1.QueryService", + "NextProposalId", + ), + ); + self.inner.unary(req, path, codec).await } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self + pub async fn validator_votes( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.governance.v1alpha1.QueryService/ValidatorVotes", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.governance.v1alpha1.QueryService", + "ValidatorVotes", + ), + ); + self.inner.server_streaming(req, path, codec).await } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + pub async fn voting_power_at_proposal_start( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.governance.v1alpha1.QueryService/VotingPowerAtProposalStart", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.governance.v1alpha1.QueryService", + "VotingPowerAtProposalStart", + ), + ); + self.inner.unary(req, path, codec).await + } + pub async fn all_tallied_delegator_votes_for_proposal( + &mut self, + request: impl tonic::IntoRequest< + super::AllTalliedDelegatorVotesForProposalRequest, + >, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming< + super::AllTalliedDelegatorVotesForProposalResponse, + >, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.governance.v1alpha1.QueryService/AllTalliedDelegatorVotesForProposal", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.governance.v1alpha1.QueryService", + "AllTalliedDelegatorVotesForProposal", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + /// Used for computing voting power ? + pub async fn proposal_rate_data( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalRateData", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.governance.v1alpha1.QueryService", + "ProposalRateData", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "rpc")] +pub mod query_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with QueryServiceServer. + #[async_trait] + pub trait QueryService: Send + Sync + 'static { + async fn proposal_info( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the ProposalList method. + type ProposalListStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + async fn proposal_list( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn proposal_data( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn next_proposal_id( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the ValidatorVotes method. + type ValidatorVotesStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + async fn validator_votes( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn voting_power_at_proposal_start( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the AllTalliedDelegatorVotesForProposal method. + type AllTalliedDelegatorVotesForProposalStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::AllTalliedDelegatorVotesForProposalResponse, + tonic::Status, + >, + > + + Send + + 'static; + async fn all_tallied_delegator_votes_for_proposal( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the ProposalRateData method. + type ProposalRateDataStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::ProposalRateDataResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// Used for computing voting power ? + async fn proposal_rate_data( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Query operations for the governance component. + #[derive(Debug)] + pub struct QueryServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl QueryServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { self.send_compression_encodings.enable(encoding); self } @@ -885,6 +1286,301 @@ pub mod query_service_server { }; Box::pin(fut) } + "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalList" => { + #[allow(non_camel_case_types)] + struct ProposalListSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::ServerStreamingService + for ProposalListSvc { + type Response = super::ProposalListResponse; + type ResponseStream = T::ProposalListStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::proposal_list(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ProposalListSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalData" => { + #[allow(non_camel_case_types)] + struct ProposalDataSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::UnaryService + for ProposalDataSvc { + type Response = super::ProposalDataResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::proposal_data(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ProposalDataSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/penumbra.core.component.governance.v1alpha1.QueryService/NextProposalId" => { + #[allow(non_camel_case_types)] + struct NextProposalIdSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::UnaryService + for NextProposalIdSvc { + type Response = super::NextProposalIdResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::next_proposal_id(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = NextProposalIdSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/penumbra.core.component.governance.v1alpha1.QueryService/ValidatorVotes" => { + #[allow(non_camel_case_types)] + struct ValidatorVotesSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::ServerStreamingService + for ValidatorVotesSvc { + type Response = super::ValidatorVotesResponse; + type ResponseStream = T::ValidatorVotesStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::validator_votes(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ValidatorVotesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/penumbra.core.component.governance.v1alpha1.QueryService/VotingPowerAtProposalStart" => { + #[allow(non_camel_case_types)] + struct VotingPowerAtProposalStartSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::UnaryService< + super::VotingPowerAtProposalStartRequest, + > for VotingPowerAtProposalStartSvc { + type Response = super::VotingPowerAtProposalStartResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::VotingPowerAtProposalStartRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::voting_power_at_proposal_start( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = VotingPowerAtProposalStartSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/penumbra.core.component.governance.v1alpha1.QueryService/AllTalliedDelegatorVotesForProposal" => { + #[allow(non_camel_case_types)] + struct AllTalliedDelegatorVotesForProposalSvc( + pub Arc, + ); + impl< + T: QueryService, + > tonic::server::ServerStreamingService< + super::AllTalliedDelegatorVotesForProposalRequest, + > for AllTalliedDelegatorVotesForProposalSvc { + type Response = super::AllTalliedDelegatorVotesForProposalResponse; + type ResponseStream = T::AllTalliedDelegatorVotesForProposalStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::AllTalliedDelegatorVotesForProposalRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::all_tallied_delegator_votes_for_proposal( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = AllTalliedDelegatorVotesForProposalSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } "/penumbra.core.component.governance.v1alpha1.QueryService/ProposalRateData" => { #[allow(non_camel_case_types)] struct ProposalRateDataSvc(pub Arc); diff --git a/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.serde.rs b/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.serde.rs index 087d366998..9dfa27ed5d 100644 --- a/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.governance.v1alpha1.serde.rs @@ -1,3 +1,225 @@ +impl serde::Serialize for AllTalliedDelegatorVotesForProposalRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.chain_id.is_empty() { + len += 1; + } + if self.proposal_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.AllTalliedDelegatorVotesForProposalRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; + } + if self.proposal_id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("proposalId", ToString::to_string(&self.proposal_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AllTalliedDelegatorVotesForProposalRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "chain_id", + "chainId", + "proposal_id", + "proposalId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ChainId, + ProposalId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + "proposalId" | "proposal_id" => Ok(GeneratedField::ProposalId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AllTalliedDelegatorVotesForProposalRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.AllTalliedDelegatorVotesForProposalRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + let mut proposal_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + GeneratedField::ProposalId => { + if proposal_id__.is_some() { + return Err(serde::de::Error::duplicate_field("proposalId")); + } + proposal_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(AllTalliedDelegatorVotesForProposalRequest { + chain_id: chain_id__.unwrap_or_default(), + proposal_id: proposal_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.AllTalliedDelegatorVotesForProposalRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for AllTalliedDelegatorVotesForProposalResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.tally.is_some() { + len += 1; + } + if self.identity_key.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.AllTalliedDelegatorVotesForProposalResponse", len)?; + if let Some(v) = self.tally.as_ref() { + struct_ser.serialize_field("tally", v)?; + } + if let Some(v) = self.identity_key.as_ref() { + struct_ser.serialize_field("identityKey", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AllTalliedDelegatorVotesForProposalResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "tally", + "identity_key", + "identityKey", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Tally, + IdentityKey, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "tally" => Ok(GeneratedField::Tally), + "identityKey" | "identity_key" => Ok(GeneratedField::IdentityKey), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AllTalliedDelegatorVotesForProposalResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.AllTalliedDelegatorVotesForProposalResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut tally__ = None; + let mut identity_key__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Tally => { + if tally__.is_some() { + return Err(serde::de::Error::duplicate_field("tally")); + } + tally__ = map_.next_value()?; + } + GeneratedField::IdentityKey => { + if identity_key__.is_some() { + return Err(serde::de::Error::duplicate_field("identityKey")); + } + identity_key__ = map_.next_value()?; + } + } + } + Ok(AllTalliedDelegatorVotesForProposalResponse { + tally: tally__, + identity_key: identity_key__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.AllTalliedDelegatorVotesForProposalResponse", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for ChangedAppParameters { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -1731,7 +1953,7 @@ impl<'de> serde::Deserialize<'de> for GovernanceParameters { deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.GovernanceParameters", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for Proposal { +impl serde::Serialize for NextProposalIdRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1739,76 +1961,263 @@ impl serde::Serialize for Proposal { { use serde::ser::SerializeStruct; let mut len = 0; - if self.id != 0 { - len += 1; - } - if !self.title.is_empty() { - len += 1; - } - if !self.description.is_empty() { - len += 1; - } - if self.signaling.is_some() { - len += 1; - } - if self.emergency.is_some() { - len += 1; - } - if self.parameter_change.is_some() { - len += 1; - } - if self.dao_spend.is_some() { - len += 1; - } - if self.upgrade_plan.is_some() { + if !self.chain_id.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.Proposal", len)?; - if self.id != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; - } - if !self.title.is_empty() { - struct_ser.serialize_field("title", &self.title)?; - } - if !self.description.is_empty() { - struct_ser.serialize_field("description", &self.description)?; - } - if let Some(v) = self.signaling.as_ref() { - struct_ser.serialize_field("signaling", v)?; - } - if let Some(v) = self.emergency.as_ref() { - struct_ser.serialize_field("emergency", v)?; - } - if let Some(v) = self.parameter_change.as_ref() { - struct_ser.serialize_field("parameterChange", v)?; - } - if let Some(v) = self.dao_spend.as_ref() { - struct_ser.serialize_field("daoSpend", v)?; - } - if let Some(v) = self.upgrade_plan.as_ref() { - struct_ser.serialize_field("upgradePlan", v)?; + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.NextProposalIdRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for Proposal { +impl<'de> serde::Deserialize<'de> for NextProposalIdRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "id", - "title", - "description", - "signaling", - "emergency", - "parameter_change", - "parameterChange", - "dao_spend", - "daoSpend", - "upgrade_plan", + "chain_id", + "chainId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ChainId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = NextProposalIdRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.NextProposalIdRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + } + } + Ok(NextProposalIdRequest { + chain_id: chain_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.NextProposalIdRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for NextProposalIdResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.next_proposal_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.NextProposalIdResponse", len)?; + if self.next_proposal_id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("nextProposalId", ToString::to_string(&self.next_proposal_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for NextProposalIdResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "next_proposal_id", + "nextProposalId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + NextProposalId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "nextProposalId" | "next_proposal_id" => Ok(GeneratedField::NextProposalId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = NextProposalIdResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.NextProposalIdResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut next_proposal_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::NextProposalId => { + if next_proposal_id__.is_some() { + return Err(serde::de::Error::duplicate_field("nextProposalId")); + } + next_proposal_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(NextProposalIdResponse { + next_proposal_id: next_proposal_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.NextProposalIdResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Proposal { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.id != 0 { + len += 1; + } + if !self.title.is_empty() { + len += 1; + } + if !self.description.is_empty() { + len += 1; + } + if self.signaling.is_some() { + len += 1; + } + if self.emergency.is_some() { + len += 1; + } + if self.parameter_change.is_some() { + len += 1; + } + if self.dao_spend.is_some() { + len += 1; + } + if self.upgrade_plan.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.Proposal", len)?; + if self.id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; + } + if !self.title.is_empty() { + struct_ser.serialize_field("title", &self.title)?; + } + if !self.description.is_empty() { + struct_ser.serialize_field("description", &self.description)?; + } + if let Some(v) = self.signaling.as_ref() { + struct_ser.serialize_field("signaling", v)?; + } + if let Some(v) = self.emergency.as_ref() { + struct_ser.serialize_field("emergency", v)?; + } + if let Some(v) = self.parameter_change.as_ref() { + struct_ser.serialize_field("parameterChange", v)?; + } + if let Some(v) = self.dao_spend.as_ref() { + struct_ser.serialize_field("daoSpend", v)?; + } + if let Some(v) = self.upgrade_plan.as_ref() { + struct_ser.serialize_field("upgradePlan", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Proposal { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "id", + "title", + "description", + "signaling", + "emergency", + "parameter_change", + "parameterChange", + "dao_spend", + "daoSpend", + "upgrade_plan", "upgradePlan", ]; @@ -2426,7 +2835,7 @@ impl<'de> serde::Deserialize<'de> for proposal::UpgradePlan { deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.Proposal.UpgradePlan", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for ProposalDepositClaim { +impl serde::Serialize for ProposalDataRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2434,47 +2843,40 @@ impl serde::Serialize for ProposalDepositClaim { { use serde::ser::SerializeStruct; let mut len = 0; - if self.proposal != 0 { + if !self.chain_id.is_empty() { len += 1; } - if self.deposit_amount.is_some() { + if self.proposal_id != 0 { len += 1; } - if self.outcome.is_some() { - len += 1; + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalDataRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalDepositClaim", len)?; - if self.proposal != 0 { + if self.proposal_id != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("proposal", ToString::to_string(&self.proposal).as_str())?; - } - if let Some(v) = self.deposit_amount.as_ref() { - struct_ser.serialize_field("depositAmount", v)?; - } - if let Some(v) = self.outcome.as_ref() { - struct_ser.serialize_field("outcome", v)?; + struct_ser.serialize_field("proposalId", ToString::to_string(&self.proposal_id).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for ProposalDepositClaim { +impl<'de> serde::Deserialize<'de> for ProposalDataRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "proposal", - "deposit_amount", - "depositAmount", - "outcome", + "chain_id", + "chainId", + "proposal_id", + "proposalId", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Proposal, - DepositAmount, - Outcome, + ChainId, + ProposalId, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2496,9 +2898,8 @@ impl<'de> serde::Deserialize<'de> for ProposalDepositClaim { E: serde::de::Error, { match value { - "proposal" => Ok(GeneratedField::Proposal), - "depositAmount" | "deposit_amount" => Ok(GeneratedField::DepositAmount), - "outcome" => Ok(GeneratedField::Outcome), + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + "proposalId" | "proposal_id" => Ok(GeneratedField::ProposalId), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2508,13 +2909,323 @@ impl<'de> serde::Deserialize<'de> for ProposalDepositClaim { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ProposalDepositClaim; + type Value = ProposalDataRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalDepositClaim") + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalDataRequest") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + let mut proposal_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + GeneratedField::ProposalId => { + if proposal_id__.is_some() { + return Err(serde::de::Error::duplicate_field("proposalId")); + } + proposal_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(ProposalDataRequest { + chain_id: chain_id__.unwrap_or_default(), + proposal_id: proposal_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalDataRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ProposalDataResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.proposal.is_some() { + len += 1; + } + if self.start_block_height != 0 { + len += 1; + } + if self.end_block_height != 0 { + len += 1; + } + if self.start_position != 0 { + len += 1; + } + if self.state.is_some() { + len += 1; + } + if self.proposal_deposit_amount.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalDataResponse", len)?; + if let Some(v) = self.proposal.as_ref() { + struct_ser.serialize_field("proposal", v)?; + } + if self.start_block_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("startBlockHeight", ToString::to_string(&self.start_block_height).as_str())?; + } + if self.end_block_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("endBlockHeight", ToString::to_string(&self.end_block_height).as_str())?; + } + if self.start_position != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("startPosition", ToString::to_string(&self.start_position).as_str())?; + } + if let Some(v) = self.state.as_ref() { + struct_ser.serialize_field("state", v)?; + } + if let Some(v) = self.proposal_deposit_amount.as_ref() { + struct_ser.serialize_field("proposalDepositAmount", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ProposalDataResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "proposal", + "start_block_height", + "startBlockHeight", + "end_block_height", + "endBlockHeight", + "start_position", + "startPosition", + "state", + "proposal_deposit_amount", + "proposalDepositAmount", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Proposal, + StartBlockHeight, + EndBlockHeight, + StartPosition, + State, + ProposalDepositAmount, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "proposal" => Ok(GeneratedField::Proposal), + "startBlockHeight" | "start_block_height" => Ok(GeneratedField::StartBlockHeight), + "endBlockHeight" | "end_block_height" => Ok(GeneratedField::EndBlockHeight), + "startPosition" | "start_position" => Ok(GeneratedField::StartPosition), + "state" => Ok(GeneratedField::State), + "proposalDepositAmount" | "proposal_deposit_amount" => Ok(GeneratedField::ProposalDepositAmount), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProposalDataResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalDataResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut proposal__ = None; + let mut start_block_height__ = None; + let mut end_block_height__ = None; + let mut start_position__ = None; + let mut state__ = None; + let mut proposal_deposit_amount__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Proposal => { + if proposal__.is_some() { + return Err(serde::de::Error::duplicate_field("proposal")); + } + proposal__ = map_.next_value()?; + } + GeneratedField::StartBlockHeight => { + if start_block_height__.is_some() { + return Err(serde::de::Error::duplicate_field("startBlockHeight")); + } + start_block_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::EndBlockHeight => { + if end_block_height__.is_some() { + return Err(serde::de::Error::duplicate_field("endBlockHeight")); + } + end_block_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::StartPosition => { + if start_position__.is_some() { + return Err(serde::de::Error::duplicate_field("startPosition")); + } + start_position__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::State => { + if state__.is_some() { + return Err(serde::de::Error::duplicate_field("state")); + } + state__ = map_.next_value()?; + } + GeneratedField::ProposalDepositAmount => { + if proposal_deposit_amount__.is_some() { + return Err(serde::de::Error::duplicate_field("proposalDepositAmount")); + } + proposal_deposit_amount__ = map_.next_value()?; + } + } + } + Ok(ProposalDataResponse { + proposal: proposal__, + start_block_height: start_block_height__.unwrap_or_default(), + end_block_height: end_block_height__.unwrap_or_default(), + start_position: start_position__.unwrap_or_default(), + state: state__, + proposal_deposit_amount: proposal_deposit_amount__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalDataResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ProposalDepositClaim { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.proposal != 0 { + len += 1; + } + if self.deposit_amount.is_some() { + len += 1; + } + if self.outcome.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalDepositClaim", len)?; + if self.proposal != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("proposal", ToString::to_string(&self.proposal).as_str())?; + } + if let Some(v) = self.deposit_amount.as_ref() { + struct_ser.serialize_field("depositAmount", v)?; + } + if let Some(v) = self.outcome.as_ref() { + struct_ser.serialize_field("outcome", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ProposalDepositClaim { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "proposal", + "deposit_amount", + "depositAmount", + "outcome", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Proposal, + DepositAmount, + Outcome, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "proposal" => Ok(GeneratedField::Proposal), + "depositAmount" | "deposit_amount" => Ok(GeneratedField::DepositAmount), + "outcome" => Ok(GeneratedField::Outcome), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProposalDepositClaim; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalDepositClaim") + } + + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { @@ -2632,15 +3343,242 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoRequest { type Value = ProposalInfoRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalInfoRequest") + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalInfoRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + let mut proposal_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + GeneratedField::ProposalId => { + if proposal_id__.is_some() { + return Err(serde::de::Error::duplicate_field("proposalId")); + } + proposal_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(ProposalInfoRequest { + chain_id: chain_id__.unwrap_or_default(), + proposal_id: proposal_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalInfoRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ProposalInfoResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.start_block_height != 0 { + len += 1; + } + if self.start_position != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalInfoResponse", len)?; + if self.start_block_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("startBlockHeight", ToString::to_string(&self.start_block_height).as_str())?; + } + if self.start_position != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("startPosition", ToString::to_string(&self.start_position).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "start_block_height", + "startBlockHeight", + "start_position", + "startPosition", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + StartBlockHeight, + StartPosition, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "startBlockHeight" | "start_block_height" => Ok(GeneratedField::StartBlockHeight), + "startPosition" | "start_position" => Ok(GeneratedField::StartPosition), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProposalInfoResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalInfoResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut start_block_height__ = None; + let mut start_position__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::StartBlockHeight => { + if start_block_height__.is_some() { + return Err(serde::de::Error::duplicate_field("startBlockHeight")); + } + start_block_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::StartPosition => { + if start_position__.is_some() { + return Err(serde::de::Error::duplicate_field("startPosition")); + } + start_position__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(ProposalInfoResponse { + start_block_height: start_block_height__.unwrap_or_default(), + start_position: start_position__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalInfoResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ProposalListRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.chain_id.is_empty() { + len += 1; + } + if self.inactive { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalListRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; + } + if self.inactive { + struct_ser.serialize_field("inactive", &self.inactive)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ProposalListRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "chain_id", + "chainId", + "inactive", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ChainId, + Inactive, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + "inactive" => Ok(GeneratedField::Inactive), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProposalListRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalListRequest") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { let mut chain_id__ = None; - let mut proposal_id__ = None; + let mut inactive__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::ChainId => { @@ -2649,26 +3587,24 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoRequest { } chain_id__ = Some(map_.next_value()?); } - GeneratedField::ProposalId => { - if proposal_id__.is_some() { - return Err(serde::de::Error::duplicate_field("proposalId")); + GeneratedField::Inactive => { + if inactive__.is_some() { + return Err(serde::de::Error::duplicate_field("inactive")); } - proposal_id__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; + inactive__ = Some(map_.next_value()?); } } } - Ok(ProposalInfoRequest { + Ok(ProposalListRequest { chain_id: chain_id__.unwrap_or_default(), - proposal_id: proposal_id__.unwrap_or_default(), + inactive: inactive__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalInfoRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalListRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for ProposalInfoResponse { +impl serde::Serialize for ProposalListResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2676,41 +3612,67 @@ impl serde::Serialize for ProposalInfoResponse { { use serde::ser::SerializeStruct; let mut len = 0; + if self.proposal.is_some() { + len += 1; + } if self.start_block_height != 0 { len += 1; } + if self.end_block_height != 0 { + len += 1; + } if self.start_position != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalInfoResponse", len)?; + if self.state.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ProposalListResponse", len)?; + if let Some(v) = self.proposal.as_ref() { + struct_ser.serialize_field("proposal", v)?; + } if self.start_block_height != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("startBlockHeight", ToString::to_string(&self.start_block_height).as_str())?; } + if self.end_block_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("endBlockHeight", ToString::to_string(&self.end_block_height).as_str())?; + } if self.start_position != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("startPosition", ToString::to_string(&self.start_position).as_str())?; } + if let Some(v) = self.state.as_ref() { + struct_ser.serialize_field("state", v)?; + } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { +impl<'de> serde::Deserialize<'de> for ProposalListResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ + "proposal", "start_block_height", "startBlockHeight", + "end_block_height", + "endBlockHeight", "start_position", "startPosition", + "state", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { + Proposal, StartBlockHeight, + EndBlockHeight, StartPosition, + State, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2732,8 +3694,11 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { E: serde::de::Error, { match value { + "proposal" => Ok(GeneratedField::Proposal), "startBlockHeight" | "start_block_height" => Ok(GeneratedField::StartBlockHeight), + "endBlockHeight" | "end_block_height" => Ok(GeneratedField::EndBlockHeight), "startPosition" | "start_position" => Ok(GeneratedField::StartPosition), + "state" => Ok(GeneratedField::State), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2743,20 +3708,29 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ProposalInfoResponse; + type Value = ProposalListResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalInfoResponse") + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ProposalListResponse") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { + let mut proposal__ = None; let mut start_block_height__ = None; + let mut end_block_height__ = None; let mut start_position__ = None; + let mut state__ = None; while let Some(k) = map_.next_key()? { match k { + GeneratedField::Proposal => { + if proposal__.is_some() { + return Err(serde::de::Error::duplicate_field("proposal")); + } + proposal__ = map_.next_value()?; + } GeneratedField::StartBlockHeight => { if start_block_height__.is_some() { return Err(serde::de::Error::duplicate_field("startBlockHeight")); @@ -2765,6 +3739,14 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } + GeneratedField::EndBlockHeight => { + if end_block_height__.is_some() { + return Err(serde::de::Error::duplicate_field("endBlockHeight")); + } + end_block_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } GeneratedField::StartPosition => { if start_position__.is_some() { return Err(serde::de::Error::duplicate_field("startPosition")); @@ -2773,15 +3755,24 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } + GeneratedField::State => { + if state__.is_some() { + return Err(serde::de::Error::duplicate_field("state")); + } + state__ = map_.next_value()?; + } } } - Ok(ProposalInfoResponse { + Ok(ProposalListResponse { + proposal: proposal__, start_block_height: start_block_height__.unwrap_or_default(), + end_block_height: end_block_height__.unwrap_or_default(), start_position: start_position__.unwrap_or_default(), + state: state__, }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalInfoResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ProposalListResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ProposalOutcome { @@ -4549,19 +5540,223 @@ impl<'de> serde::Deserialize<'de> for ValidatorVoteBody { } } } - Ok(ValidatorVoteBody { - proposal: proposal__.unwrap_or_default(), - vote: vote__, - identity_key: identity_key__, - governance_key: governance_key__, - reason: reason__, + Ok(ValidatorVoteBody { + proposal: proposal__.unwrap_or_default(), + vote: vote__, + identity_key: identity_key__, + governance_key: governance_key__, + reason: reason__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVoteBody", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ValidatorVoteReason { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.reason.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVoteReason", len)?; + if !self.reason.is_empty() { + struct_ser.serialize_field("reason", &self.reason)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ValidatorVoteReason { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "reason", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Reason, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "reason" => Ok(GeneratedField::Reason), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ValidatorVoteReason; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ValidatorVoteReason") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut reason__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Reason => { + if reason__.is_some() { + return Err(serde::de::Error::duplicate_field("reason")); + } + reason__ = Some(map_.next_value()?); + } + } + } + Ok(ValidatorVoteReason { + reason: reason__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVoteReason", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ValidatorVotesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.chain_id.is_empty() { + len += 1; + } + if self.proposal_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVotesRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; + } + if self.proposal_id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("proposalId", ToString::to_string(&self.proposal_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ValidatorVotesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "chain_id", + "chainId", + "proposal_id", + "proposalId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ChainId, + ProposalId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + "proposalId" | "proposal_id" => Ok(GeneratedField::ProposalId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ValidatorVotesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ValidatorVotesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + let mut proposal_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + GeneratedField::ProposalId => { + if proposal_id__.is_some() { + return Err(serde::de::Error::duplicate_field("proposalId")); + } + proposal_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(ValidatorVotesRequest { + chain_id: chain_id__.unwrap_or_default(), + proposal_id: proposal_id__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVoteBody", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVotesRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for ValidatorVoteReason { +impl serde::Serialize for ValidatorVotesResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -4569,29 +5764,38 @@ impl serde::Serialize for ValidatorVoteReason { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.reason.is_empty() { + if self.vote.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVoteReason", len)?; - if !self.reason.is_empty() { - struct_ser.serialize_field("reason", &self.reason)?; + if self.identity_key.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVotesResponse", len)?; + if let Some(v) = self.vote.as_ref() { + struct_ser.serialize_field("vote", v)?; + } + if let Some(v) = self.identity_key.as_ref() { + struct_ser.serialize_field("identityKey", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for ValidatorVoteReason { +impl<'de> serde::Deserialize<'de> for ValidatorVotesResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "reason", + "vote", + "identity_key", + "identityKey", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Reason, + Vote, + IdentityKey, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -4613,7 +5817,8 @@ impl<'de> serde::Deserialize<'de> for ValidatorVoteReason { E: serde::de::Error, { match value { - "reason" => Ok(GeneratedField::Reason), + "vote" => Ok(GeneratedField::Vote), + "identityKey" | "identity_key" => Ok(GeneratedField::IdentityKey), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -4623,33 +5828,41 @@ impl<'de> serde::Deserialize<'de> for ValidatorVoteReason { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ValidatorVoteReason; + type Value = ValidatorVotesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ValidatorVoteReason") + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.ValidatorVotesResponse") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - let mut reason__ = None; + let mut vote__ = None; + let mut identity_key__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Reason => { - if reason__.is_some() { - return Err(serde::de::Error::duplicate_field("reason")); + GeneratedField::Vote => { + if vote__.is_some() { + return Err(serde::de::Error::duplicate_field("vote")); } - reason__ = Some(map_.next_value()?); + vote__ = map_.next_value()?; + } + GeneratedField::IdentityKey => { + if identity_key__.is_some() { + return Err(serde::de::Error::duplicate_field("identityKey")); + } + identity_key__ = map_.next_value()?; } } } - Ok(ValidatorVoteReason { - reason: reason__.unwrap_or_default(), + Ok(ValidatorVotesResponse { + vote: vote__, + identity_key: identity_key__, }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVoteReason", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.ValidatorVotesResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for Vote { @@ -4822,6 +6035,232 @@ impl<'de> serde::Deserialize<'de> for vote::Vote { deserializer.deserialize_any(GeneratedVisitor) } } +impl serde::Serialize for VotingPowerAtProposalStartRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.chain_id.is_empty() { + len += 1; + } + if self.proposal_id != 0 { + len += 1; + } + if self.identity_key.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.VotingPowerAtProposalStartRequest", len)?; + if !self.chain_id.is_empty() { + struct_ser.serialize_field("chainId", &self.chain_id)?; + } + if self.proposal_id != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("proposalId", ToString::to_string(&self.proposal_id).as_str())?; + } + if let Some(v) = self.identity_key.as_ref() { + struct_ser.serialize_field("identityKey", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for VotingPowerAtProposalStartRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "chain_id", + "chainId", + "proposal_id", + "proposalId", + "identity_key", + "identityKey", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ChainId, + ProposalId, + IdentityKey, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "chainId" | "chain_id" => Ok(GeneratedField::ChainId), + "proposalId" | "proposal_id" => Ok(GeneratedField::ProposalId), + "identityKey" | "identity_key" => Ok(GeneratedField::IdentityKey), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = VotingPowerAtProposalStartRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.VotingPowerAtProposalStartRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut chain_id__ = None; + let mut proposal_id__ = None; + let mut identity_key__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ChainId => { + if chain_id__.is_some() { + return Err(serde::de::Error::duplicate_field("chainId")); + } + chain_id__ = Some(map_.next_value()?); + } + GeneratedField::ProposalId => { + if proposal_id__.is_some() { + return Err(serde::de::Error::duplicate_field("proposalId")); + } + proposal_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::IdentityKey => { + if identity_key__.is_some() { + return Err(serde::de::Error::duplicate_field("identityKey")); + } + identity_key__ = map_.next_value()?; + } + } + } + Ok(VotingPowerAtProposalStartRequest { + chain_id: chain_id__.unwrap_or_default(), + proposal_id: proposal_id__.unwrap_or_default(), + identity_key: identity_key__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.VotingPowerAtProposalStartRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for VotingPowerAtProposalStartResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.voting_power != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1alpha1.VotingPowerAtProposalStartResponse", len)?; + if self.voting_power != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("votingPower", ToString::to_string(&self.voting_power).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for VotingPowerAtProposalStartResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "voting_power", + "votingPower", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + VotingPower, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "votingPower" | "voting_power" => Ok(GeneratedField::VotingPower), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = VotingPowerAtProposalStartResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.governance.v1alpha1.VotingPowerAtProposalStartResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut voting_power__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::VotingPower => { + if voting_power__.is_some() { + return Err(serde::de::Error::duplicate_field("votingPower")); + } + voting_power__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(VotingPowerAtProposalStartResponse { + voting_power: voting_power__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.governance.v1alpha1.VotingPowerAtProposalStartResponse", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for ZkDelegatorVoteProof { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index 74bb6c3aa0..d2b4484870 100644 Binary files a/crates/proto/src/gen/proto_descriptor.bin.no_lfs and b/crates/proto/src/gen/proto_descriptor.bin.no_lfs differ diff --git a/crates/proto/src/serializers/bech32str.rs b/crates/proto/src/serializers/bech32str.rs index 0f26afe279..d23081e4ae 100644 --- a/crates/proto/src/serializers/bech32str.rs +++ b/crates/proto/src/serializers/bech32str.rs @@ -121,7 +121,7 @@ pub mod address { use super::*; /// The Bech32 prefix used for addresses. - pub const BECH32_PREFIX: &str = "penumbrav2t"; + pub const BECH32_PREFIX: &str = "penumbra"; pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index dbcad625dc..d60bb5eb9a 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-storage" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/storage/src/snapshot.rs b/crates/storage/src/snapshot.rs index 12c48ab0df..e5ca370a06 100644 --- a/crates/storage/src/snapshot.rs +++ b/crates/storage/src/snapshot.rs @@ -471,7 +471,6 @@ impl TreeReader for Inner { /// Gets node given a node key. Returns `None` if the node does not exist. fn get_node_option(&self, node_key: &NodeKey) -> Result> { - let node_key = node_key; let db_node_key = DbNodeKey::from(node_key.clone()); tracing::trace!(?node_key); diff --git a/crates/test/tct-property-test/Cargo.toml b/crates/test/tct-property-test/Cargo.toml index 68c61b9226..fc2bca3f44 100644 --- a/crates/test/tct-property-test/Cargo.toml +++ b/crates/test/tct-property-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-tct-property-test" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/util/tendermint-proxy/Cargo.toml b/crates/util/tendermint-proxy/Cargo.toml index 24dde5b33c..2addca7dde 100644 --- a/crates/util/tendermint-proxy/Cargo.toml +++ b/crates/util/tendermint-proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-tendermint-proxy" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/util/tower-trace/Cargo.toml b/crates/util/tower-trace/Cargo.toml index 319d45c4f5..c542f8d279 100644 --- a/crates/util/tower-trace/Cargo.toml +++ b/crates/util/tower-trace/Cargo.toml @@ -1,7 +1,7 @@ [package] # A vendored copy of the unpublished `tracing-tower` crate. name = "penumbra-tower-trace" -version = "0.62.0" +version = "0.63.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/view/Cargo.toml b/crates/view/Cargo.toml index 2a51a0e8c4..0268daf5db 100644 --- a/crates/view/Cargo.toml +++ b/crates/view/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-view" -version = "0.62.0" +version = "0.63.1" authors = ["Penumbra Labs "] edition = "2021" description = "The view RPC library for the Penumbra Zone" diff --git a/crates/view/src/planner.rs b/crates/view/src/planner.rs index 12104795d7..33aa83d455 100644 --- a/crates/view/src/planner.rs +++ b/crates/view/src/planner.rs @@ -24,14 +24,14 @@ use penumbra_governance::{ proposal_state, DelegatorVotePlan, Proposal, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote, Vote, }; -use penumbra_ibc::{IbcAction, Ics20Withdrawal}; +use penumbra_ibc::IbcAction; use penumbra_keys::{ keys::{AddressIndex, WalletId}, Address, }; use penumbra_num::Amount; use penumbra_proto::view::v1alpha1::{NotesForVotingRequest, NotesRequest}; -use penumbra_shielded_pool::{Note, OutputPlan, SpendPlan}; +use penumbra_shielded_pool::{Ics20Withdrawal, Note, OutputPlan, SpendPlan}; use penumbra_stake::{rate::RateData, validator}; use penumbra_stake::{IdentityKey, UndelegateClaimPlan}; use penumbra_tct as tct; @@ -619,9 +619,9 @@ impl Planner { ); } - // If there are outputs, we check that a memo has been added. If not, we add a default memo. + // If there are outputs, we check that a memo has been added. If not, we add a blank memo. if self.plan.num_outputs() > 0 && self.plan.memo_plan.is_none() { - self.memo(MemoPlaintext::default()) + self.memo(MemoPlaintext::blank_memo(self_address.clone())) .expect("empty string is a valid memo"); } else if self.plan.num_outputs() == 0 && self.plan.memo_plan.is_some() { anyhow::bail!("if no outputs, no memo should be added"); diff --git a/crates/wallet/Cargo.toml b/crates/wallet/Cargo.toml index ea9282eb3c..3ee90e4909 100644 --- a/crates/wallet/Cargo.toml +++ b/crates/wallet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penumbra-wallet" -version = "0.62.0" +version = "0.63.1" authors = ["Penumbra Labs "] edition = "2021" description = "The wallet software for the Penumbra Zone" diff --git a/crates/wallet/src/key_store.rs b/crates/wallet/src/key_store.rs deleted file mode 100644 index e8e700bd4c..0000000000 --- a/crates/wallet/src/key_store.rs +++ /dev/null @@ -1,60 +0,0 @@ -use anyhow::Context; -use penumbra_keys::keys::{Bip44Path, SeedPhrase, SpendKey}; -use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, DisplayFromStr}; - -/// A wallet file storing a single spend authority. -#[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct KeyStore { - #[serde_as(as = "DisplayFromStr")] - pub spend_key: SpendKey, -} - -impl KeyStore { - /// Write the wallet data to the provided path. - pub fn save(&self, path: impl AsRef) -> anyhow::Result<()> { - if path.as_ref().exists() { - let p = path.as_ref().to_string_lossy(); - anyhow::bail!( - "Wallet file already exists, refusing to overwrite it: {}", - &p - ); - } - use std::io::Write; - let path = path.as_ref(); - let mut file = - std::fs::File::create(path).with_context(|| format!("can't create file {path:?}"))?; - let data = serde_json::to_vec(self).context("can't serialize wallet")?; - file.write_all(&data) - .with_context(|| format!("can't write file {path:?}"))?; - Ok(()) - } - - /// Read the wallet data from the provided path. - pub fn load(path: impl AsRef) -> anyhow::Result { - let path = path.as_ref(); - serde_json::from_slice( - std::fs::read(path) - .with_context(|| format!("can't read file {path:?}"))? - .as_slice(), - ) - .map_err(Into::into) - } - - /// Create a new wallet. - pub fn from_seed_phrase_bip39(seed_phrase: SeedPhrase) -> Self { - // Currently we support a single spend authority per wallet. In the future, - // we can derive multiple spend seeds from a single seed phrase. - let spend_key = SpendKey::from_seed_phrase_bip39(seed_phrase, 0); - - Self { spend_key } - } - - /// Create a new wallet using BIP44 derivation. - pub fn from_seed_phrase_bip44(seed_phrase: SeedPhrase, path: &Bip44Path) -> Self { - let spend_key = SpendKey::from_seed_phrase_bip44(seed_phrase, path); - - Self { spend_key } - } -} diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs index 81dbdb3f20..3eec640007 100644 --- a/crates/wallet/src/lib.rs +++ b/crates/wallet/src/lib.rs @@ -1,7 +1,5 @@ #![deny(clippy::unwrap_used)] mod build; -mod key_store; pub use build::build_transaction; -pub use key_store::KeyStore; pub mod plan; diff --git a/crates/wallet/src/plan.rs b/crates/wallet/src/plan.rs index 22cfcb6a53..937fa7bc43 100644 --- a/crates/wallet/src/plan.rs +++ b/crates/wallet/src/plan.rs @@ -93,7 +93,7 @@ where values, fee, dest_address, - source_address, + source_address_index, tx_memo ))] pub async fn send( @@ -103,22 +103,29 @@ pub async fn send( values: &[Value], fee: Fee, dest_address: Address, - source_address: AddressIndex, + source_address_index: AddressIndex, tx_memo: Option, ) -> anyhow::Result where V: ViewClient, R: RngCore + CryptoRng, { - tracing::debug!(?values, ?fee, ?dest_address, ?source_address, ?tx_memo); + tracing::debug!( + ?values, + ?fee, + ?dest_address, + ?source_address_index, + ?tx_memo + ); let mut planner = Planner::new(rng); planner.fee(fee); for value in values.iter().cloned() { planner.output(value, dest_address); } + let source_address = view.address_by_index(source_address_index).await?; planner - .memo(tx_memo.unwrap_or_default())? - .plan(view, wallet_id, source_address) + .memo(tx_memo.unwrap_or_else(|| MemoPlaintext::blank_memo(source_address)))? + .plan(view, wallet_id, source_address_index) .await .context("can't build send transaction") } @@ -239,7 +246,8 @@ where // chunks, ignoring the biggest notes in the remainder. for group in records.chunks_exact(SWEEP_COUNT) { let mut planner = Planner::new(&mut rng); - planner.memo(MemoPlaintext::default())?; + let sender_addr = view.address_by_index(index).await?; + planner.memo(MemoPlaintext::blank_memo(sender_addr))?; for record in group { planner.spend(record.note.clone(), record.position); diff --git a/crates/wasm/src/planner.rs b/crates/wasm/src/planner.rs index 5b4e7d33f6..30f6a46fde 100644 --- a/crates/wasm/src/planner.rs +++ b/crates/wasm/src/planner.rs @@ -24,11 +24,11 @@ use penumbra_governance::{ proposal_state::Outcome as ProposalOutcome, DelegatorVotePlan, Proposal, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote, Vote, }; -use penumbra_ibc::{IbcAction, Ics20Withdrawal}; +use penumbra_ibc::IbcAction; use penumbra_keys::Address; use penumbra_num::Amount; use penumbra_proto::view::v1alpha1::{NotesForVotingRequest, NotesRequest}; -use penumbra_shielded_pool::{Note, OutputPlan, SpendPlan}; +use penumbra_shielded_pool::{Ics20Withdrawal, Note, OutputPlan, SpendPlan}; use penumbra_stake::{rate::RateData, validator}; use penumbra_stake::{IdentityKey, UndelegateClaimPlan}; use penumbra_tct as tct; @@ -526,9 +526,9 @@ impl Planner { ); } - // If there are outputs, we check that a memo has been added. If not, we add a default memo. + // If there are outputs, we check that a memo has been added. If not, we add a blank memo. if self.plan.num_outputs() > 0 && self.plan.memo_plan.is_none() { - self.memo(MemoPlaintext::default()) + self.memo(MemoPlaintext::blank_memo(self_address.clone())) .expect("empty string is a valid memo"); } else if self.plan.num_outputs() == 0 && self.plan.memo_plan.is_some() { anyhow::bail!("if no outputs, no memo should be added"); diff --git a/deployments/charts/penumbra-network/files/validators.json b/deployments/charts/penumbra-network/files/validators.json index ed94983fb8..f740f0f63e 100644 --- a/deployments/charts/penumbra-network/files/validators.json +++ b/deployments/charts/penumbra-network/files/validators.json @@ -6,27 +6,27 @@ "funding_streams": [ [ 50, - "penumbrav2t1fcy6crf6u4r450k8y4nye43puxet2ytfh7s0dzxsxjk68czej9mp37xv49np0clv4dc8cwg4re0xfs79uwlfehnja4p0revmlek0drezxfse8spg3qc6gux6vyuzuuls6v6mmr" + "penumbra1fcy6crf6u4r450k8y4nye43puxet2ytfh7s0dzxsxjk68czej9mp37xv49np0clv4dc8cwg4re0xfs79uwlfehnja4p0revmlek0drezxfse8spg3qc6gux6vyuzuulse7xuxv" ], [ 50, - "penumbrav2t13ahs2s8ms6q0utgetty3zflwteepg87gqm88sqqcdj2mjhhydkykwu6n7dk557x84aa9a6cqhdytw0zk33xjgmuedprrlunc86up6zps8juej9rpuuydjtk7jaxpmrw2a64mcf" + "penumbra13ahs2s8ms6q0utgetty3zflwteepg87gqm88sqqcdj2mjhhydkykwu6n7dk557x84aa9a6cqhdytw0zk33xjgmuedprrlunc86up6zps8juej9rpuuydjtk7jaxpmrw27gfu9x" ], [ 50, - "penumbrav2t1uw03wyt49u7wm5wgu4nvkdt0v48fdaw5y4az4xlgmnp6ucs6th4xd0zg8wqxwndwfv286ktjwgemyhrxqu0d5qjf8dapr57l3k8yqs09vw9m5ywxsx9hjj2dj4qwnrl2qs6222" + "penumbra1uw03wyt49u7wm5wgu4nvkdt0v48fdaw5y4az4xlgmnp6ucs6th4xd0zg8wqxwndwfv286ktjwgemyhrxqu0d5qjf8dapr57l3k8yqs09vw9m5ywxsx9hjj2dj4qwnrl2rzxdh9" ], [ 50, - "penumbrav2t1w6em8sdx0467ug9kk0s0sng254tqjfk9gglv6ff7dq2v8arwekevkjte9udzmsj9l83mz74747tj0a49w2vhecxj7ac4upr5c5pvjqhsy7dwn422m8dgdekt7y4lmad0fg04dr" + "penumbra1w6em8sdx0467ug9kk0s0sng254tqjfk9gglv6ff7dq2v8arwekevkjte9udzmsj9l83mz74747tj0a49w2vhecxj7ac4upr5c5pvjqhsy7dwn422m8dgdekt7y4lmad026njsv" ], [ 50, - "penumbrav2t1jp4pryqqmh65pq8e7zwk6k2674vwhn4qqphxjk0vukxln0crmp2tdld0mhavuyrspwuajnsk5t5t33u2auxvheunr7qde4l068ez0euvtu08z7rwj6shlh64ndz0wvz7mfqdcd" + "penumbra1jp4pryqqmh65pq8e7zwk6k2674vwhn4qqphxjk0vukxln0crmp2tdld0mhavuyrspwuajnsk5t5t33u2auxvheunr7qde4l068ez0euvtu08z7rwj6shlh64ndz0wvz7cmu29z" ], [ 50, - "penumbrav2t1hum845ches70c8kp8zfx7nerjwfe653hxsrpgwepwtspcp4jy6ytnxhe5kwn56sku684x6zzqcwp5ycrkee5mmg9kdl3jkr5lqn2xq3kqxvp4d7gwqdue5jznk2ter2t66mk4n" + "penumbra1hum845ches70c8kp8zfx7nerjwfe653hxsrpgwepwtspcp4jy6ytnxhe5kwn56sku684x6zzqcwp5ycrkee5mmg9kdl3jkr5lqn2xq3kqxvp4d7gwqdue5jznk2ter2teg83gu" ] ], "sequence_number": 0 @@ -38,27 +38,27 @@ "funding_streams": [ [ 50, - "penumbrav2t1fcy6crf6u4r450k8y4nye43puxet2ytfh7s0dzxsxjk68czej9mp37xv49np0clv4dc8cwg4re0xfs79uwlfehnja4p0revmlek0drezxfse8spg3qc6gux6vyuzuuls6v6mmr" + "penumbra1fcy6crf6u4r450k8y4nye43puxet2ytfh7s0dzxsxjk68czej9mp37xv49np0clv4dc8cwg4re0xfs79uwlfehnja4p0revmlek0drezxfse8spg3qc6gux6vyuzuulse7xuxv" ], [ 50, - "penumbrav2t13ahs2s8ms6q0utgetty3zflwteepg87gqm88sqqcdj2mjhhydkykwu6n7dk557x84aa9a6cqhdytw0zk33xjgmuedprrlunc86up6zps8juej9rpuuydjtk7jaxpmrw2a64mcf" + "penumbra13ahs2s8ms6q0utgetty3zflwteepg87gqm88sqqcdj2mjhhydkykwu6n7dk557x84aa9a6cqhdytw0zk33xjgmuedprrlunc86up6zps8juej9rpuuydjtk7jaxpmrw27gfu9x" ], [ 50, - "penumbrav2t1uw03wyt49u7wm5wgu4nvkdt0v48fdaw5y4az4xlgmnp6ucs6th4xd0zg8wqxwndwfv286ktjwgemyhrxqu0d5qjf8dapr57l3k8yqs09vw9m5ywxsx9hjj2dj4qwnrl2qs6222" + "penumbra1uw03wyt49u7wm5wgu4nvkdt0v48fdaw5y4az4xlgmnp6ucs6th4xd0zg8wqxwndwfv286ktjwgemyhrxqu0d5qjf8dapr57l3k8yqs09vw9m5ywxsx9hjj2dj4qwnrl2rzxdh9" ], [ 50, - "penumbrav2t1w6em8sdx0467ug9kk0s0sng254tqjfk9gglv6ff7dq2v8arwekevkjte9udzmsj9l83mz74747tj0a49w2vhecxj7ac4upr5c5pvjqhsy7dwn422m8dgdekt7y4lmad0fg04dr" + "penumbra1w6em8sdx0467ug9kk0s0sng254tqjfk9gglv6ff7dq2v8arwekevkjte9udzmsj9l83mz74747tj0a49w2vhecxj7ac4upr5c5pvjqhsy7dwn422m8dgdekt7y4lmad026njsv" ], [ 50, - "penumbrav2t1jp4pryqqmh65pq8e7zwk6k2674vwhn4qqphxjk0vukxln0crmp2tdld0mhavuyrspwuajnsk5t5t33u2auxvheunr7qde4l068ez0euvtu08z7rwj6shlh64ndz0wvz7mfqdcd" + "penumbra1jp4pryqqmh65pq8e7zwk6k2674vwhn4qqphxjk0vukxln0crmp2tdld0mhavuyrspwuajnsk5t5t33u2auxvheunr7qde4l068ez0euvtu08z7rwj6shlh64ndz0wvz7cmu29z" ], [ 50, - "penumbrav2t1hum845ches70c8kp8zfx7nerjwfe653hxsrpgwepwtspcp4jy6ytnxhe5kwn56sku684x6zzqcwp5ycrkee5mmg9kdl3jkr5lqn2xq3kqxvp4d7gwqdue5jznk2ter2t66mk4n" + "penumbra1hum845ches70c8kp8zfx7nerjwfe653hxsrpgwepwtspcp4jy6ytnxhe5kwn56sku684x6zzqcwp5ycrkee5mmg9kdl3jkr5lqn2xq3kqxvp4d7gwqdue5jznk2ter2teg83gu" ] ], "sequence_number": 0 diff --git a/deployments/charts/penumbra-network/templates/deployment.yaml b/deployments/charts/penumbra-network/templates/deployment.yaml index 9155b34fe4..5ba9fb961f 100644 --- a/deployments/charts/penumbra-network/templates/deployment.yaml +++ b/deployments/charts/penumbra-network/templates/deployment.yaml @@ -12,6 +12,7 @@ metadata: labels: app: {{ $val_name }} app.kubernetes.io/component: genesis-validator + app.kubernetes.io/part-of: {{ include "penumbra-network.part_of" $ }} {{- include "penumbra-network.labels" $ | nindent 4 }} spec: replicas: 1 @@ -81,6 +82,8 @@ spec: - "0.0.0.0:9000" - --home - "/penumbra-config/{{ $val_name }}/node{{ $i }}/pd" + env: + {{- toYaml $.Values.containerEnv | nindent 12 }} ports: - name: pd-grpc containerPort: 8080 diff --git a/deployments/charts/penumbra-network/templates/job-generate.yaml b/deployments/charts/penumbra-network/templates/job-generate.yaml index c65678f683..bd43204408 100644 --- a/deployments/charts/penumbra-network/templates/job-generate.yaml +++ b/deployments/charts/penumbra-network/templates/job-generate.yaml @@ -59,6 +59,8 @@ spec: allowPrivilegeEscalation: true image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- toYaml $.Values.containerEnv | nindent 12 }} command: - sh - -c diff --git a/deployments/charts/penumbra-network/values.yaml b/deployments/charts/penumbra-network/values.yaml index 661fd781f3..9f39c787a4 100644 --- a/deployments/charts/penumbra-network/values.yaml +++ b/deployments/charts/penumbra-network/values.yaml @@ -51,6 +51,11 @@ containerArgs: # store state in emptyDir for now - /penumbra-config/testnet_data/node0/pd +# Environment variables for pd containers. +containerEnv: + - name: RUST_LOG + value: info,pd=debug,penumbra=debug + imagePullSecrets: [] nameOverride: "" fullnameOverride: "" diff --git a/deployments/charts/penumbra-node/files/postgres-cometbft-schema.sql b/deployments/charts/penumbra-node/files/postgres-cometbft-schema.sql new file mode 100644 index 0000000000..ce5a241bad --- /dev/null +++ b/deployments/charts/penumbra-node/files/postgres-cometbft-schema.sql @@ -0,0 +1,85 @@ +/* + This file defines the database schema for the PostgresQL ("psql") event sink + implementation in CometBFT. The operator must create a database and install + this schema before using the database to index events. + */ + +-- The blocks table records metadata about each block. +-- The block record does not include its events or transactions (see tx_results). +CREATE TABLE blocks ( + rowid BIGSERIAL PRIMARY KEY, + + height BIGINT NOT NULL, + chain_id VARCHAR NOT NULL, + + -- When this block header was logged into the sink, in UTC. + created_at TIMESTAMPTZ NOT NULL, + + UNIQUE (height, chain_id) +); + +-- Index blocks by height and chain, since we need to resolve block IDs when +-- indexing transaction records and transaction events. +CREATE INDEX idx_blocks_height_chain ON blocks(height, chain_id); + +-- The tx_results table records metadata about transaction results. Note that +-- the events from a transaction are stored separately. +CREATE TABLE tx_results ( + rowid BIGSERIAL PRIMARY KEY, + + -- The block to which this transaction belongs. + block_id BIGINT NOT NULL REFERENCES blocks(rowid), + -- The sequential index of the transaction within the block. + index INTEGER NOT NULL, + -- When this result record was logged into the sink, in UTC. + created_at TIMESTAMPTZ NOT NULL, + -- The hex-encoded hash of the transaction. + tx_hash VARCHAR NOT NULL, + -- The protobuf wire encoding of the TxResult message. + tx_result BYTEA NOT NULL, + + UNIQUE (block_id, index) +); + +-- The events table records events. All events (both block and transaction) are +-- associated with a block ID; transaction events also have a transaction ID. +CREATE TABLE events ( + rowid BIGSERIAL PRIMARY KEY, + + -- The block and transaction this event belongs to. + -- If tx_id is NULL, this is a block event. + block_id BIGINT NOT NULL REFERENCES blocks(rowid), + tx_id BIGINT NULL REFERENCES tx_results(rowid), + + -- The application-defined type label for the event. + type VARCHAR NOT NULL +); + +-- The attributes table records event attributes. +CREATE TABLE attributes ( + event_id BIGINT NOT NULL REFERENCES events(rowid), + key VARCHAR NOT NULL, -- bare key + composite_key VARCHAR NOT NULL, -- composed type.key + value VARCHAR NULL, + + UNIQUE (event_id, key) +); + +-- A joined view of events and their attributes. Events that do not have any +-- attributes are represented as a single row with empty key and value fields. +CREATE VIEW event_attributes AS + SELECT block_id, tx_id, type, key, composite_key, value + FROM events LEFT JOIN attributes ON (events.rowid = attributes.event_id); + +-- A joined view of all block events (those having tx_id NULL). +CREATE VIEW block_events AS + SELECT blocks.rowid as block_id, height, chain_id, type, key, composite_key, value + FROM blocks JOIN event_attributes ON (blocks.rowid = event_attributes.block_id) + WHERE event_attributes.tx_id IS NULL; + +-- A joined view of all transaction events. +CREATE VIEW tx_events AS + SELECT height, index, chain_id, type, key, composite_key, value, tx_results.created_at + FROM blocks JOIN tx_results ON (blocks.rowid = tx_results.block_id) + JOIN event_attributes ON (tx_results.rowid = event_attributes.tx_id) + WHERE event_attributes.tx_id IS NOT NULL; diff --git a/deployments/charts/penumbra-node/templates/configmap-postgresql-cometbft-schema.yaml b/deployments/charts/penumbra-node/templates/configmap-postgresql-cometbft-schema.yaml new file mode 100644 index 0000000000..3e57537d1b --- /dev/null +++ b/deployments/charts/penumbra-node/templates/configmap-postgresql-cometbft-schema.yaml @@ -0,0 +1,13 @@ +--- +{{- /* +The database schema file for CometBFT is required for event indexing via postgres. +See more info at "https://docs.cometbft.com/v0.37/app-dev/indexing-transactions#postgresql". +*/}} +{{- if eq .Values.cometbft.config.indexer "psql" }} +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "penumbra-node.fullname" . }}-postgres-schema +data: + postgres-cometbft-schema.sql: {{ .Files.Get (printf "files/postgres-cometbft-schema.sql") | quote }} +{{ end }} diff --git a/deployments/charts/penumbra-node/templates/deployment.yaml b/deployments/charts/penumbra-node/templates/deployment.yaml index 8a49464718..bf9ffd4108 100644 --- a/deployments/charts/penumbra-node/templates/deployment.yaml +++ b/deployments/charts/penumbra-node/templates/deployment.yaml @@ -10,6 +10,10 @@ metadata: name: {{ $fn_name }} labels: {{- include "penumbra-node.labels" $ | nindent 4 }} + "app.kubernetes.io/component": fullnode + {{- if $.Values.part_of }} + "app.kubernetes.io/part-of": {{ $.Values.part_of }} + {{- end }} spec: replicas: 1 strategy: @@ -67,6 +71,23 @@ spec: {{- else }} emptyDir: {} {{- end }} + {{- if eq $.Values.cometbft.config.indexer "psql" }} + {{- if $.Values.persistence.enabled }} + - name: db + persistentVolumeClaim: + claimName: {{ $fn_name }}-db + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + {{- if eq $.Values.cometbft.config.indexer "psql" }} + - name: postgres-schema + configMap: + name: {{ include "penumbra-node.fullname" $ }}-postgres-schema + items: + - key: "postgres-cometbft-schema.sql" + path: "postgres-cometbft-schema.sql" + {{ end }} initContainers: - name: {{ $.Chart.Name }}-init securityContext: @@ -99,6 +120,10 @@ spec: --moniker {{ . | quote }} \ {{- end }} {{ $.Values.penumbra_bootstrap_node_cometbft_rpc_url }} + + {{ if eq $.Values.cometbft.config.indexer "psql" -}} + sed -i -e 's#^indexer.*#indexer = "psql"\npsql-conn = "{{ $.Values.cometbft.config.postgres_connection_url }}"#' /penumbra-config/testnet_data/node0/cometbft/config/config.toml + {{- end }} fi # set ownership for pd user @@ -145,6 +170,7 @@ spec: - 0.0.0.0:9000 - --home - /penumbra-config/testnet_data/node0/pd + - --enable-expensive-rpc ports: - name: pd-grpc containerPort: 8080 @@ -193,6 +219,39 @@ spec: - name: config mountPath: /cometbft subPath: testnet_data/node0/cometbft + {{- if eq $.Values.cometbft.config.indexer "psql" }} + - name: postgres + securityContext: + {{- toYaml $.Values.postgres.securityContext | nindent 12 }} + image: "{{ $.Values.postgres.image.repository }}:{{ $.Values.postgres.image.tag }}" + imagePullPolicy: {{ $.Values.postgres.image.pullPolicy }} + ports: + - name: postgres + containerPort: 5432 + protocol: TCP + # Lazy to hardcode these values, but the db connection is intra-cluster. + env: + - name: POSTGRES_DB + value: penumbra + - name: POSTGRES_USER + value: penumbra + - name: POSTGRES_PASSWORD + value: penumbra + readinessProbe: + tcpSocket: + port: 5432 + timeoutSeconds: 10 + initialDelaySeconds: 10 + resources: + {{- toYaml $.Values.postgres.resources | nindent 12 }} + volumeMounts: + - name: postgres-schema + mountPath: /docker-entrypoint-initdb.d + readOnly: true + - name: db + mountPath: /var/lib/postgresql + {{ end }} + {{- with $.Values.nodeSelector }} nodeSelector: {{- toYaml $ | nindent 8 }} diff --git a/deployments/charts/penumbra-node/templates/ingressroute.yaml b/deployments/charts/penumbra-node/templates/ingressroute.yaml index 7afd05b17c..35c9c8faf6 100644 --- a/deployments/charts/penumbra-node/templates/ingressroute.yaml +++ b/deployments/charts/penumbra-node/templates/ingressroute.yaml @@ -14,6 +14,10 @@ spec: routes: - kind: Rule match: Host(`{{ .Values.ingressRoute.hosts.pd }}`) + {{- with .Values.ingressRoute.middlewares }} + middlewares: + {{- toYaml . | nindent 6 }} + {{- end }} services: {{- /* Skip nodes with seed_mode=true when looping over nodes, to exclude from LB RPCs. diff --git a/deployments/charts/penumbra-node/templates/pvc.yml b/deployments/charts/penumbra-node/templates/pvc.yml index 91f472fb56..821dc96348 100644 --- a/deployments/charts/penumbra-node/templates/pvc.yml +++ b/deployments/charts/penumbra-node/templates/pvc.yml @@ -2,12 +2,11 @@ {{ $count := (.Values.nodes | len | int) }} {{ range $i,$e := until $count }} {{ $fn_name := printf "%s-fn-%d" $.Release.Name $i }} -{{ $pvc_name := printf "%s-config" $fn_name }} --- apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ $pvc_name }} + name: {{ $fn_name }}-config labels: {{- include "penumbra-node.labels" $ | nindent 4 }} spec: @@ -18,5 +17,26 @@ spec: {{- if $.Values.persistence.storageClassName }} storageClassName: {{ $.Values.persistence.storageClassName }} {{- end }} +--- +{{- if eq $.Values.cometbft.config.indexer "psql" }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ $fn_name }}-db + labels: + {{- include "penumbra-node.labels" $ | nindent 4 }} +spec: + accessModes: {{ $.Values.persistence.accessModes }} + resources: + requests: + # Storage for db is <1% of storage for node state. + # We could dynamically compute that based on persistence size, + # but it's also fine if we just hardcode the value for now. + storage: 2G + {{- if $.Values.persistence.storageClassName }} + storageClassName: {{ $.Values.persistence.storageClassName }} + {{- end }} +{{- end }} + {{- end }} {{- end }} diff --git a/deployments/charts/penumbra-node/templates/service.yaml b/deployments/charts/penumbra-node/templates/service.yaml index 5814e9fe98..55ea44495f 100644 --- a/deployments/charts/penumbra-node/templates/service.yaml +++ b/deployments/charts/penumbra-node/templates/service.yaml @@ -29,6 +29,12 @@ spec: port: 26660 targetPort: 26660 protocol: TCP + {{- if eq $.Values.cometbft.config.indexer "psql" }} + - name: postgres + port: 5432 + targetPort: 5432 + protocol: TCP + {{- end }} selector: app: {{ $fn_name }} {{- include "penumbra-node.selectorLabels" $ | nindent 4 }} diff --git a/deployments/charts/penumbra-node/values.yaml b/deployments/charts/penumbra-node/values.yaml index cb6ac56039..2945ef98e2 100644 --- a/deployments/charts/penumbra-node/values.yaml +++ b/deployments/charts/penumbra-node/values.yaml @@ -44,6 +44,17 @@ cometbft: p2p: max_num_inbound_peers: 300 max_num_outbound_peers: 200 + # Set the indexer strategy. Can be "kv" or "psql". + indexer: kv + # URL for connecting to the postgresql database. Only used if `indexer=psql`. + postgres_connection_url: "postgresql://penumbra:penumbra@localhost:5432/penumbra?sslmode=disable" + +# settings for optional postgres sidecar, used for indexing cometbft events +postgres: + image: + repository: docker.io/library/postgres + pullPolicy: IfNotPresent + tag: "latest" # Configure nodes. By default, only one is created. # Extend this list to add more. Valid node attributes are: @@ -73,6 +84,10 @@ ingressRoute: tm: tm.chart-example.local # Secret object containing TLS info secretName: "" + # Traefik middleware CRDs, to be applied to pd's gRPC service. + # These config objects must already exist in the API, i.e. create them out of band. + middlewares: + - name: cors-allow-all imagePullSecrets: [] nameOverride: "" diff --git a/deployments/ci.sh b/deployments/ci.sh index 7c059716a6..e1d584f415 100755 --- a/deployments/ci.sh +++ b/deployments/ci.sh @@ -49,8 +49,7 @@ function helm_uninstall() { # as necessary. Will *not* replace certain durable resources like # the LoadBalancer Service objects, which are annotated with helm.sh/resource-policy=keep. function helm_install() { - # TODO: make sure helmfile is present in ci environemnt. - helmfile sync -f "$HELMFILE_MANIFEST" --args \ + helmfile apply -f "$HELMFILE_MANIFEST" --args \ --set="image.tag=${PENUMBRA_VERSION}" } diff --git a/deployments/containerfiles/Dockerfile b/deployments/containerfiles/Dockerfile index 892f503be2..491c0fbcae 100644 --- a/deployments/containerfiles/Dockerfile +++ b/deployments/containerfiles/Dockerfile @@ -1,4 +1,5 @@ -ARG RUST_VERSION=1.71.1 +# N.B. the RUST_VERSION should match MSRV in crates/bin/pd/Cargo.toml +ARG RUST_VERSION=1.73.0 FROM docker.io/rust:${RUST_VERSION}-slim-bookworm AS build-env # Install build dependencies. These packages should match what's recommended on diff --git a/deployments/containerfiles/Dockerfile-grafana b/deployments/containerfiles/Dockerfile-grafana index 12c0878bdc..09dca43e20 100644 --- a/deployments/containerfiles/Dockerfile-grafana +++ b/deployments/containerfiles/Dockerfile-grafana @@ -1,4 +1,5 @@ -FROM docker.io/grafana/grafana:latest +ARG GRAFANA_VERSION="10.1.5" +FROM docker.io/grafana/grafana:${GRAFANA_VERSION} COPY deployments/config/grafana/provisioning /etc/grafana/provisioning COPY deployments/config/grafana/grafana.ini /etc/grafana/grafana.ini COPY deployments/config/grafana/dashboards /var/lib/grafana/dashboards diff --git a/deployments/helmfile.d/penumbra-preview.yaml b/deployments/helmfile.d/penumbra-preview.yaml index fd2c042902..1d61d79f94 100644 --- a/deployments/helmfile.d/penumbra-preview.yaml +++ b/deployments/helmfile.d/penumbra-preview.yaml @@ -62,3 +62,26 @@ releases: - persistence: enabled: true size: 10G + + - name: penumbra-preview-cuiloa-node + chart: ../charts/penumbra-node + needs: + - penumbra-preview + # It's not strictly necessary to wait for node deploys, but doing so allows us to exercise + # the public HTTPS RPC endpoint for joining, which is nice. + - penumbra-preview-nodes + values: + - penumbra_bootstrap_node_cometbft_rpc_url: "https://rpc.testnet-preview.penumbra.zone" + - ingressRoute: + enabled: false + - image: + tag: main + - persistence: + enabled: true + size: 50G + - cometbft: + config: + indexer: psql + - part_of: penumbra-preview + - nodes: + - moniker: cuiloa diff --git a/deployments/relayer/penumbra.tpl b/deployments/relayer/penumbra.tpl index 7a61b8c1c3..c42691a8b5 100644 --- a/deployments/relayer/penumbra.tpl +++ b/deployments/relayer/penumbra.tpl @@ -4,7 +4,7 @@ "key": "default", "chain-id": "$PENUMBRA_CHAIN_ID", "rpc-addr": "$PENUMBRA_RPC_URL", - "account-prefix": "penumbrav2t", + "account-prefix": "penumbra", "keyring-backend": "test", "gas-adjustment": 1.0, "gas-prices": "0.00upenumbra", diff --git a/deployments/scripts/gha-repository-dispatch b/deployments/scripts/gha-repository-dispatch index 9fcad01e29..93c7caa96a 100755 --- a/deployments/scripts/gha-repository-dispatch +++ b/deployments/scripts/gha-repository-dispatch @@ -2,7 +2,10 @@ # Utility script to trigger GitHub Action workflows across different repositories [0]. # Requires a GitHub Personal Access Token (PAT), exported as GITHUB_PAT env var [1]. # -# [0] https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository_dispatch +# Uses event `workflow_dispatch`, rather than `repository_dispatch`, because the latter +# does not support passing `inputs`, which we need to set a specific version of Penumbra. +# +# [0] https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch # [1] https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens set -euo pipefail @@ -19,11 +22,15 @@ elif [[ -z "$github_pat" ]] ; then exit 1 fi +# The workflow id is the filename containing the job YAML. +# https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow +workflow_id="container.yml" + # Support overriding the upstream version of Penumbra, but default to 'main'. penumbra_version="${PENUMBRA_VERSION:-main}" -# Build URL for repository dispatch API endpoint. -github_repository_url="https://api.github.com/repos/${github_repo}/dispatches" +# Build URL for workflow dispatch API endpoint. +github_workflow_url="https://api.github.com/repos/${github_repo}/actions/workflows/${workflow_id}/dispatches" # Accept arguments for workflow, and emit valid JSON for curl request. # Using printf allows us to interpolate bash variables in JSON, @@ -32,12 +39,15 @@ function format_json_payload() { local v v="${1:-}" shift - printf '{"event_type": "container-build", "client_payload": { "penumbra_version": "%s" }}' "$v" + # N.B. the "ref" value here is the gitref on the remote repo, not the calling repo. + printf '{"ref": "main", "inputs": { "penumbra_version": "%s" }}' "$v" } json_payload="$(format_json_payload "$penumbra_version")" -curl -f -X POST "$github_repository_url" \ - -H 'Accept: application/vnd.github.v3+json' \ +>&2 printf 'Sending JSON blob:\n%s\nto URL: %s\n' "$json_payload" "$github_workflow_url" +curl -f -L -X POST \ + -H 'Accept: application/vnd.github+json' \ -H 'Content-Type: application/json' \ -H "Authorization: token $github_pat" \ + "$github_workflow_url" \ --data "$json_payload" diff --git a/deployments/scripts/smoke-summoner.sh b/deployments/scripts/smoke-summoner.sh index 201e1104f9..e64d19e679 100755 --- a/deployments/scripts/smoke-summoner.sh +++ b/deployments/scripts/smoke-summoner.sh @@ -20,7 +20,7 @@ if ! hash cometbft > /dev/null 2>&1 ; then exit 1 fi -export RUST_LOG="summonerd=info,pcli=info,pd=info,penumbra=info" +export RUST_LOG="summonerd=info,pd=info,penumbra=info" # Duration that the network will run before we start the ceremony. TESTNET_BOOTTIME="${TESTNET_BOOTTIME:-20}" @@ -57,13 +57,14 @@ cargo run --quiet --release --bin summonerd -- generate-phase1 --output phase1.b echo "Setting up storage directory..." mkdir /tmp/summonerd -cargo run --quiet --release --bin pcli -- --home /tmp/summonerd --node http://127.0.0.1:8080 keys generate -export SUMMONER_ADDRESS=$(PCLI_UNLEASH_DANGER="yes" cargo run --quiet --release --bin pcli -- --home /tmp/summonerd --node http://127.0.0.1:8080 view address 0 2>&1) -export SUMMONER_FVK=$(PCLI_UNLEASH_DANGER="yes" cargo run --quiet --release --bin pcli -- --home /tmp/summonerd --node http://127.0.0.1:8080 keys export full-viewing-key 2>&1) +cargo run --quiet --release --bin pcli -- --home /tmp/summonerd init --grpc-url http://127.0.0.1:8080 soft-kms generate +export SUMMONER_ADDRESS=$(PCLI_UNLEASH_DANGER="yes" cargo run --quiet --release --bin pcli -- --home /tmp/summonerd view address 0 2>&1) +echo $SUMMONER_ADDRESS +export SUMMONER_FVK=$(grep "full_viewing_key" /tmp/summonerd/config.toml | cut -d= -f2 | tr -d ' "') cargo run --quiet --release --bin summonerd -- init --storage-dir /tmp/summonerd --phase1-root phase1.bin echo "Starting phase 1 run..." -cargo run --quiet --release --bin summonerd -- start --phase 1 --storage-dir /tmp/summonerd --fvk $SUMMONER_FVK --node http://127.0.0.1:8080 & +cargo run --quiet --release --bin summonerd -- start --phase 1 --storage-dir /tmp/summonerd --fvk $SUMMONER_FVK --node http://127.0.0.1:8080 --bind-addr 127.0.0.1:8082 & phase1_pid="$!" # If script ends early, ensure phase 1 is halted. trap 'kill -9 "$phase1_pid"' EXIT @@ -73,11 +74,12 @@ echo "Setting up test accounts..." # the home directory, and so if there is already a backup wallet, we refuse to overwrite it, # and will exit non-zero. We don't care about the backup wallet for this test, so we ignore the # exit code. -echo $SEED_PHRASE | cargo run --quiet --release --bin pcli -- --home /tmp/account1 keys import phrase || true -export ACCOUNT1_ADDRESS=$(PCLI_UNLEASH_DANGER="yes" cargo run --quiet --release --bin pcli -- --home /tmp/account1 --node http://127.0.0.1:8080 view address 0 2>&1) +echo $SEED_PHRASE | cargo run --quiet --release --bin pcli -- --home /tmp/account1 init --grpc-url http://127.0.0.1:8080 soft-kms import-phrase || true +export ACCOUNT1_ADDRESS=$(PCLI_UNLEASH_DANGER="yes" cargo run --quiet --release --bin pcli -- --home /tmp/account1 view address 0 2>&1) +echo $ACCOUNT1_ADDRESS echo "Phase 1 contributions..." -cargo run --quiet --release --bin pcli -- --node http://127.0.0.1:8080 --home /tmp/account1 ceremony contribute --coordinator-url http://127.0.0.1:8081 --coordinator-address $SUMMONER_ADDRESS --phase 1 --bid 10penumbra +cargo run --quiet --release --bin pcli -- --home /tmp/account1 ceremony contribute --coordinator-url http://127.0.0.1:8082 --coordinator-address $SUMMONER_ADDRESS --phase 1 --bid 10penumbra echo "Stopping phase 1 run..." if ! kill -0 "$phase1_pid" ; then @@ -92,13 +94,13 @@ echo "Transitioning..." cargo run --quiet --release --bin summonerd -- transition --storage-dir /tmp/summonerd echo "Starting phase 2 run..." -cargo run --quiet --release --bin summonerd -- start --phase 2 --storage-dir /tmp/summonerd --fvk $SUMMONER_FVK --node http://127.0.0.1:8080 & +cargo run --quiet --release --bin summonerd -- start --phase 2 --storage-dir /tmp/summonerd --fvk $SUMMONER_FVK --node http://127.0.0.1:8080 --bind-addr 127.0.0.1:8082 & phase2_pid="$!" # If script ends early, ensure phase 2 is halted. trap 'kill -9 "$phase2_pid"' EXIT echo "Phase 2 contributions..." -cargo run --quiet --release --bin pcli -- --node http://127.0.0.1:8080 --home /tmp/account1 ceremony contribute --coordinator-url http://127.0.0.1:8081 --coordinator-address $SUMMONER_ADDRESS --phase 2 --bid 10penumbra +cargo run --quiet --release --bin pcli -- --home /tmp/account1 ceremony contribute --coordinator-url http://127.0.0.1:8082 --coordinator-address $SUMMONER_ADDRESS --phase 2 --bid 10penumbra echo "Exporting keys..." cargo run --quiet --release --bin summonerd -- export --storage-dir /tmp/summonerd --target-dir ./crates/crypto/proof-params/src/gen diff --git a/docs/guide/src/pcli.md b/docs/guide/src/pcli.md index c247020c52..3c6ccc2df2 100644 --- a/docs/guide/src/pcli.md +++ b/docs/guide/src/pcli.md @@ -10,10 +10,7 @@ This section describes how to use `pcli`, the command line client for Penumbra: Penumbra is a private blockchain, so the public chain state does not reveal any private user data. By default, `pcli` includes a _view service_ that -synchronizes with the chain and scans with a viewing key. However, it's also -possible to run the view service as a standalone `pclientd` daemon: - -- [Using `pcli` with `pclientd`](./pcli/pclientd.md) describes how to use `pcli` with `pclientd`. +synchronizes with the chain and scans with a viewing key. ### Please submit any feedback and bug reports diff --git a/docs/guide/src/pcli/install.md b/docs/guide/src/pcli/install.md index 06baf48fd5..a9142b8d56 100644 --- a/docs/guide/src/pcli/install.md +++ b/docs/guide/src/pcli/install.md @@ -2,11 +2,13 @@ ### Installing the Rust toolchain -This requires that you install a recent stable version +This requires that you install a recent (>= 1.73) stable version of the Rust compiler, installation instructions for which you can find [here](https://www.rust-lang.org/learn/get-started). Don't forget to reload your shell so that `cargo` is available in your `\$PATH`! +You can verify the rust compiler version by running `rustc --version` which should indicate version 1.73 or later. + `pcli` requires `rustfmt` as part of the build process --- depending on your OS/install method for Rust, you may have to install that separately. @@ -60,7 +62,7 @@ latest tag for the current [testnet](https://github.com/penumbra-zone/penumbra/releases): ```bash -cd penumbra && git fetch && git checkout v0.62.0 +cd penumbra && git fetch && git checkout v0.63.1 ``` ### Building the `pcli` client software diff --git a/docs/guide/src/pcli/pclientd.md b/docs/guide/src/pcli/pclientd.md deleted file mode 100644 index b13bebf2dc..0000000000 --- a/docs/guide/src/pcli/pclientd.md +++ /dev/null @@ -1,30 +0,0 @@ -# Using `pcli` with `pclientd` - -First, export a viewing key from `pcli`: - -```shell -pcli keys export full-viewing-key -``` - -Next, use the FVK it prints to initialize the `pclientd` state: - -```shell -pclientd --home /path/to/state/dir init FVK_STRING -``` - -Finally, run - -```shell -pclientd --home /path/to/state/dir start -``` - -to start the view server, and invoke `pcli` with - -```shell -pcli -v 127.0.0.1:8081 q validator list -``` - -to use it instead of an in-process view service. - -**WARNING: the view service does not currently use transport encryption, so it should -not be used over a public network.** diff --git a/docs/guide/src/pcli/transaction.md b/docs/guide/src/pcli/transaction.md index ee95b9c90c..185a2b0dff 100644 --- a/docs/guide/src/pcli/transaction.md +++ b/docs/guide/src/pcli/transaction.md @@ -180,7 +180,7 @@ has been configured between the Osmosis testnet and the *current* Penumbra testn Penumbra aims to implement full IBC support for cross-chain asset transfers. For now, however, we're only running a relayer between the Penumbra testnet and the [Osmosis testnet] chains. -For Testnet 62 Iapetus, the channel information is: +For Testnet 63 Rhea, the channel information is: