From 16f4fc70f082ae1c43b52f221a08942b9b9b9a61 Mon Sep 17 00:00:00 2001 From: Conor Schaefer Date: Thu, 28 Sep 2023 09:18:00 -0700 Subject: [PATCH] feat(pd)!: migrate to cometbft v0.37 Bumps the rust imports for tendermint-rs and tower-abci to use the v037, rather than v034, modules. The substantive change is the addition of two new ABCI event types: * PrepareProposal * ProcessProposal The consensus logic has been updated according to spec [0]. Refs #2263. [0] https://github.com/cometbft/cometbft/blob/v0.37.2/spec/abci/abci%2B%2B_comet_expected_behavior.md#adapting-existing-applications-that-use-abci --- .github/workflows/smoke.yml | 8 ++- Cargo.lock | 1 + crates/bin/pd/src/consensus.rs | 55 +++++++++++++++++- crates/bin/pd/src/events.rs | 4 +- crates/bin/pd/src/info.rs | 5 +- crates/bin/pd/src/main.rs | 6 +- crates/bin/pd/src/mempool.rs | 2 +- crates/bin/pd/src/snapshot.rs | 2 +- .../core/component/dex/src/component/dex.rs | 2 +- .../component/governance/src/component.rs | 2 +- .../src/component/shielded_pool.rs | 2 +- crates/narsil/narsil/Cargo.toml | 1 + crates/narsil/narsil/src/bin/narsild.rs | 6 +- crates/narsil/narsil/src/ledger/consensus.rs | 55 +++++++++++++++++- crates/narsil/narsil/src/ledger/info.rs | 5 +- crates/narsil/narsil/src/ledger/mempool.rs | 6 +- crates/narsil/narsil/src/ledger/snapshot.rs | 2 +- .../proto/src/gen/proto_descriptor.bin.no_lfs | Bin 339201 -> 332657 bytes .../charts/penumbra-network/values.yaml | 14 +---- deployments/charts/penumbra-node/values.yaml | 3 +- deployments/compose/docker-compose.yml | 2 +- deployments/scripts/smoke-test.sh | 3 + docs/guide/src/dev/devnet-quickstart.md | 2 +- docs/guide/src/pd/build.md | 8 +-- docs/guide/src/pd/join-testnet.md | 2 +- 25 files changed, 149 insertions(+), 49 deletions(-) diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 4c8f3957b2..c994482325 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -25,17 +25,19 @@ jobs: - name: Install cometbft binary run: | - curl -L -O "https://github.com/cometbft/cometbft/releases/download/v0.34.27/cometbft_0.34.27_linux_amd64.tar.gz" - tar -xzf "cometbft_0.34.27_linux_amd64.tar.gz" cometbft + COMETBFT_VERSION="0.37.2" + curl -L -O "https://github.com/cometbft/cometbft/releases/download/v${COMETBFT_VERSION}/cometbft_${COMETBFT_VERSION}_linux_amd64.tar.gz" + tar -xzf "cometbft_${COMETBFT_VERSION}_linux_amd64.tar.gz" cometbft mkdir -p $HOME/bin cp cometbft $HOME/bin echo $PATH export PATH=$HOME/bin:$PATH which cometbft + cometbft version - name: Run the smoke test suite run: | - export PATH=$HOME/bin:$PATH + export PATH="$HOME/bin:$PATH" ./deployments/scripts/smoke-test.sh env: TESTNET_RUNTIME: 2m diff --git a/Cargo.lock b/Cargo.lock index d53274882b..1d4e0e2333 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3958,6 +3958,7 @@ dependencies = [ "anyhow", "async-stream 0.2.1", "atty", + "bytes", "clap 3.2.25", "console-subscriber", "futures", diff --git a/crates/bin/pd/src/consensus.rs b/crates/bin/pd/src/consensus.rs index 534445c5dc..f22daefec0 100644 --- a/crates/bin/pd/src/consensus.rs +++ b/crates/bin/pd/src/consensus.rs @@ -3,7 +3,7 @@ use anyhow::Result; use penumbra_app::genesis; use penumbra_storage::Storage; use tendermint::abci::Event; -use tendermint::v0_34::abci::{ +use tendermint::v0_37::abci::{ request, response, ConsensusRequest as Request, ConsensusResponse as Response, }; use tokio::sync::mpsc; @@ -81,6 +81,18 @@ impl Consensus { .await .expect("commit must succeed"), ), + Request::PrepareProposal(proposal) => Response::PrepareProposal( + self.prepare_proposal(proposal) + .instrument(span) + .await + .expect("prepare proposal must succeed"), + ), + Request::ProcessProposal(proposal) => Response::ProcessProposal( + self.process_proposal(proposal) + .instrument(span) + .await + .expect("process proposal must succeed"), + ), })); } Ok(()) @@ -203,4 +215,45 @@ impl Consensus { retain_height: 0u32.into(), }) } + + /// Ensures that a proposal is suitable for processing. Inspects + /// the transactions within the proposal, discarding any that would + /// exceed the maximum number of tx bytes for a proposal, and returns + /// a propoal with the remainder. + async fn prepare_proposal( + &mut self, + proposal: request::PrepareProposal, + ) -> Result { + let mut txs: Vec = Vec::new(); + let num_candidate_txs = proposal.txs.len(); + tracing::debug!( + "processing PrepareProposal, found {} candidate transactions", + proposal.txs.len() + ); + // Ensure that list of transactions doesn't exceed max tx bytes. See spec at + // https://github.com/cometbft/cometbft/blob/v0.37.2/spec/abci/abci%2B%2B_comet_expected_behavior.md#adapting-existing-applications-that-use-abci + for tx in proposal.txs { + if (tx.len() + txs.len()) <= proposal.max_tx_bytes.try_into()? { + txs.push(tx); + } else { + break; + } + } + tracing::debug!( + "finished processing PrepareProposal, including {}/{} candidate transactions", + txs.len(), + num_candidate_txs + ); + Ok(response::PrepareProposal { txs: txs }) + } + + /// Mark the proposal as accepted. The [prepare_proposal] method ensures + /// that the number of tx bytes is within the acceptable limit. + async fn process_proposal( + &mut self, + proposal: request::ProcessProposal, + ) -> Result { + tracing::debug!(?proposal, "processing proposal"); + Ok(response::ProcessProposal::Accept) + } } diff --git a/crates/bin/pd/src/events.rs b/crates/bin/pd/src/events.rs index ee3e9f131c..f50fd2730c 100644 --- a/crates/bin/pd/src/events.rs +++ b/crates/bin/pd/src/events.rs @@ -4,7 +4,7 @@ use anyhow::Result; use futures::FutureExt; use regex::RegexSet; use tendermint::abci::Event; -use tendermint::v0_34::abci::{ConsensusRequest as Request, ConsensusResponse as Response}; +use tendermint::v0_37::abci::{ConsensusRequest as Request, ConsensusResponse as Response}; use tower::{Layer, Service}; #[derive(Debug, Clone)] @@ -102,6 +102,8 @@ where // No events. Response::InitChain(_) => {} Response::Commit(_) => {} + Response::PrepareProposal(_) => {} + Response::ProcessProposal(_) => {} // These responses have events. Response::BeginBlock(ref mut msg) => config.adjust_events(&mut msg.events), Response::DeliverTx(ref mut msg) => config.adjust_events(&mut msg.events), diff --git a/crates/bin/pd/src/info.rs b/crates/bin/pd/src/info.rs index 494288cde8..5e4efd2bf3 100644 --- a/crates/bin/pd/src/info.rs +++ b/crates/bin/pd/src/info.rs @@ -33,7 +33,7 @@ use penumbra_ibc::component::ConnectionStateReadExt as _; use penumbra_storage::Storage; use prost::Message; use std::str::FromStr; -use tendermint::v0_34::abci::{ +use tendermint::v0_37::abci::{ request, response::{self, Echo}, InfoRequest, InfoResponse, @@ -41,7 +41,7 @@ use tendermint::v0_34::abci::{ use tower_abci::BoxError; use tracing::Instrument; -use penumbra_tower_trace::v034::RequestExt; +use penumbra_tower_trace::v037::RequestExt; const ABCI_INFO_VERSION: &str = env!("VERGEN_GIT_SEMVER"); @@ -609,7 +609,6 @@ impl tower_service::Service for Info { InfoRequest::Echo(echo) => Ok(InfoResponse::Echo(Echo { message: echo.message, })), - InfoRequest::SetOption(_) => todo!(), } } .instrument(span) diff --git a/crates/bin/pd/src/main.rs b/crates/bin/pd/src/main.rs index a5786b6bcb..cff4238b5e 100644 --- a/crates/bin/pd/src/main.rs +++ b/crates/bin/pd/src/main.rs @@ -33,8 +33,8 @@ use tonic::transport::Server; use tracing_subscriber::{prelude::*, EnvFilter}; use url::Url; -use penumbra_tower_trace::v034::RequestExt; -use tendermint::v0_34::abci::{ConsensusRequest, MempoolRequest}; +use penumbra_tower_trace::v037::RequestExt; +use tendermint::v0_37::abci::{ConsensusRequest, MempoolRequest}; #[derive(Debug, Parser)] #[clap( @@ -318,7 +318,7 @@ async fn main() -> anyhow::Result<()> { let abci_server = tokio::task::Builder::new() .name("abci_server") .spawn( - tower_abci::v034::Server::builder() + tower_abci::v037::Server::builder() .consensus(consensus) .snapshot(snapshot) .mempool(mempool) diff --git a/crates/bin/pd/src/mempool.rs b/crates/bin/pd/src/mempool.rs index 3bb37c2e6f..7716c5af90 100644 --- a/crates/bin/pd/src/mempool.rs +++ b/crates/bin/pd/src/mempool.rs @@ -2,7 +2,7 @@ use anyhow::Result; use penumbra_storage::{Snapshot, Storage}; -use tendermint::v0_34::abci::{ +use tendermint::v0_37::abci::{ request::CheckTx as CheckTxReq, request::CheckTxKind, response::CheckTx as CheckTxRsp, MempoolRequest as Request, MempoolResponse as Response, }; diff --git a/crates/bin/pd/src/snapshot.rs b/crates/bin/pd/src/snapshot.rs index 7147c8fe64..bc297b42f0 100644 --- a/crates/bin/pd/src/snapshot.rs +++ b/crates/bin/pd/src/snapshot.rs @@ -5,7 +5,7 @@ use std::{ }; use futures::FutureExt; -use tendermint::v0_34::abci::{SnapshotRequest, SnapshotResponse}; +use tendermint::v0_37::abci::{SnapshotRequest, SnapshotResponse}; use tower_abci::BoxError; #[derive(Clone, Debug)] diff --git a/crates/core/component/dex/src/component/dex.rs b/crates/core/component/dex/src/component/dex.rs index 541cfcae01..387a756584 100644 --- a/crates/core/component/dex/src/component/dex.rs +++ b/crates/core/component/dex/src/component/dex.rs @@ -7,7 +7,7 @@ use penumbra_chain::component::StateReadExt as _; use penumbra_component::Component; use penumbra_proto::{StateReadProto, StateWriteProto}; use penumbra_storage::{StateRead, StateWrite}; -use tendermint::v0_34::abci; +use tendermint::v0_37::abci; use tracing::instrument; use crate::{ diff --git a/crates/core/component/governance/src/component.rs b/crates/core/component/governance/src/component.rs index 51558b895d..1c5868270b 100644 --- a/crates/core/component/governance/src/component.rs +++ b/crates/core/component/governance/src/component.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::{Context, Result}; use async_trait::async_trait; use penumbra_storage::StateWrite; -use tendermint::v0_34::abci; +use tendermint::v0_37::abci; use tracing::instrument; use penumbra_component::Component; diff --git a/crates/core/component/shielded-pool/src/component/shielded_pool.rs b/crates/core/component/shielded-pool/src/component/shielded_pool.rs index fc8e3bbef9..48d3f0a1f4 100644 --- a/crates/core/component/shielded-pool/src/component/shielded_pool.rs +++ b/crates/core/component/shielded-pool/src/component/shielded_pool.rs @@ -9,7 +9,7 @@ use penumbra_proto::StateReadProto; use penumbra_sct::Nullifier; use penumbra_storage::StateRead; use penumbra_storage::StateWrite; -use tendermint::v0_34::abci; +use tendermint::v0_37::abci; use tracing::instrument; use crate::genesis::Content as GenesisContent; diff --git a/crates/narsil/narsil/Cargo.toml b/crates/narsil/narsil/Cargo.toml index a3553ba240..a58178fa59 100644 --- a/crates/narsil/narsil/Cargo.toml +++ b/crates/narsil/narsil/Cargo.toml @@ -30,6 +30,7 @@ tower-actor = "0.1.0" # External dependencies anyhow = "1" async-stream = "0.2" +bytes = "1" clap = { version = "3", features = ["derive", "env"] } console-subscriber = "0.1.8" futures = "0.3" diff --git a/crates/narsil/narsil/src/bin/narsild.rs b/crates/narsil/narsil/src/bin/narsild.rs index 216b889283..89078ff297 100644 --- a/crates/narsil/narsil/src/bin/narsild.rs +++ b/crates/narsil/narsil/src/bin/narsild.rs @@ -7,8 +7,8 @@ use console_subscriber::ConsoleLayer; use metrics_tracing_context::{MetricsLayer, TracingContextLayer}; use metrics_util::layers::Stack; use penumbra_tendermint_proxy::TendermintProxy; -use penumbra_tower_trace::v034::RequestExt; -use tendermint::v0_34::abci::{ConsensusRequest, MempoolRequest}; +use penumbra_tower_trace::v037::RequestExt; +use tendermint::v0_37::abci::{ConsensusRequest, MempoolRequest}; use narsil::{ ledger::{consensus::Consensus, mempool::Mempool, snapshot::Snapshot, Info}, @@ -159,7 +159,7 @@ async fn main() -> anyhow::Result<()> { let abci_server = tokio::task::Builder::new() .name("abci_server") .spawn( - tower_abci::v034::Server::builder() + tower_abci::v037::Server::builder() .consensus(consensus) .snapshot(snapshot) .mempool(mempool) diff --git a/crates/narsil/narsil/src/ledger/consensus.rs b/crates/narsil/narsil/src/ledger/consensus.rs index dcc26cb68a..c4b2bab7db 100644 --- a/crates/narsil/narsil/src/ledger/consensus.rs +++ b/crates/narsil/narsil/src/ledger/consensus.rs @@ -3,7 +3,7 @@ use anyhow::Result; use penumbra_app::genesis; use penumbra_storage::Storage; use tendermint::abci::Event; -use tendermint::v0_34::abci::{ +use tendermint::v0_37::abci::{ request, response, ConsensusRequest as Request, ConsensusResponse as Response, }; use tokio::sync::mpsc; @@ -81,6 +81,18 @@ impl Consensus { .await .expect("commit must succeed"), ), + Request::PrepareProposal(proposal) => Response::PrepareProposal( + self.prepare_proposal(proposal) + .instrument(span) + .await + .expect("prepare proposal must succeed"), + ), + Request::ProcessProposal(proposal) => Response::ProcessProposal( + self.process_proposal(proposal) + .instrument(span) + .await + .expect("process proposal must succeed"), + ), })); } Ok(()) @@ -193,4 +205,45 @@ impl Consensus { retain_height: 0u32.into(), }) } + + /// Ensures that a proposal is suitable for processing. Inspects + /// the transactions within the proposal, discarding any that would + /// exceed the maximum number of tx bytes for a proposal, and returns + /// a propoal with the remainder. + async fn prepare_proposal( + &mut self, + proposal: request::PrepareProposal, + ) -> Result { + let mut txs: Vec = Vec::new(); + let num_candidate_txs = proposal.txs.len(); + tracing::debug!( + "Processing PrepareProposal, found {} candidate transactions", + proposal.txs.len() + ); + // Ensure that list of transactions doesn't exceed max tx bytes. See spec at + // https://github.com/cometbft/cometbft/blob/v0.37.2/spec/abci/abci%2B%2B_comet_expected_behavior.md#adapting-existing-applications-that-use-abci + for tx in proposal.txs { + if (tx.len() + txs.len()) <= proposal.max_tx_bytes.try_into()? { + txs.push(tx); + } else { + break; + } + } + tracing::debug!( + "Finished processing PrepareProposal, including {}/{} candidate transactions", + txs.len(), + num_candidate_txs + ); + Ok(response::PrepareProposal { txs: txs }) + } + + /// Mark the proposal as accepted. The [prepare_proposal] method ensures + /// that the number of tx bytes is within the acceptable limit. + async fn process_proposal( + &mut self, + proposal: request::ProcessProposal, + ) -> Result { + tracing::debug!(?proposal, "Processing proposal"); + Ok(response::ProcessProposal::Accept) + } } diff --git a/crates/narsil/narsil/src/ledger/info.rs b/crates/narsil/narsil/src/ledger/info.rs index 1f886ee06e..6e1dba0bc9 100644 --- a/crates/narsil/narsil/src/ledger/info.rs +++ b/crates/narsil/narsil/src/ledger/info.rs @@ -7,8 +7,8 @@ use std::{ use futures::FutureExt; use penumbra_chain::component::{AppHashRead, StateReadExt}; use penumbra_storage::Storage; -use penumbra_tower_trace::v034::RequestExt; -use tendermint::v0_34::abci::{self, response::Echo, InfoRequest, InfoResponse}; +use penumbra_tower_trace::v037::RequestExt; +use tendermint::v0_37::abci::{self, response::Echo, InfoRequest, InfoResponse}; use tower_abci::BoxError; use tracing::Instrument; @@ -78,7 +78,6 @@ impl tower_service::Service for Info { InfoRequest::Echo(echo) => Ok(InfoResponse::Echo(Echo { message: echo.message, })), - InfoRequest::SetOption(_) => todo!(), } } .instrument(span) diff --git a/crates/narsil/narsil/src/ledger/mempool.rs b/crates/narsil/narsil/src/ledger/mempool.rs index b77d35cecd..d03fbc6f22 100644 --- a/crates/narsil/narsil/src/ledger/mempool.rs +++ b/crates/narsil/narsil/src/ledger/mempool.rs @@ -2,9 +2,9 @@ use anyhow::Result; use penumbra_storage::{Snapshot, Storage}; -use tendermint::v0_34::abci::request::{CheckTx as CheckTxReq, CheckTxKind}; -use tendermint::v0_34::abci::response::CheckTx as CheckTxRsp; -use tendermint::v0_34::abci::{MempoolRequest as Request, MempoolResponse as Response}; +use tendermint::v0_37::abci::request::{CheckTx as CheckTxReq, CheckTxKind}; +use tendermint::v0_37::abci::response::CheckTx as CheckTxRsp; +use tendermint::v0_37::abci::{MempoolRequest as Request, MempoolResponse as Response}; use tokio::sync::{mpsc, watch}; use tower_actor::Message; diff --git a/crates/narsil/narsil/src/ledger/snapshot.rs b/crates/narsil/narsil/src/ledger/snapshot.rs index 7147c8fe64..bc297b42f0 100644 --- a/crates/narsil/narsil/src/ledger/snapshot.rs +++ b/crates/narsil/narsil/src/ledger/snapshot.rs @@ -5,7 +5,7 @@ use std::{ }; use futures::FutureExt; -use tendermint::v0_34::abci::{SnapshotRequest, SnapshotResponse}; +use tendermint::v0_37::abci::{SnapshotRequest, SnapshotResponse}; use tower_abci::BoxError; #[derive(Clone, Debug)] diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index 5e765a788e4db641fa23cf6231778c473c16da21..8213311bd58be7cd87c13485f00e1fa0d9bbbfad 100644 GIT binary patch delta 14121 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 delta 20691 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