From fae22f94d47a2ad00aff80c157f47350d24997de Mon Sep 17 00:00:00 2001
From: Mikhail Zabaluev <mikhail@informal.systems>
Date: Wed, 19 Oct 2022 19:57:38 +0300
Subject: [PATCH] WIP: Adapt domain types to v0.37 and add multi-version
 conversions.

---
 proto/src/serializers.rs                      |   1 -
 proto/src/serializers/evidence.rs             |  73 ---
 .../src/abci/doc/request-prepareproposal.md   |   1 +
 .../src/abci/doc/request-processproposal.md   |   1 +
 tendermint/src/abci/request.rs                |  84 +++
 .../src/abci/request/prepare_proposal.rs      |  76 +++
 .../src/abci/request/process_proposal.rs      |  75 +++
 tendermint/src/abci/types.rs                  | 486 +++++++++++++-----
 tendermint/src/account.rs                     |  20 +
 tendermint/src/evidence.rs                    |   5 +-
 10 files changed, 612 insertions(+), 210 deletions(-)
 delete mode 100644 proto/src/serializers/evidence.rs
 create mode 100644 tendermint/src/abci/doc/request-prepareproposal.md
 create mode 100644 tendermint/src/abci/doc/request-processproposal.md
 create mode 100644 tendermint/src/abci/request/prepare_proposal.rs
 create mode 100644 tendermint/src/abci/request/process_proposal.rs

diff --git a/proto/src/serializers.rs b/proto/src/serializers.rs
index 651a197f6..9915739ec 100644
--- a/proto/src/serializers.rs
+++ b/proto/src/serializers.rs
@@ -53,7 +53,6 @@
 // Todo: remove dead_code allowance as soon as more types are implemented
 #![allow(dead_code)]
 pub mod bytes;
-pub mod evidence;
 pub mod from_str;
 pub mod nullable;
 pub mod optional;
