From fae22f94d47a2ad00aff80c157f47350d24997de Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev 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 for Evidence { - fn from(value: EvidenceVariant) -> Self { - match value { - EvidenceVariant::None => Evidence { sum: None }, - _ => Evidence { - sum: Some(value.into()), - }, - } - } - } - - impl From for EvidenceVariant { - fn from(value: Evidence) -> Self { - match value.sum { - Some(sum) => sum.into(), - None => Self::None, - } - } - } - - impl From for EvidenceVariant { - fn from(value: Sum) -> Self { - match value { - Sum::DuplicateVoteEvidence(d) => Self::DuplicateVoteEvidence(d), - Sum::LightClientAttackEvidence(l) => Self::LightClientAttackEvidence(l), - } - } - } - - impl From for Sum { - fn from(value: EvidenceVariant) -> Self { - match value { - // This should never be called - should be handled instead in the - // `impl From 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 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 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 for Request { + type Error = Error; + + fn try_from(request: pb::Request) -> Result { + 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 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, + pub local_last_commit: Option, + pub misbehavior: Vec, + 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 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 for PrepareProposal { + type Error = Error; + + fn try_from(message: pb::RequestPrepareProposal) -> Result { + 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::, _>>()?, + 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 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, + pub proposed_last_commit: Option, + pub misbehavior: Vec, + 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 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 for ProcessProposal { + type Error = Error; + + fn try_from(message: pb::RequestProcessProposal) -> Result { + 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::, _>>()?, + 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 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 for pb::Validator { - fn from(v: Validator) -> Self { - Self { - address: Bytes::copy_from_slice(&v.address[..]), - power: v.power.into(), + impl From for pb::Validator { + fn from(v: Validator) -> Self { + Self { + address: Bytes::copy_from_slice(&v.address[..]), + power: v.power.into(), + } } } -} -impl TryFrom for Validator { - type Error = Error; - - fn try_from(vu: pb::Validator) -> Result { - 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 for Validator { + type Error = Error; + + fn try_from(vu: pb::Validator) -> Result { + 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 for Validator {} + impl Protobuf for Validator {} -impl From for pb::VoteInfo { - fn from(vi: VoteInfo) -> Self { - Self { - validator: Some(vi.validator.into()), - signed_last_block: vi.signed_last_block, + impl From for pb::VoteInfo { + fn from(vi: VoteInfo) -> Self { + Self { + validator: Some(vi.validator.into()), + signed_last_block: vi.signed_last_block, + } } } -} -impl TryFrom for VoteInfo { - type Error = Error; - - fn try_from(vi: pb::VoteInfo) -> Result { - Ok(Self { - validator: vi - .validator - .ok_or_else(Error::missing_validator)? - .try_into()?, - signed_last_block: vi.signed_last_block, - }) + impl TryFrom for VoteInfo { + type Error = Error; + + fn try_from(vi: pb::VoteInfo) -> Result { + Ok(Self { + validator: vi + .validator + .ok_or_else(Error::missing_validator)? + .try_into()?, + signed_last_block: vi.signed_last_block, + }) + } } -} -impl Protobuf for VoteInfo {} + impl Protobuf for VoteInfo {} + + impl From 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 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 for Misbehavior { + type Error = Error; + + fn try_from(evidence: pb::Evidence) -> Result { + 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 for Evidence { - type Error = Error; - - fn try_from(evidence: pb::Evidence) -> Result { - 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 for Misbehavior {} + + impl From for pb::LastCommitInfo { + fn from(lci: CommitInfo) -> Self { + Self { + round: lci.round.into(), + votes: lci.votes.into_iter().map(Into::into).collect(), + } + } } -} -impl Protobuf for Evidence {} + impl TryFrom for CommitInfo { + type Error = Error; + + fn try_from(lci: pb::LastCommitInfo) -> Result { + Ok(Self { + round: lci.round.try_into()?, + votes: lci + .votes + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } + } -impl From for pb::LastCommitInfo { - fn from(lci: LastCommitInfo) -> Self { - Self { - round: lci.round.into(), - votes: lci.votes.into_iter().map(Into::into).collect(), + impl Protobuf for CommitInfo {} + + impl From 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 for LastCommitInfo { - type Error = Error; - - fn try_from(lci: pb::LastCommitInfo) -> Result { - Ok(Self { - round: lci.round.try_into()?, - votes: lci - .votes - .into_iter() - .map(TryInto::try_into) - .collect::>()?, - }) + impl TryFrom for Snapshot { + type Error = Error; + + fn try_from(snapshot: pb::Snapshot) -> Result { + Ok(Self { + height: snapshot.height.try_into()?, + format: snapshot.format, + chunks: snapshot.chunks, + hash: snapshot.hash, + metadata: snapshot.metadata, + }) + } } + + impl Protobuf for Snapshot {} } -impl Protobuf 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 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 for pb::Validator { + fn from(v: Validator) -> Self { + Self { + address: Bytes::copy_from_slice(&v.address[..]), + power: v.power.into(), + } } } -} -impl TryFrom for Snapshot { - type Error = Error; - - fn try_from(snapshot: pb::Snapshot) -> Result { - Ok(Self { - height: snapshot.height.try_into()?, - format: snapshot.format, - chunks: snapshot.chunks, - hash: snapshot.hash, - metadata: snapshot.metadata, - }) + impl TryFrom for Validator { + type Error = Error; + + fn try_from(vu: pb::Validator) -> Result { + 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 for Validator {} + + impl From for pb::VoteInfo { + fn from(vi: VoteInfo) -> Self { + Self { + validator: Some(vi.validator.into()), + signed_last_block: vi.signed_last_block, + } + } + } + + impl TryFrom for VoteInfo { + type Error = Error; + + fn try_from(vi: pb::VoteInfo) -> Result { + Ok(Self { + validator: vi + .validator + .ok_or_else(Error::missing_validator)? + .try_into()?, + signed_last_block: vi.signed_last_block, + }) + } + } + + impl Protobuf 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 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 for VoteInfo { + type Error = Error; + + fn try_from(vi: pb::ExtendedVoteInfo) -> Result { + Ok(Self { + validator: vi + .validator + .ok_or_else(Error::missing_validator)? + .try_into()?, + signed_last_block: vi.signed_last_block, + }) + } } -} -impl Protobuf for Snapshot {} + impl Protobuf for VoteInfo {} + + impl From 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 for Misbehavior { + type Error = Error; + + fn try_from(evidence: pb::Misbehavior) -> Result { + 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 for Misbehavior {} + + // The CommitInfo domain type represents both CommitInfo and ExtendedCommitInfo + // as defined in protobuf for 0.37. + + impl From for pb::CommitInfo { + fn from(lci: CommitInfo) -> Self { + Self { + round: lci.round.into(), + votes: lci.votes.into_iter().map(Into::into).collect(), + } + } + } + + impl TryFrom for CommitInfo { + type Error = Error; + + fn try_from(lci: pb::CommitInfo) -> Result { + Ok(Self { + round: lci.round.try_into()?, + votes: lci + .votes + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } + } + + impl Protobuf for CommitInfo {} + + impl From for pb::ExtendedCommitInfo { + fn from(lci: CommitInfo) -> Self { + Self { + round: lci.round.into(), + votes: lci.votes.into_iter().map(Into::into).collect(), + } + } + } + + impl TryFrom for CommitInfo { + type Error = Error; + + fn try_from(lci: pb::ExtendedCommitInfo) -> Result { + Ok(Self { + round: lci.round.try_into()?, + votes: lci + .votes + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } + } + + impl Protobuf for CommitInfo {} + + impl From 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 for Snapshot { + type Error = Error; + + fn try_from(snapshot: pb::Snapshot) -> Result { + Ok(Self { + height: snapshot.height.try_into()?, + format: snapshot.format, + chunks: snapshot.chunks, + hash: snapshot.hash, + metadata: snapshot.metadata, + }) + } + } + + impl Protobuf 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 for Vec { } } +impl TryFrom for Id { + type Error = Error; + + fn try_from(value: Bytes) -> Result { + 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 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