Skip to content

Commit

Permalink
feat(proto)!: remove optional fields in protos
Browse files Browse the repository at this point in the history
The cosmos fork of gogoproto, used to generate golang code from BSR,
doesn't support "optional" fields in proto3 syntax. Rather than sort
that out, we're removing use of optional to unblock integration work on
interchaintest.

For the most part, we can still represent optionality by special-casing
zero (for blockheights) and empty (for encrypted memos). The exception
is governance protos, which require use of a submessage type to
represent the optional "withdrawn" state on proposal outcomes.
H/t to @plaidfinch for describing the submessage pattern to me,
and implementing it on the governance changes.

Closes #2933.
  • Loading branch information
conorsch committed Aug 21, 2023
1 parent adb82a2 commit 110440b
Show file tree
Hide file tree
Showing 26 changed files with 2,214 additions and 2,009 deletions.
8 changes: 5 additions & 3 deletions crates/core/component/stake/src/validator/bonding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ impl From<State> for pb::BondingState {
}
},
unbonding_epoch: match v {
State::Unbonding { unbonding_epoch } => Some(unbonding_epoch),
_ => None,
State::Unbonding { unbonding_epoch } => unbonding_epoch,
_ => 0,
},
}
}
Expand All @@ -71,7 +71,9 @@ impl TryFrom<pb::BondingState> for State {
pb::bonding_state::BondingStateEnum::Bonded => Ok(State::Bonded),
pb::bonding_state::BondingStateEnum::Unbonded => Ok(State::Unbonded),
pb::bonding_state::BondingStateEnum::Unbonding => {
let Some(unbonding_epoch) = v.unbonding_epoch else {
let unbonding_epoch = if v.unbonding_epoch > 0 {
v.unbonding_epoch
} else {
anyhow::bail!("unbonding epoch should be set for unbonding state")
};
Ok(State::Unbonding { unbonding_epoch })
Expand Down
64 changes: 50 additions & 14 deletions crates/core/transaction/src/proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,13 @@ impl From<Proposal> for pb::Proposal {
};
match inner.payload {
ProposalPayload::Signaling { commit } => {
proposal.signaling = Some(pb::proposal::Signaling { commit });
proposal.signaling = Some(pb::proposal::Signaling {
commit: if let Some(c) = commit {
c
} else {
String::default()
},
});
}
ProposalPayload::Emergency { halt_chain } => {
proposal.emergency = Some(pb::proposal::Emergency { halt_chain });
Expand Down Expand Up @@ -106,7 +112,11 @@ impl TryFrom<pb::Proposal> for Proposal {
description: inner.description,
payload: if let Some(signaling) = inner.signaling {
ProposalPayload::Signaling {
commit: signaling.commit,
commit: if signaling.commit.is_empty() {
None
} else {
Some(signaling.commit)
},
}
} else if let Some(emergency) = inner.emergency {
ProposalPayload::Emergency {
Expand Down Expand Up @@ -581,12 +591,22 @@ impl From<Outcome<String>> for pb::ProposalOutcome {
}
Outcome::Failed { withdrawn } => {
pb::proposal_outcome::Outcome::Failed(pb::proposal_outcome::Failed {
withdrawn_with_reason: withdrawn.into(),
withdrawn: match withdrawn {
Withdrawn::No => None,
Withdrawn::WithReason { reason } => {
Some(pb::proposal_outcome::Withdrawn { reason })
}
},
})
}
Outcome::Slashed { withdrawn } => {
pb::proposal_outcome::Outcome::Slashed(pb::proposal_outcome::Slashed {
withdrawn_with_reason: withdrawn.into(),
withdrawn: match withdrawn {
Withdrawn::No => None,
Withdrawn::WithReason { reason } => {
Some(pb::proposal_outcome::Withdrawn { reason })
}
},
})
}
};
Expand All @@ -609,14 +629,22 @@ impl TryFrom<pb::ProposalOutcome> for Outcome<String> {
Outcome::Passed
}
pb::proposal_outcome::Outcome::Failed(pb::proposal_outcome::Failed {
withdrawn_with_reason,
withdrawn,
}) => Outcome::Failed {
withdrawn: withdrawn_with_reason.into(),
withdrawn: if let Some(pb::proposal_outcome::Withdrawn { reason }) = withdrawn {
Withdrawn::WithReason { reason }
} else {
Withdrawn::No
},
},
pb::proposal_outcome::Outcome::Slashed(pb::proposal_outcome::Slashed {
withdrawn_with_reason,
withdrawn,
}) => Outcome::Slashed {
withdrawn: withdrawn_with_reason.into(),
withdrawn: if let Some(pb::proposal_outcome::Withdrawn { reason }) = withdrawn {
Withdrawn::WithReason { reason }
} else {
Withdrawn::No
},
},
},
)
Expand All @@ -639,12 +667,20 @@ impl From<Outcome<()>> for pb::ProposalOutcome {
}
Outcome::Failed { withdrawn } => {
pb::proposal_outcome::Outcome::Failed(pb::proposal_outcome::Failed {
withdrawn_with_reason: <Option<()>>::from(withdrawn).map(|()| "".to_string()),
withdrawn: <Option<()>>::from(withdrawn).map(|()| {
pb::proposal_outcome::Withdrawn {
reason: "".to_string(),
}
}),
})
}
Outcome::Slashed { withdrawn } => {
pb::proposal_outcome::Outcome::Slashed(pb::proposal_outcome::Slashed {
withdrawn_with_reason: <Option<()>>::from(withdrawn).map(|()| "".to_string()),
withdrawn: <Option<()>>::from(withdrawn).map(|()| {
pb::proposal_outcome::Withdrawn {
reason: "".to_string(),
}
}),
})
}
};
Expand All @@ -667,14 +703,14 @@ impl TryFrom<pb::ProposalOutcome> for Outcome<()> {
Outcome::Passed
}
pb::proposal_outcome::Outcome::Failed(pb::proposal_outcome::Failed {
withdrawn_with_reason,
withdrawn,
}) => Outcome::Failed {
withdrawn: <Withdrawn<String>>::from(withdrawn_with_reason).try_into()?,
withdrawn: <Withdrawn<String>>::from(withdrawn.map(|w| w.reason)).try_into()?,
},
pb::proposal_outcome::Outcome::Slashed(pb::proposal_outcome::Slashed {
withdrawn_with_reason,
withdrawn,
}) => Outcome::Slashed {
withdrawn: <Withdrawn<String>>::from(withdrawn_with_reason).try_into()?,
withdrawn: <Withdrawn<String>>::from(withdrawn.map(|w| w.reason)).try_into()?,
},
},
)
Expand Down
20 changes: 11 additions & 9 deletions crates/core/transaction/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,10 @@ impl From<TransactionBody> for pbt::TransactionBody {
fn from(msg: TransactionBody) -> Self {
let encrypted_memo: pbt::MemoData = match msg.memo {
Some(memo) => pbt::MemoData {
encrypted_memo: Some(bytes::Bytes::copy_from_slice(&memo.0)),
encrypted_memo: bytes::Bytes::copy_from_slice(&memo.0),
},
None => pbt::MemoData {
encrypted_memo: None,
encrypted_memo: Bytes::default(),
},
};

Expand Down Expand Up @@ -578,17 +578,19 @@ impl TryFrom<pbt::TransactionBody> for TransactionBody {
.try_into()
.context("fee malformed")?;

let memo = match proto
let encrypted_memo = proto
.memo_data
.ok_or_else(|| anyhow::anyhow!("transaction body missing memo data field"))?
.encrypted_memo
{
Some(bytes) => Some(
bytes[..]
.encrypted_memo;

let memo: Option<MemoCiphertext> = if encrypted_memo.is_empty() {
None
} else {
Some(
encrypted_memo[..]
.try_into()
.context("encrypted memo malformed while parsing transaction body")?,
),
None => None,
)
};

let detection_data = match proto.detection_data {
Expand Down
28 changes: 16 additions & 12 deletions crates/proto/src/gen/penumbra.core.governance.v1alpha1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ pub struct ProposalOutcome {
}
/// Nested message and enum types in `ProposalOutcome`.
pub mod proposal_outcome {
/// Whether or not the proposal was withdrawn.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Withdrawn {
/// The reason for withdrawing the proposal during the voting period.
#[prost(string, tag = "1")]
pub reason: ::prost::alloc::string::String,
}
/// The proposal was passed.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
Expand All @@ -281,21 +289,17 @@ pub mod proposal_outcome {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Failed {
/// The proposal was withdrawn during the voting period.
#[prost(string, optional, tag = "1")]
pub withdrawn_with_reason: ::core::option::Option<
::prost::alloc::string::String,
>,
/// Present if the proposal was withdrawn during the voting period.
#[prost(message, optional, tag = "1")]
pub withdrawn: ::core::option::Option<Withdrawn>,
}
/// The proposal did not pass, and was slashed.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Slashed {
/// The proposal was withdrawn during the voting period.
#[prost(string, optional, tag = "1")]
pub withdrawn_with_reason: ::core::option::Option<
::prost::alloc::string::String,
>,
/// Present if the proposal was withdrawn during the voting period.
#[prost(message, optional, tag = "1")]
pub withdrawn: ::core::option::Option<Withdrawn>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
Expand Down Expand Up @@ -355,8 +359,8 @@ pub mod proposal {
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Signaling {
/// The commit to be voted upon, if any is relevant.
#[prost(string, optional, tag = "1")]
pub commit: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "1")]
pub commit: ::prost::alloc::string::String,
}
/// An emergency proposal can be passed instantaneously by a 2/3 majority of validators, without
/// waiting for the voting period to expire.
Expand Down
Loading

0 comments on commit 110440b

Please sign in to comment.