diff --git a/proto/src/serializers/evidence.rs b/proto/src/serializers/evidence.rs
deleted file mode 100644
index cede08634..000000000
--- a/proto/src/serializers/evidence.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-//! EvidenceVariant helper struct for evidence serialization.
-//!
-//! This is a workaround until we figure a better way of JSON serializing evidence.
-//! It is a modified copy of the crate::tendermint::types::evidence::Sum struct.
-
-macro_rules! evidence_variant_helper {
-    ($ver_mod:ident) => {
-        use crate::tendermint::$ver_mod::types::{evidence::Sum, Evidence};
-
-        #[allow(clippy::large_enum_variant)]
-        #[derive(Clone, PartialEq, ::serde::Deserialize, ::serde::Serialize)]
-        #[serde(tag = "type", content = "value")]
-        pub enum EvidenceVariant {
-            /// Provided for when the evidence struct's optional `sum` field is `None`.
-            None,
-            #[serde(rename = "tendermint/DuplicateVoteEvidence")]
-            DuplicateVoteEvidence(crate::tendermint::$ver_mod::types::DuplicateVoteEvidence),
-            #[serde(rename = "tendermint/LightClientAttackEvidence")]
-            LightClientAttackEvidence(crate::tendermint::$ver_mod::types::LightClientAttackEvidence),
-        }
-
-        impl From<EvidenceVariant> for Evidence {
-            fn from(value: EvidenceVariant) -> Self {
-                match value {
-                    EvidenceVariant::None => Evidence { sum: None },
-                    _ => Evidence {
-                        sum: Some(value.into()),
-                    },
-                }
-            }
-        }
-
-        impl From<Evidence> for EvidenceVariant {
-            fn from(value: Evidence) -> Self {
-                match value.sum {
-                    Some(sum) => sum.into(),
-                    None => Self::None,
-                }
-            }
-        }
-
-        impl From<Sum> for EvidenceVariant {
-            fn from(value: Sum) -> Self {
-                match value {
-                    Sum::DuplicateVoteEvidence(d) => Self::DuplicateVoteEvidence(d),
-                    Sum::LightClientAttackEvidence(l) => Self::LightClientAttackEvidence(l),
-                }
-            }
-        }
-
-        impl From<EvidenceVariant> for Sum {
-            fn from(value: EvidenceVariant) -> Self {
-                match value {
-                    // This should never be called - should be handled instead in the
-                    // `impl From<EvidenceVariant> for Evidence` above.
-                    EvidenceVariant::None => {
-                        panic!("non-existent evidence cannot be converted into its protobuf representation")
-                    },
-                    EvidenceVariant::DuplicateVoteEvidence(d) => Self::DuplicateVoteEvidence(d),
-                    EvidenceVariant::LightClientAttackEvidence(l) => Self::LightClientAttackEvidence(l),
-                }
-            }
-        }
-    };
-}
-
-pub mod v0_34 {
-    evidence_variant_helper!(v0_34);
-}
-
-pub mod v0_37 {
-    evidence_variant_helper!(v0_37);
-}
diff --git a/tendermint/src/abci/doc/request-prepareproposal.md b/tendermint/src/abci/doc/request-prepareproposal.md
new file mode 100644
index 000000000..b76cbb647
--- /dev/null
+++ b/tendermint/src/abci/doc/request-prepareproposal.md
@@ -0,0 +1 @@
+[ABCI documentation](https://github.com/tendermint/tendermint/blob/main/spec/abci/abci++_methods.md#prepareproposal)
diff --git a/tendermint/src/abci/doc/request-processproposal.md b/tendermint/src/abci/doc/request-processproposal.md
new file mode 100644
index 000000000..b7474ce7f
--- /dev/null
+++ b/tendermint/src/abci/doc/request-processproposal.md
@@ -0,0 +1 @@
+[ABCI documentation](https://github.com/tendermint/tendermint/blob/main/spec/abci/abci++_methods.md#processproposal)
diff --git a/tendermint/src/abci/request.rs b/tendermint/src/abci/request.rs
index 23cea370f..309565da2 100644
--- a/tendermint/src/abci/request.rs
+++ b/tendermint/src/abci/request.rs
@@ -36,6 +36,8 @@ mod info;
 mod init_chain;
 mod load_snapshot_chunk;
 mod offer_snapshot;
+mod prepare_proposal;
+mod process_proposal;
 mod query;
 mod set_option;
 
@@ -49,6 +51,8 @@ pub use info::Info;
 pub use init_chain::InitChain;
 pub use load_snapshot_chunk::LoadSnapshotChunk;
 pub use offer_snapshot::OfferSnapshot;
+pub use prepare_proposal::PrepareProposal;
+pub use process_proposal::ProcessProposal;
 pub use query::Query;
 pub use set_option::SetOption;
 
@@ -86,6 +90,10 @@ pub enum Request {
     LoadSnapshotChunk(LoadSnapshotChunk),
     #[doc = include_str!("doc/request-applysnapshotchunk.md")]
     ApplySnapshotChunk(ApplySnapshotChunk),
+    #[doc = include_str!("doc/request-prepareproposal.md")]
+    PrepareProposal(PrepareProposal),
+    #[doc = include_str!("doc/request-processproposal.md")]
+    ProcessProposal(ProcessProposal),
 }
 
 impl Request {
@@ -99,6 +107,8 @@ impl Request {
             DeliverTx(_) => MethodKind::Consensus,
             EndBlock(_) => MethodKind::Consensus,
             Commit => MethodKind::Consensus,
+            PrepareProposal(_) => MethodKind::Consensus,
+            ProcessProposal(_) => MethodKind::Consensus,
             CheckTx(_) => MethodKind::Mempool,
             ListSnapshots => MethodKind::Snapshot,
             OfferSnapshot(_) => MethodKind::Snapshot,
@@ -282,6 +292,12 @@ mod v0_34 {
                 Request::OfferSnapshot(x) => Some(Value::OfferSnapshot(x.into())),
                 Request::LoadSnapshotChunk(x) => Some(Value::LoadSnapshotChunk(x.into())),
                 Request::ApplySnapshotChunk(x) => Some(Value::ApplySnapshotChunk(x.into())),
+                Request::PrepareProposal(x) => {
+                    panic!("PrepareProposal should not be used with Tendermint 0.34")
+                },
+                Request::ProcessProposal(x) => {
+                    panic!("ProcessProposal should not be used with Tendermint 0.34")
+                },
             };
             pb::Request { value }
         }
@@ -319,3 +335,71 @@ mod v0_34 {
 
     impl Protobuf<pb::Request> for Request {}
 }
+
+mod v0_37 {
+    use super::Request;
+    use crate::{prelude::*, Error};
+    use tendermint_proto::v0_37::abci as pb;
+    use tendermint_proto::Protobuf;
+
+    impl From<Request> for pb::Request {
+        fn from(request: Request) -> pb::Request {
+            use pb::request::Value;
+            let value = match request {
+                Request::Echo(x) => Some(Value::Echo(x.into())),
+                Request::Flush => Some(Value::Flush(Default::default())),
+                Request::Info(x) => Some(Value::Info(x.into())),
+                Request::InitChain(x) => Some(Value::InitChain(x.into())),
+                Request::Query(x) => Some(Value::Query(x.into())),
+                Request::BeginBlock(x) => Some(Value::BeginBlock(x.into())),
+                Request::CheckTx(x) => Some(Value::CheckTx(x.into())),
+                Request::DeliverTx(x) => Some(Value::DeliverTx(x.into())),
+                Request::EndBlock(x) => Some(Value::EndBlock(x.into())),
+                Request::Commit => Some(Value::Commit(Default::default())),
+                Request::ListSnapshots => Some(Value::ListSnapshots(Default::default())),
+                Request::OfferSnapshot(x) => Some(Value::OfferSnapshot(x.into())),
+                Request::LoadSnapshotChunk(x) => Some(Value::LoadSnapshotChunk(x.into())),
+                Request::ApplySnapshotChunk(x) => Some(Value::ApplySnapshotChunk(x.into())),
+                Request::PrepareProposal(x) => Some(Value::PrepareProposal(x.into())),
+                Request::ProcessProposal(x) => Some(Value::ProcessProposal(x.into())),
+                Request::SetOption(x) => {
+                    panic!("SetOption should not be used with Tendermint 0.37")
+                },
+            };
+            pb::Request { value }
+        }
+    }
+
+    impl TryFrom<pb::Request> for Request {
+        type Error = Error;
+
+        fn try_from(request: pb::Request) -> Result<Self, Self::Error> {
+            use pb::request::Value;
+            match request.value {
+                Some(Value::Echo(x)) => Ok(Request::Echo(x.try_into()?)),
+                Some(Value::Flush(pb::RequestFlush {})) => Ok(Request::Flush),
+                Some(Value::Info(x)) => Ok(Request::Info(x.try_into()?)),
+                Some(Value::InitChain(x)) => Ok(Request::InitChain(x.try_into()?)),
+                Some(Value::Query(x)) => Ok(Request::Query(x.try_into()?)),
+                Some(Value::BeginBlock(x)) => Ok(Request::BeginBlock(x.try_into()?)),
+                Some(Value::CheckTx(x)) => Ok(Request::CheckTx(x.try_into()?)),
+                Some(Value::DeliverTx(x)) => Ok(Request::DeliverTx(x.try_into()?)),
+                Some(Value::EndBlock(x)) => Ok(Request::EndBlock(x.try_into()?)),
+                Some(Value::Commit(pb::RequestCommit {})) => Ok(Request::Commit),
+                Some(Value::ListSnapshots(pb::RequestListSnapshots {})) => {
+                    Ok(Request::ListSnapshots)
+                },
+                Some(Value::OfferSnapshot(x)) => Ok(Request::OfferSnapshot(x.try_into()?)),
+                Some(Value::LoadSnapshotChunk(x)) => Ok(Request::LoadSnapshotChunk(x.try_into()?)),
+                Some(Value::ApplySnapshotChunk(x)) => {
+                    Ok(Request::ApplySnapshotChunk(x.try_into()?))
+                },
+                Some(Value::PrepareProposal(x)) => Ok(Request::PrepareProposal(x.try_into()?)),
+                Some(Value::ProcessProposal(x)) => Ok(Request::ProcessProposal(x.try_into()?)),
+                None => Err(crate::Error::missing_data()),
+            }
+        }
+    }
+
+    impl Protobuf<pb::Request> for Request {}
+}
diff --git a/tendermint/src/abci/request/prepare_proposal.rs b/tendermint/src/abci/request/prepare_proposal.rs
new file mode 100644
index 000000000..30e618063
--- /dev/null
+++ b/tendermint/src/abci/request/prepare_proposal.rs
@@ -0,0 +1,76 @@
+use crate::prelude::*;
+use crate::{
+    abci::types::{CommitInfo, Misbehavior},
+    account, block, Error, Hash, Time,
+};
+
+use bytes::Bytes;
+
+#[doc = include_str!("../doc/request-prepareproposal.md")]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PrepareProposal {
+    /// the modified transactions cannot exceed this size.
+    pub max_tx_bytes: i64,
+    /// txs is an array of transactions that will be included in a block,
+    /// sent to the app for possible modifications.
+    pub txs: Vec<Bytes>,
+    pub local_last_commit: Option<CommitInfo>,
+    pub misbehavior: Vec<Misbehavior>,
+    pub height: block::Height,
+    pub time: Time,
+    pub next_validators_hash: Hash,
+    /// address of the public key of the validator proposing the block.
+    pub proposer_address: account::Id,
+}
+
+// =============================================================================
+// Protobuf conversions
+// =============================================================================
+
+use tendermint_proto::v0_37::abci as pb;
+use tendermint_proto::Protobuf;
+
+impl From<PrepareProposal> for pb::RequestPrepareProposal {
+    fn from(value: PrepareProposal) -> Self {
+        Self {
+            max_tx_bytes: value.max_tx_bytes,
+            txs: value.txs,
+            local_last_commit: value.local_last_commit.map(Into::into),
+            misbehavior: value.misbehavior.into_iter().map(Into::into).collect(),
+            height: value.height.into(),
+            time: Some(value.time.into()),
+            next_validators_hash: value.next_validators_hash.into(),
+            proposer_address: value.proposer_address.into(),
+        }
+    }
+}
+
+impl TryFrom<pb::RequestPrepareProposal> for PrepareProposal {
+    type Error = Error;
+
+    fn try_from(message: pb::RequestPrepareProposal) -> Result<Self, Self::Error> {
+        let req = Self {
+            max_tx_bytes: message.max_tx_bytes,
+            txs: message.txs,
+            local_last_commit: message
+                .local_last_commit
+                .map(TryInto::try_into)
+                .transpose()?,
+            misbehavior: message
+                .misbehavior
+                .into_iter()
+                .map(TryInto::try_into)
+                .collect::<Result<Vec<_>, _>>()?,
+            height: message.height.try_into()?,
+            time: message
+                .time
+                .ok_or_else(Error::missing_timestamp)?
+                .try_into()?,
+            next_validators_hash: message.next_validators_hash.try_into()?,
+            proposer_address: message.proposer_address.try_into()?,
+        };
+        Ok(req)
+    }
+}
+
+impl Protobuf<pb::RequestPrepareProposal> for PrepareProposal {}
diff --git a/tendermint/src/abci/request/process_proposal.rs b/tendermint/src/abci/request/process_proposal.rs
new file mode 100644
index 000000000..97937b1c8
--- /dev/null
+++ b/tendermint/src/abci/request/process_proposal.rs
@@ -0,0 +1,75 @@
+use crate::prelude::*;
+use crate::{
+    abci::types::{CommitInfo, Misbehavior},
+    account, block, Error, Hash, Time,
+};
+
+use bytes::Bytes;
+
+#[doc = include_str!("../doc/request-processproposal.md")]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ProcessProposal {
+    /// txs is an array of transactions that will be included in a block,
+    /// sent to the app for possible modifications.
+    pub txs: Vec<Bytes>,
+    pub proposed_last_commit: Option<CommitInfo>,
+    pub misbehavior: Vec<Misbehavior>,
+    pub hash: Hash,
+    pub height: block::Height,
+    pub time: Time,
+    pub next_validators_hash: Hash,
+    /// address of the public key of the validator proposing the block.
+    pub proposer_address: account::Id,
+}
+
+// =============================================================================
+// Protobuf conversions
+// =============================================================================
+
+use tendermint_proto::v0_37::abci as pb;
+use tendermint_proto::Protobuf;
+
+impl From<ProcessProposal> for pb::RequestProcessProposal {
+    fn from(value: ProcessProposal) -> Self {
+        Self {
+            txs: value.txs,
+            proposed_last_commit: value.proposed_last_commit.map(Into::into),
+            misbehavior: value.misbehavior.into_iter().map(Into::into).collect(),
+            hash: value.hash.into(),
+            height: value.height.into(),
+            time: Some(value.time.into()),
+            next_validators_hash: value.next_validators_hash.into(),
+            proposer_address: value.proposer_address.into(),
+        }
+    }
+}
+
+impl TryFrom<pb::RequestProcessProposal> for ProcessProposal {
+    type Error = Error;
+
+    fn try_from(message: pb::RequestProcessProposal) -> Result<Self, Self::Error> {
+        let req = Self {
+            txs: message.txs,
+            proposed_last_commit: message
+                .proposed_last_commit
+                .map(TryInto::try_into)
+                .transpose()?,
+            misbehavior: message
+                .misbehavior
+                .into_iter()
+                .map(TryInto::try_into)
+                .collect::<Result<Vec<_>, _>>()?,
+            hash: message.hash.try_into()?,
+            height: message.height.try_into()?,
+            time: message
+                .time
+                .ok_or_else(Error::missing_timestamp)?
+                .try_into()?,
+            next_validators_hash: message.next_validators_hash.try_into()?,
+            proposer_address: message.proposer_address.try_into()?,
+        };
+        Ok(req)
+    }
+}
+
+impl Protobuf<pb::RequestProcessProposal> for ProcessProposal {}
diff --git a/tendermint/src/abci/types.rs b/tendermint/src/abci/types.rs
index 11f8afa28..6aeaf7536 100644
--- a/tendermint/src/abci/types.rs
+++ b/tendermint/src/abci/types.rs
@@ -5,11 +5,9 @@
 //!
 //! [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#data-types)
 
-use core::convert::{TryFrom, TryInto};
-
 use bytes::Bytes;
 
-use crate::{block, prelude::*, vote, Error, Time};
+use crate::{block, prelude::*, vote, Time};
 
 /// A validator address with voting power.
 ///
@@ -33,15 +31,15 @@ pub struct VoteInfo {
     pub signed_last_block: bool,
 }
 
-/// The possible kinds of [`Evidence`].
+/// The possible kinds of [`Misbehavior`].
 ///
 /// Note: the
-/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#evidencetype-2)
-/// calls this `EvidenceType`, but we follow the Rust convention and name it `EvidenceKind`
+/// [ABCI documentation](https://github.com/tendermint/tendermint/blob/main/spec/abci/abci++_methods.md#misbehaviortype)
+/// calls this `MisbehaviorType`, but we follow the Rust convention and name it `MisbehaviorKind`
 /// to avoid confusion with Rust types.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 #[repr(i32)]
-pub enum EvidenceKind {
+pub enum MisbehaviorKind {
     /// Unknown evidence type (proto default value).
     Unknown = 0,
     /// Evidence that the validator voted for two different blocks in the same
@@ -55,12 +53,12 @@ pub enum EvidenceKind {
 ///
 /// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#evidence)
 #[derive(Clone, PartialEq, Eq, Debug)]
-pub struct Evidence {
+pub struct Misbehavior {
     /// The kind of evidence.
     ///
     /// Note: this field is called `type` in the protobuf, but we call it `kind`
     /// to avoid the Rust keyword.
-    pub kind: EvidenceKind,
+    pub kind: MisbehaviorKind,
     /// The offending validator.
     pub validator: Validator,
     /// The height when the offense occurred.
@@ -75,11 +73,11 @@ pub struct Evidence {
     pub total_voting_power: vote::Power,
 }
 
-/// Information on the last block commit.
+/// Information on a block commit.
 ///
-/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#lastcommitinfo)
+/// [ABCI documentation](https://github.com/tendermint/tendermint/blob/main/spec/abci/abci++_methods.md#extendedcommitinfo)
 #[derive(Clone, PartialEq, Eq, Debug)]
-pub struct LastCommitInfo {
+pub struct CommitInfo {
     /// The commit round.
     ///
     /// Reflects the total number of rounds it took to come to consensus for the
@@ -120,154 +118,378 @@ pub struct Snapshot {
 // Protobuf conversions
 // =============================================================================
 
-use tendermint_proto::{abci as pb, Protobuf};
+mod v0_34 {
+    use super::{CommitInfo, Misbehavior, MisbehaviorKind, Snapshot, Validator, VoteInfo};
+    use crate::{prelude::*, Error};
+    use tendermint_proto::v0_34::abci as pb;
+    use tendermint_proto::Protobuf;
+
+    use bytes::Bytes;
 
-impl From<Validator> for pb::Validator {
-    fn from(v: Validator) -> Self {
-        Self {
-            address: Bytes::copy_from_slice(&v.address[..]),
-            power: v.power.into(),
+    impl From<Validator> for pb::Validator {
+        fn from(v: Validator) -> Self {
+            Self {
+                address: Bytes::copy_from_slice(&v.address[..]),
+                power: v.power.into(),
+            }
         }
     }
-}
 
-impl TryFrom<pb::Validator> for Validator {
-    type Error = Error;
-
-    fn try_from(vu: pb::Validator) -> Result<Self, Self::Error> {
-        let address = if vu.address.len() == 20 {
-            let mut bytes = [0u8; 20];
-            bytes.copy_from_slice(&vu.address);
-            bytes
-        } else {
-            return Err(Error::invalid_account_id_length());
-        };
-
-        Ok(Self {
-            address,
-            power: vu.power.try_into()?,
-        })
+    impl TryFrom<pb::Validator> for Validator {
+        type Error = Error;
+
+        fn try_from(vu: pb::Validator) -> Result<Self, Self::Error> {
+            let address = if vu.address.len() == 20 {
+                let mut bytes = [0u8; 20];
+                bytes.copy_from_slice(&vu.address);
+                bytes
+            } else {
+                return Err(Error::invalid_account_id_length());
+            };
+
+            Ok(Self {
+                address,
+                power: vu.power.try_into()?,
+            })
+        }
     }
-}
 
-impl Protobuf<pb::Validator> for Validator {}
+    impl Protobuf<pb::Validator> for Validator {}
 
-impl From<VoteInfo> for pb::VoteInfo {
-    fn from(vi: VoteInfo) -> Self {
-        Self {
-            validator: Some(vi.validator.into()),
-            signed_last_block: vi.signed_last_block,
+    impl From<VoteInfo> for pb::VoteInfo {
+        fn from(vi: VoteInfo) -> Self {
+            Self {
+                validator: Some(vi.validator.into()),
+                signed_last_block: vi.signed_last_block,
+            }
         }
     }
-}
 
-impl TryFrom<pb::VoteInfo> for VoteInfo {
-    type Error = Error;
-
-    fn try_from(vi: pb::VoteInfo) -> Result<Self, Self::Error> {
-        Ok(Self {
-            validator: vi
-                .validator
-                .ok_or_else(Error::missing_validator)?
-                .try_into()?,
-            signed_last_block: vi.signed_last_block,
-        })
+    impl TryFrom<pb::VoteInfo> for VoteInfo {
+        type Error = Error;
+
+        fn try_from(vi: pb::VoteInfo) -> Result<Self, Self::Error> {
+            Ok(Self {
+                validator: vi
+                    .validator
+                    .ok_or_else(Error::missing_validator)?
+                    .try_into()?,
+                signed_last_block: vi.signed_last_block,
+            })
+        }
     }
-}
 
-impl Protobuf<pb::VoteInfo> for VoteInfo {}
+    impl Protobuf<pb::VoteInfo> for VoteInfo {}
+
+    impl From<Misbehavior> for pb::Evidence {
+        fn from(evidence: Misbehavior) -> Self {
+            Self {
+                r#type: evidence.kind as i32,
+                validator: Some(evidence.validator.into()),
+                height: evidence.height.into(),
+                time: Some(evidence.time.into()),
+                total_voting_power: evidence.total_voting_power.into(),
+            }
+        }
+    }
 
-impl From<Evidence> for pb::Evidence {
-    fn from(evidence: Evidence) -> Self {
-        Self {
-            r#type: evidence.kind as i32,
-            validator: Some(evidence.validator.into()),
-            height: evidence.height.into(),
-            time: Some(evidence.time.into()),
-            total_voting_power: evidence.total_voting_power.into(),
+    impl TryFrom<pb::Evidence> for Misbehavior {
+        type Error = Error;
+
+        fn try_from(evidence: pb::Evidence) -> Result<Self, Self::Error> {
+            let kind = match evidence.r#type {
+                0 => MisbehaviorKind::Unknown,
+                1 => MisbehaviorKind::DuplicateVote,
+                2 => MisbehaviorKind::LightClientAttack,
+                _ => return Err(Error::invalid_evidence()),
+            };
+
+            Ok(Self {
+                kind,
+                validator: evidence
+                    .validator
+                    .ok_or_else(Error::missing_validator)?
+                    .try_into()?,
+                height: evidence.height.try_into()?,
+                time: evidence
+                    .time
+                    .ok_or_else(Error::missing_timestamp)?
+                    .try_into()?,
+                total_voting_power: evidence.total_voting_power.try_into()?,
+            })
         }
     }
-}
 
-impl TryFrom<pb::Evidence> for Evidence {
-    type Error = Error;
-
-    fn try_from(evidence: pb::Evidence) -> Result<Self, Self::Error> {
-        let kind = match evidence.r#type {
-            0 => EvidenceKind::Unknown,
-            1 => EvidenceKind::DuplicateVote,
-            2 => EvidenceKind::LightClientAttack,
-            _ => return Err(Error::invalid_evidence()),
-        };
-
-        Ok(Self {
-            kind,
-            validator: evidence
-                .validator
-                .ok_or_else(Error::missing_validator)?
-                .try_into()?,
-            height: evidence.height.try_into()?,
-            time: evidence
-                .time
-                .ok_or_else(Error::missing_timestamp)?
-                .try_into()?,
-            total_voting_power: evidence.total_voting_power.try_into()?,
-        })
+    impl Protobuf<pb::Evidence> for Misbehavior {}
+
+    impl From<CommitInfo> for pb::LastCommitInfo {
+        fn from(lci: CommitInfo) -> Self {
+            Self {
+                round: lci.round.into(),
+                votes: lci.votes.into_iter().map(Into::into).collect(),
+            }
+        }
     }
-}
 
-impl Protobuf<pb::Evidence> for Evidence {}
+    impl TryFrom<pb::LastCommitInfo> for CommitInfo {
+        type Error = Error;
+
+        fn try_from(lci: pb::LastCommitInfo) -> Result<Self, Self::Error> {
+            Ok(Self {
+                round: lci.round.try_into()?,
+                votes: lci
+                    .votes
+                    .into_iter()
+                    .map(TryInto::try_into)
+                    .collect::<Result<_, _>>()?,
+            })
+        }
+    }
 
-impl From<LastCommitInfo> for pb::LastCommitInfo {
-    fn from(lci: LastCommitInfo) -> Self {
-        Self {
-            round: lci.round.into(),
-            votes: lci.votes.into_iter().map(Into::into).collect(),
+    impl Protobuf<pb::LastCommitInfo> for CommitInfo {}
+
+    impl From<Snapshot> for pb::Snapshot {
+        fn from(snapshot: Snapshot) -> Self {
+            Self {
+                height: snapshot.height.into(),
+                format: snapshot.format,
+                chunks: snapshot.chunks,
+                hash: snapshot.hash,
+                metadata: snapshot.metadata,
+            }
         }
     }
-}
 
-impl TryFrom<pb::LastCommitInfo> for LastCommitInfo {
-    type Error = Error;
-
-    fn try_from(lci: pb::LastCommitInfo) -> Result<Self, Self::Error> {
-        Ok(Self {
-            round: lci.round.try_into()?,
-            votes: lci
-                .votes
-                .into_iter()
-                .map(TryInto::try_into)
-                .collect::<Result<_, _>>()?,
-        })
+    impl TryFrom<pb::Snapshot> for Snapshot {
+        type Error = Error;
+
+        fn try_from(snapshot: pb::Snapshot) -> Result<Self, Self::Error> {
+            Ok(Self {
+                height: snapshot.height.try_into()?,
+                format: snapshot.format,
+                chunks: snapshot.chunks,
+                hash: snapshot.hash,
+                metadata: snapshot.metadata,
+            })
+        }
     }
+
+    impl Protobuf<pb::Snapshot> for Snapshot {}
 }
 
-impl Protobuf<pb::LastCommitInfo> for LastCommitInfo {}
+mod v0_37 {
+    use super::{CommitInfo, Misbehavior, MisbehaviorKind, Snapshot, Validator, VoteInfo};
+    use crate::{prelude::*, Error};
+    use tendermint_proto::v0_37::abci as pb;
+    use tendermint_proto::Protobuf;
 
-impl From<Snapshot> for pb::Snapshot {
-    fn from(snapshot: Snapshot) -> Self {
-        Self {
-            height: snapshot.height.into(),
-            format: snapshot.format,
-            chunks: snapshot.chunks,
-            hash: snapshot.hash,
-            metadata: snapshot.metadata,
+    use bytes::Bytes;
+
+    impl From<Validator> for pb::Validator {
+        fn from(v: Validator) -> Self {
+            Self {
+                address: Bytes::copy_from_slice(&v.address[..]),
+                power: v.power.into(),
+            }
         }
     }
-}
 
-impl TryFrom<pb::Snapshot> for Snapshot {
-    type Error = Error;
-
-    fn try_from(snapshot: pb::Snapshot) -> Result<Self, Self::Error> {
-        Ok(Self {
-            height: snapshot.height.try_into()?,
-            format: snapshot.format,
-            chunks: snapshot.chunks,
-            hash: snapshot.hash,
-            metadata: snapshot.metadata,
-        })
+    impl TryFrom<pb::Validator> for Validator {
+        type Error = Error;
+
+        fn try_from(vu: pb::Validator) -> Result<Self, Self::Error> {
+            let address = if vu.address.len() == 20 {
+                let mut bytes = [0u8; 20];
+                bytes.copy_from_slice(&vu.address);
+                bytes
+            } else {
+                return Err(Error::invalid_account_id_length());
+            };
+
+            Ok(Self {
+                address,
+                power: vu.power.try_into()?,
+            })
+        }
+    }
+
+    impl Protobuf<pb::Validator> for Validator {}
+
+    impl From<VoteInfo> for pb::VoteInfo {
+        fn from(vi: VoteInfo) -> Self {
+            Self {
+                validator: Some(vi.validator.into()),
+                signed_last_block: vi.signed_last_block,
+            }
+        }
+    }
+
+    impl TryFrom<pb::VoteInfo> for VoteInfo {
+        type Error = Error;
+
+        fn try_from(vi: pb::VoteInfo) -> Result<Self, Self::Error> {
+            Ok(Self {
+                validator: vi
+                    .validator
+                    .ok_or_else(Error::missing_validator)?
+                    .try_into()?,
+                signed_last_block: vi.signed_last_block,
+            })
+        }
+    }
+
+    impl Protobuf<pb::VoteInfo> for VoteInfo {}
+
+    // ExtendedVoteInfo is defined in 0.37, but the vote_extension field is always nil,
+    // so we can omit it from VoteInfo for the time being.
+
+    impl From<VoteInfo> for pb::ExtendedVoteInfo {
+        fn from(vi: VoteInfo) -> Self {
+            Self {
+                validator: Some(vi.validator.into()),
+                signed_last_block: vi.signed_last_block,
+                vote_extension: Default::default(),
+            }
+        }
+    }
+
+    impl TryFrom<pb::ExtendedVoteInfo> for VoteInfo {
+        type Error = Error;
+
+        fn try_from(vi: pb::ExtendedVoteInfo) -> Result<Self, Self::Error> {
+            Ok(Self {
+                validator: vi
+                    .validator
+                    .ok_or_else(Error::missing_validator)?
+                    .try_into()?,
+                signed_last_block: vi.signed_last_block,
+            })
+        }
     }
-}
 
-impl Protobuf<pb::Snapshot> for Snapshot {}
+    impl Protobuf<pb::ExtendedVoteInfo> for VoteInfo {}
+
+    impl From<Misbehavior> for pb::Misbehavior {
+        fn from(evidence: Misbehavior) -> Self {
+            Self {
+                r#type: evidence.kind as i32,
+                validator: Some(evidence.validator.into()),
+                height: evidence.height.into(),
+                time: Some(evidence.time.into()),
+                total_voting_power: evidence.total_voting_power.into(),
+            }
+        }
+    }
+
+    impl TryFrom<pb::Misbehavior> for Misbehavior {
+        type Error = Error;
+
+        fn try_from(evidence: pb::Misbehavior) -> Result<Self, Self::Error> {
+            let kind = match evidence.r#type {
+                0 => MisbehaviorKind::Unknown,
+                1 => MisbehaviorKind::DuplicateVote,
+                2 => MisbehaviorKind::LightClientAttack,
+                _ => return Err(Error::invalid_evidence()),
+            };
+
+            Ok(Self {
+                kind,
+                validator: evidence
+                    .validator
+                    .ok_or_else(Error::missing_validator)?
+                    .try_into()?,
+                height: evidence.height.try_into()?,
+                time: evidence
+                    .time
+                    .ok_or_else(Error::missing_timestamp)?
+                    .try_into()?,
+                total_voting_power: evidence.total_voting_power.try_into()?,
+            })
+        }
+    }
+
+    impl Protobuf<pb::Misbehavior> for Misbehavior {}
+
+    // The CommitInfo domain type represents both CommitInfo and ExtendedCommitInfo
+    // as defined in protobuf for 0.37.
+
+    impl From<CommitInfo> for pb::CommitInfo {
+        fn from(lci: CommitInfo) -> Self {
+            Self {
+                round: lci.round.into(),
+                votes: lci.votes.into_iter().map(Into::into).collect(),
+            }
+        }
+    }
+
+    impl TryFrom<pb::CommitInfo> for CommitInfo {
+        type Error = Error;
+
+        fn try_from(lci: pb::CommitInfo) -> Result<Self, Self::Error> {
+            Ok(Self {
+                round: lci.round.try_into()?,
+                votes: lci
+                    .votes
+                    .into_iter()
+                    .map(TryInto::try_into)
+                    .collect::<Result<_, _>>()?,
+            })
+        }
+    }
+
+    impl Protobuf<pb::CommitInfo> for CommitInfo {}
+
+    impl From<CommitInfo> for pb::ExtendedCommitInfo {
+        fn from(lci: CommitInfo) -> Self {
+            Self {
+                round: lci.round.into(),
+                votes: lci.votes.into_iter().map(Into::into).collect(),
+            }
+        }
+    }
+
+    impl TryFrom<pb::ExtendedCommitInfo> for CommitInfo {
+        type Error = Error;
+
+        fn try_from(lci: pb::ExtendedCommitInfo) -> Result<Self, Self::Error> {
+            Ok(Self {
+                round: lci.round.try_into()?,
+                votes: lci
+                    .votes
+                    .into_iter()
+                    .map(TryInto::try_into)
+                    .collect::<Result<_, _>>()?,
+            })
+        }
+    }
+
+    impl Protobuf<pb::ExtendedCommitInfo> for CommitInfo {}
+
+    impl From<Snapshot> for pb::Snapshot {
+        fn from(snapshot: Snapshot) -> Self {
+            Self {
+                height: snapshot.height.into(),
+                format: snapshot.format,
+                chunks: snapshot.chunks,
+                hash: snapshot.hash,
+                metadata: snapshot.metadata,
+            }
+        }
+    }
+
+    impl TryFrom<pb::Snapshot> for Snapshot {
+        type Error = Error;
+
+        fn try_from(snapshot: pb::Snapshot) -> Result<Self, Self::Error> {
+            Ok(Self {
+                height: snapshot.height.try_into()?,
+                format: snapshot.format,
+                chunks: snapshot.chunks,
+                hash: snapshot.hash,
+                metadata: snapshot.metadata,
+            })
+        }
+    }
+
+    impl Protobuf<pb::Snapshot> for Snapshot {}
+}
diff --git a/tendermint/src/account.rs b/tendermint/src/account.rs
index e01d83074..0fcc8cc71 100644
--- a/tendermint/src/account.rs
+++ b/tendermint/src/account.rs
@@ -6,6 +6,7 @@ use core::{
     str::FromStr,
 };
 
+use bytes::Bytes;
 #[cfg(feature = "secp256k1")]
 use ripemd160::Ripemd160;
 use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@@ -46,6 +47,25 @@ impl From<Id> for Vec<u8> {
     }
 }
 
+impl TryFrom<Bytes> for Id {
+    type Error = Error;
+
+    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
+        if value.len() != LENGTH {
+            return Err(Error::invalid_account_id_length());
+        }
+        let mut slice: [u8; LENGTH] = [0; LENGTH];
+        slice.copy_from_slice(&value[..]);
+        Ok(Id(slice))
+    }
+}
+
+impl From<Id> for Bytes {
+    fn from(value: Id) -> Self {
+        value.as_bytes().into()
+    }
+}
+
 impl Id {
     /// Create a new account ID from raw bytes
     pub fn new(bytes: [u8; LENGTH]) -> Id {
diff --git a/tendermint/src/evidence.rs b/tendermint/src/evidence.rs
index 267ff7806..1bfe70040 100644
--- a/tendermint/src/evidence.rs
+++ b/tendermint/src/evidence.rs
@@ -16,10 +16,7 @@ use tendermint_proto::{
     Protobuf,
 };
 
-use crate::{
-    block::signed_header::SignedHeader, error::Error, prelude::*, serializers, vote::Power, Time,
-    Vote,
-};
+use crate::{error::Error, prelude::*, serializers, vote::Power, Time, Vote};
 
 /// Evidence of malfeasance by validators (i.e. signing conflicting votes).
 /// encoded using an Amino prefix. There is currently only a single type of