From 5d7a9d652f4fcad7ba78878ed10451fe490636a0 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 15 Nov 2024 16:23:47 +0200 Subject: [PATCH 01/28] refactor --- rust/vote-tx-v2/src/{ => gen_tx}/decoding.rs | 2 +- rust/vote-tx-v2/src/gen_tx/mod.rs | 92 ++++++++++++++++++++ rust/vote-tx-v2/src/lib.rs | 92 +------------------- 3 files changed, 97 insertions(+), 89 deletions(-) rename rust/vote-tx-v2/src/{ => gen_tx}/decoding.rs (99%) create mode 100644 rust/vote-tx-v2/src/gen_tx/mod.rs diff --git a/rust/vote-tx-v2/src/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs similarity index 99% rename from rust/vote-tx-v2/src/decoding.rs rename to rust/vote-tx-v2/src/gen_tx/decoding.rs index 213323cfce8..343eaca8220 100644 --- a/rust/vote-tx-v2/src/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -7,7 +7,7 @@ use minicbor::{ Decode, Decoder, Encode, Encoder, }; -use crate::{ +use super::{ Choice, EventKey, EventMap, GeneralizedTx, Proof, PropId, TxBody, Uuid, Vote, VoterData, }; diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs new file mode 100644 index 00000000000..c64673c3eb3 --- /dev/null +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -0,0 +1,92 @@ +//! A Catalyst generalised vote transaction object, structured following this +//! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/gen_vote_tx/) + +// cspell: words Coap + +mod decoding; + +use minicbor::data::Int; + +/// A generalized tx struct. +#[derive(Debug, Clone, PartialEq)] +pub struct GeneralizedTx { + /// `tx-body` field + tx_body: TxBody, + /// `signature` field + signature: coset::CoseSign, +} + +/// A tx body struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TxBody { + /// `vote-type` field + vote_type: Uuid, + /// `event` field + event: EventMap, + /// `votes` field + votes: Vec, + /// `voter-data` field + voter_data: VoterData, +} + +/// A vote struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Vote { + /// `choices` field + choices: Vec, + /// `proof` field + proof: Proof, + /// `prop-id` field + prop_id: PropId, +} + +/// A CBOR map +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EventMap(Vec<(EventKey, Vec)>); + +/// An `event-key` type definition. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EventKey { + /// CBOR `int` type + Int(Int), + /// CBOR `text` type + Text(String), +} + +/// A UUID struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Uuid(Vec); + +/// A voter's data struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VoterData(Vec); + +/// A choice struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Choice(Vec); + +/// A proof struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Proof(Vec); + +/// A prop id struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PropId(Vec); + +impl GeneralizedTx { + /// Creates a new `GeneralizedTx` struct. + #[must_use] + pub fn new(tx_body: TxBody) -> Self { + let signature = coset::CoseSignBuilder::new() + .protected(Self::cose_protected_header()) + .build(); + Self { tx_body, signature } + } + + /// Returns the COSE protected header. + fn cose_protected_header() -> coset::Header { + coset::HeaderBuilder::new() + .content_format(coset::iana::CoapContentFormat::Cbor) + .build() + } +} diff --git a/rust/vote-tx-v2/src/lib.rs b/rust/vote-tx-v2/src/lib.rs index 4d3bfa887c7..8df0f917809 100644 --- a/rust/vote-tx-v2/src/lib.rs +++ b/rust/vote-tx-v2/src/lib.rs @@ -1,96 +1,12 @@ -//! A Catalyst vote transaction v1 object, structured following this -//! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/v2/) +//! A Catalyst vote transaction v2 objects, structured following this +//! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/) // cspell: words Coap use anyhow::anyhow; -use minicbor::{data::Int, Decode, Decoder, Encode, Encoder}; +use minicbor::{Decode, Decoder, Encode, Encoder}; -mod decoding; - -/// A generalized tx struct. -#[derive(Debug, Clone, PartialEq)] -pub struct GeneralizedTx { - /// `tx-body` field - tx_body: TxBody, - /// `signature` field - signature: coset::CoseSign, -} - -/// A tx body struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TxBody { - /// `vote-type` field - vote_type: Uuid, - /// `event` field - event: EventMap, - /// `votes` field - votes: Vec, - /// `voter-data` field - voter_data: VoterData, -} - -/// A vote struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Vote { - /// `choices` field - choices: Vec, - /// `proof` field - proof: Proof, - /// `prop-id` field - prop_id: PropId, -} - -/// A CBOR map -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EventMap(Vec<(EventKey, Vec)>); - -/// An `event-key` type definition. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum EventKey { - /// CBOR `int` type - Int(Int), - /// CBOR `text` type - Text(String), -} - -/// A UUID struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Uuid(Vec); - -/// A voter's data struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VoterData(Vec); - -/// A choice struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Choice(Vec); - -/// A proof struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Proof(Vec); - -/// A prop id struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PropId(Vec); - -impl GeneralizedTx { - /// Creates a new `GeneralizedTx` struct. - #[must_use] - pub fn new(tx_body: TxBody) -> Self { - let signature = coset::CoseSignBuilder::new() - .protected(Self::cose_protected_header()) - .build(); - Self { tx_body, signature } - } - - /// Returns the COSE protected header. - fn cose_protected_header() -> coset::Header { - coset::HeaderBuilder::new() - .content_format(coset::iana::CoapContentFormat::Cbor) - .build() - } -} +pub mod gen_tx; /// Cbor encodable and decodable type trait. pub trait Cbor<'a>: Encode<()> + Decode<'a, ()> { From 3b53b887c54dd87e2dcfe7888b4d14404336d406 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 15 Nov 2024 16:26:49 +0200 Subject: [PATCH 02/28] add public_tx mod --- rust/vote-tx-v2/src/lib.rs | 1 + rust/vote-tx-v2/src/public_tx/decoding.rs | 2 ++ rust/vote-tx-v2/src/public_tx/mod.rs | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 rust/vote-tx-v2/src/public_tx/decoding.rs create mode 100644 rust/vote-tx-v2/src/public_tx/mod.rs diff --git a/rust/vote-tx-v2/src/lib.rs b/rust/vote-tx-v2/src/lib.rs index 8df0f917809..c094a80df40 100644 --- a/rust/vote-tx-v2/src/lib.rs +++ b/rust/vote-tx-v2/src/lib.rs @@ -7,6 +7,7 @@ use anyhow::anyhow; use minicbor::{Decode, Decoder, Encode, Encoder}; pub mod gen_tx; +pub mod public_tx; /// Cbor encodable and decodable type trait. pub trait Cbor<'a>: Encode<()> + Decode<'a, ()> { diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs new file mode 100644 index 00000000000..1e8d4056601 --- /dev/null +++ b/rust/vote-tx-v2/src/public_tx/decoding.rs @@ -0,0 +1,2 @@ +//! CBOR encoding and decoding implementation. +//! diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs new file mode 100644 index 00000000000..7d98b29a6aa --- /dev/null +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -0,0 +1,4 @@ +//! A Catalyst public vote transaction v2 object, structured following this +//! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/v2/#public-vote) + +mod decoding; From d47b09ee19a972e5c95c23826c5e430e56e8be20 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Fri, 15 Nov 2024 16:32:51 +0200 Subject: [PATCH 03/28] add initial PublicTx impl --- rust/vote-tx-v2/src/public_tx/decoding.rs | 19 ++++++++++++++++ rust/vote-tx-v2/src/public_tx/mod.rs | 27 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs index 1e8d4056601..27b67c270a3 100644 --- a/rust/vote-tx-v2/src/public_tx/decoding.rs +++ b/rust/vote-tx-v2/src/public_tx/decoding.rs @@ -1,2 +1,21 @@ //! CBOR encoding and decoding implementation. //! + +use minicbor::{Decode, Encode}; + +use super::{GeneralizedTx, PublicTx}; + +impl Decode<'_, ()> for PublicTx { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let gen_tx = GeneralizedTx::decode(d, &mut ())?; + Ok(PublicTx(gen_tx)) + } +} + +impl Encode<()> for PublicTx { + fn encode( + &self, e: &mut minicbor::Encoder, ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, ctx) + } +} diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index 7d98b29a6aa..c06c726927e 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -1,4 +1,31 @@ //! A Catalyst public vote transaction v2 object, structured following this //! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/v2/#public-vote) +use std::ops::{Deref, DerefMut}; + +use crate::gen_tx::{GeneralizedTx, Uuid}; + mod decoding; + +/// A public vote tx struct. +pub struct PublicTx(GeneralizedTx); + +/// A public voting choice struct. +pub struct Choice(pub u64); + +/// A public voting proposal struct. +pub struct Proposal(pub Uuid); + +impl Deref for PublicTx { + type Target = GeneralizedTx; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for PublicTx { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} From 90208cd16936ab17b2b5065bae253f26c05a8458 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Sat, 16 Nov 2024 20:53:11 +0200 Subject: [PATCH 04/28] make gen_tx::Choice generic --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 49 +++++++++++++++++------ rust/vote-tx-v2/src/gen_tx/mod.rs | 29 +++++++++----- rust/vote-tx-v2/src/public_tx/decoding.rs | 17 +++++++- rust/vote-tx-v2/src/public_tx/mod.rs | 4 +- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index 343eaca8220..555ee485aa6 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -10,6 +10,7 @@ use minicbor::{ use super::{ Choice, EventKey, EventMap, GeneralizedTx, Proof, PropId, TxBody, Uuid, Vote, VoterData, }; +use crate::Cbor; /// UUID CBOR tag . const CBOR_UUID_TAG: u64 = 37; @@ -23,7 +24,9 @@ const TX_BODY_LEN: u64 = 4; /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; -impl Decode<'_, ()> for GeneralizedTx { +impl Decode<'_, ()> for GeneralizedTx +where ChoiceT: for<'a> Cbor<'a> +{ fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(GENERALIZED_TX_LEN) = d.array()? else { return Err(minicbor::decode::Error::message(format!( @@ -48,7 +51,9 @@ impl Decode<'_, ()> for GeneralizedTx { } } -impl Encode<()> for GeneralizedTx { +impl Encode<()> for GeneralizedTx +where ChoiceT: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { @@ -68,7 +73,9 @@ impl Encode<()> for GeneralizedTx { } } -impl Decode<'_, ()> for TxBody { +impl Decode<'_, ()> for TxBody +where ChoiceT: for<'a> Cbor<'a> +{ fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(TX_BODY_LEN) = d.array()? else { return Err(minicbor::decode::Error::message(format!( @@ -78,7 +85,7 @@ impl Decode<'_, ()> for TxBody { let vote_type = Uuid::decode(d, &mut ())?; let event = EventMap::decode(d, &mut ())?; - let votes = Vec::::decode(d, &mut ())?; + let votes = Vec::>::decode(d, &mut ())?; let voter_data = VoterData::decode(d, &mut ())?; Ok(Self { vote_type, @@ -89,7 +96,9 @@ impl Decode<'_, ()> for TxBody { } } -impl Encode<()> for TxBody { +impl Encode<()> for TxBody +where ChoiceT: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { @@ -220,7 +229,9 @@ impl Encode<()> for Uuid { } } -impl Decode<'_, ()> for Vote { +impl Decode<'_, ()> for Vote +where ChoiceT: for<'a> Cbor<'a> +{ fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(VOTE_LEN) = d.array()? else { return Err(minicbor::decode::Error::message(format!( @@ -228,7 +239,7 @@ impl Decode<'_, ()> for Vote { ))); }; - let choices = Vec::::decode(d, &mut ())?; + let choices = Vec::>::decode(d, &mut ())?; if choices.is_empty() { return Err(minicbor::decode::Error::message( "choices array must has at least one entry", @@ -244,7 +255,9 @@ impl Decode<'_, ()> for Vote { } } -impl Encode<()> for Vote { +impl Encode<()> for Vote +where ChoiceT: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { @@ -256,7 +269,9 @@ impl Encode<()> for Vote { } } -impl Decode<'_, ()> for Choice { +impl Decode<'_, ()> for Choice +where ChoiceT: for<'a> Cbor<'a> +{ fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; let expected_tag = minicbor::data::IanaTag::Cbor.tag(); @@ -267,17 +282,27 @@ impl Decode<'_, ()> for Choice { tag.as_u64(), ))); } - let choice = d.bytes()?.to_vec(); + let choice_bytes = d.bytes()?.to_vec(); + let choice = + ChoiceT::from_bytes(&choice_bytes).map_err(minicbor::decode::Error::message)?; Ok(Self(choice)) } } -impl Encode<()> for Choice { +impl Encode<()> for Choice +where ChoiceT: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { e.tag(IanaTag::Cbor.tag())?; - e.bytes(&self.0)?; + + let choice_bytes = self + .0 + .to_bytes() + .map_err(minicbor::encode::Error::message)?; + + e.bytes(&choice_bytes)?; Ok(()) } } diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index c64673c3eb3..13eff229e4e 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -7,33 +7,41 @@ mod decoding; use minicbor::data::Int; +use crate::Cbor; + /// A generalized tx struct. #[derive(Debug, Clone, PartialEq)] -pub struct GeneralizedTx { +pub struct GeneralizedTx +where ChoiceT: for<'a> Cbor<'a> +{ /// `tx-body` field - tx_body: TxBody, + tx_body: TxBody, /// `signature` field signature: coset::CoseSign, } /// A tx body struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TxBody { +pub struct TxBody +where ChoiceT: for<'a> Cbor<'a> +{ /// `vote-type` field vote_type: Uuid, /// `event` field event: EventMap, /// `votes` field - votes: Vec, + votes: Vec>, /// `voter-data` field voter_data: VoterData, } /// A vote struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Vote { +pub struct Vote +where ChoiceT: for<'a> Cbor<'a> +{ /// `choices` field - choices: Vec, + choices: Vec>, /// `proof` field proof: Proof, /// `prop-id` field @@ -63,7 +71,8 @@ pub struct VoterData(Vec); /// A choice struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Choice(Vec); +pub struct Choice(T) +where T: for<'a> Cbor<'a>; /// A proof struct. #[derive(Debug, Clone, PartialEq, Eq)] @@ -73,10 +82,12 @@ pub struct Proof(Vec); #[derive(Debug, Clone, PartialEq, Eq)] pub struct PropId(Vec); -impl GeneralizedTx { +impl GeneralizedTx +where ChoiceT: for<'a> Cbor<'a> +{ /// Creates a new `GeneralizedTx` struct. #[must_use] - pub fn new(tx_body: TxBody) -> Self { + pub fn new(tx_body: TxBody) -> Self { let signature = coset::CoseSignBuilder::new() .protected(Self::cose_protected_header()) .build(); diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs index 27b67c270a3..1ec27ec8910 100644 --- a/rust/vote-tx-v2/src/public_tx/decoding.rs +++ b/rust/vote-tx-v2/src/public_tx/decoding.rs @@ -3,7 +3,7 @@ use minicbor::{Decode, Encode}; -use super::{GeneralizedTx, PublicTx}; +use super::{Choice, GeneralizedTx, PublicTx}; impl Decode<'_, ()> for PublicTx { fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { @@ -19,3 +19,18 @@ impl Encode<()> for PublicTx { self.0.encode(e, ctx) } } + +impl Decode<'_, ()> for Choice { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let choice = d.u64()?; + Ok(Choice(choice)) + } +} + +impl Encode<()> for Choice { + fn encode( + &self, e: &mut minicbor::Encoder, ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, ctx) + } +} diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index c06c726927e..dc65f631ced 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -8,7 +8,7 @@ use crate::gen_tx::{GeneralizedTx, Uuid}; mod decoding; /// A public vote tx struct. -pub struct PublicTx(GeneralizedTx); +pub struct PublicTx(GeneralizedTx); /// A public voting choice struct. pub struct Choice(pub u64); @@ -17,7 +17,7 @@ pub struct Choice(pub u64); pub struct Proposal(pub Uuid); impl Deref for PublicTx { - type Target = GeneralizedTx; + type Target = GeneralizedTx; fn deref(&self) -> &Self::Target { &self.0 From 5ea15166e4e8c380442ad9f77d82365cc44572e5 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Sun, 17 Nov 2024 17:38:26 +0200 Subject: [PATCH 05/28] make gen_tx::PropId and gen_tx::Proof generic --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 95 +++++++++++++++-------- rust/vote-tx-v2/src/gen_tx/mod.rs | 44 +++++++---- rust/vote-tx-v2/src/public_tx/decoding.rs | 41 ++++++++-- rust/vote-tx-v2/src/public_tx/mod.rs | 11 ++- 4 files changed, 136 insertions(+), 55 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index 555ee485aa6..276f4785d02 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -24,8 +24,11 @@ const TX_BODY_LEN: u64 = 4; /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; -impl Decode<'_, ()> for GeneralizedTx -where ChoiceT: for<'a> Cbor<'a> +impl Decode<'_, ()> for GeneralizedTx +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(GENERALIZED_TX_LEN) = d.array()? else { @@ -51,8 +54,11 @@ where ChoiceT: for<'a> Cbor<'a> } } -impl Encode<()> for GeneralizedTx -where ChoiceT: for<'a> Cbor<'a> +impl Encode<()> for GeneralizedTx +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, { fn encode( &self, e: &mut Encoder, (): &mut (), @@ -73,8 +79,11 @@ where ChoiceT: for<'a> Cbor<'a> } } -impl Decode<'_, ()> for TxBody -where ChoiceT: for<'a> Cbor<'a> +impl Decode<'_, ()> for TxBody +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(TX_BODY_LEN) = d.array()? else { @@ -85,7 +94,7 @@ where ChoiceT: for<'a> Cbor<'a> let vote_type = Uuid::decode(d, &mut ())?; let event = EventMap::decode(d, &mut ())?; - let votes = Vec::>::decode(d, &mut ())?; + let votes = Vec::>::decode(d, &mut ())?; let voter_data = VoterData::decode(d, &mut ())?; Ok(Self { vote_type, @@ -96,8 +105,11 @@ where ChoiceT: for<'a> Cbor<'a> } } -impl Encode<()> for TxBody -where ChoiceT: for<'a> Cbor<'a> +impl Encode<()> for TxBody +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, { fn encode( &self, e: &mut Encoder, (): &mut (), @@ -229,8 +241,11 @@ impl Encode<()> for Uuid { } } -impl Decode<'_, ()> for Vote -where ChoiceT: for<'a> Cbor<'a> +impl Decode<'_, ()> for Vote +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(VOTE_LEN) = d.array()? else { @@ -255,8 +270,11 @@ where ChoiceT: for<'a> Cbor<'a> } } -impl Encode<()> for Vote -where ChoiceT: for<'a> Cbor<'a> +impl Encode<()> for Vote +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, { fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), @@ -269,8 +287,8 @@ where ChoiceT: for<'a> Cbor<'a> } } -impl Decode<'_, ()> for Choice -where ChoiceT: for<'a> Cbor<'a> +impl Decode<'_, ()> for Choice +where T: for<'a> Cbor<'a> { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; @@ -283,31 +301,30 @@ where ChoiceT: for<'a> Cbor<'a> ))); } let choice_bytes = d.bytes()?.to_vec(); - let choice = - ChoiceT::from_bytes(&choice_bytes).map_err(minicbor::decode::Error::message)?; + let choice = T::from_bytes(&choice_bytes).map_err(minicbor::decode::Error::message)?; Ok(Self(choice)) } } -impl Encode<()> for Choice -where ChoiceT: for<'a> Cbor<'a> +impl Encode<()> for Choice +where T: for<'a> Cbor<'a> { fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { e.tag(IanaTag::Cbor.tag())?; - let choice_bytes = self .0 .to_bytes() .map_err(minicbor::encode::Error::message)?; - e.bytes(&choice_bytes)?; Ok(()) } } -impl Decode<'_, ()> for Proof { +impl Decode<'_, ()> for Proof +where T: for<'a> Cbor<'a> +{ fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; let expected_tag = minicbor::data::IanaTag::Cbor.tag(); @@ -318,22 +335,31 @@ impl Decode<'_, ()> for Proof { tag.as_u64(), ))); } - let choice = d.bytes()?.to_vec(); - Ok(Self(choice)) + let proof_bytes = d.bytes()?.to_vec(); + let proof = T::from_bytes(&proof_bytes).map_err(minicbor::decode::Error::message)?; + Ok(Self(proof)) } } -impl Encode<()> for Proof { +impl Encode<()> for Proof +where T: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { e.tag(IanaTag::Cbor.tag())?; - e.bytes(&self.0)?; + let proof_bytes = self + .0 + .to_bytes() + .map_err(minicbor::encode::Error::message)?; + e.bytes(&proof_bytes)?; Ok(()) } } -impl Decode<'_, ()> for PropId { +impl Decode<'_, ()> for PropId +where T: for<'a> Cbor<'a> +{ fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; let expected_tag = IanaTag::Cbor.tag(); @@ -344,17 +370,24 @@ impl Decode<'_, ()> for PropId { tag.as_u64(), ))); } - let choice = d.bytes()?.to_vec(); - Ok(Self(choice)) + let prop_id_bytes = d.bytes()?.to_vec(); + let prop_id = T::from_bytes(&prop_id_bytes).map_err(minicbor::decode::Error::message)?; + Ok(Self(prop_id)) } } -impl Encode<()> for PropId { +impl Encode<()> for PropId +where T: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { e.tag(IanaTag::Cbor.tag())?; - e.bytes(&self.0)?; + let prop_id_bytes = self + .0 + .to_bytes() + .map_err(minicbor::encode::Error::message)?; + e.bytes(&prop_id_bytes)?; Ok(()) } } diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 13eff229e4e..d3385a2cca4 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -11,41 +11,50 @@ use crate::Cbor; /// A generalized tx struct. #[derive(Debug, Clone, PartialEq)] -pub struct GeneralizedTx -where ChoiceT: for<'a> Cbor<'a> +pub struct GeneralizedTx +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, { /// `tx-body` field - tx_body: TxBody, + tx_body: TxBody, /// `signature` field signature: coset::CoseSign, } /// A tx body struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TxBody -where ChoiceT: for<'a> Cbor<'a> +pub struct TxBody +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, { /// `vote-type` field vote_type: Uuid, /// `event` field event: EventMap, /// `votes` field - votes: Vec>, + votes: Vec>, /// `voter-data` field voter_data: VoterData, } /// A vote struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Vote -where ChoiceT: for<'a> Cbor<'a> +pub struct Vote +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, { /// `choices` field choices: Vec>, /// `proof` field - proof: Proof, + proof: Proof, /// `prop-id` field - prop_id: PropId, + prop_id: PropId, } /// A CBOR map @@ -76,18 +85,23 @@ where T: for<'a> Cbor<'a>; /// A proof struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Proof(Vec); +pub struct Proof(T) +where T: for<'a> Cbor<'a>; /// A prop id struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct PropId(Vec); +pub struct PropId(T) +where T: for<'a> Cbor<'a>; -impl GeneralizedTx -where ChoiceT: for<'a> Cbor<'a> +impl GeneralizedTx +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, { /// Creates a new `GeneralizedTx` struct. #[must_use] - pub fn new(tx_body: TxBody) -> Self { + pub fn new(tx_body: TxBody) -> Self { let signature = coset::CoseSignBuilder::new() .protected(Self::cose_protected_header()) .build(); diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs index 1ec27ec8910..1a19b055bf3 100644 --- a/rust/vote-tx-v2/src/public_tx/decoding.rs +++ b/rust/vote-tx-v2/src/public_tx/decoding.rs @@ -3,12 +3,12 @@ use minicbor::{Decode, Encode}; -use super::{Choice, GeneralizedTx, PublicTx}; +use super::{Choice, GeneralizedTx, Proof, PropId, PublicTx, Uuid}; impl Decode<'_, ()> for PublicTx { fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { let gen_tx = GeneralizedTx::decode(d, &mut ())?; - Ok(PublicTx(gen_tx)) + Ok(Self(gen_tx)) } } @@ -23,14 +23,45 @@ impl Encode<()> for PublicTx { impl Decode<'_, ()> for Choice { fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { let choice = d.u64()?; - Ok(Choice(choice)) + Ok(Self(choice)) } } impl Encode<()> for Choice { fn encode( - &self, e: &mut minicbor::Encoder, ctx: &mut (), + &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, ctx) + self.0.encode(e, &mut ()) + } +} + +impl Decode<'_, ()> for Proof { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + d.undefined()?; + Ok(Self) + } +} + +impl Encode<()> for Proof { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.undefined()?; + Ok(()) + } +} + +impl Decode<'_, ()> for PropId { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let prop_id = Uuid::decode(d, &mut ())?; + Ok(Self(prop_id)) + } +} + +impl Encode<()> for PropId { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, &mut ()) } } diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index dc65f631ced..4f067d44df1 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -8,16 +8,19 @@ use crate::gen_tx::{GeneralizedTx, Uuid}; mod decoding; /// A public vote tx struct. -pub struct PublicTx(GeneralizedTx); +pub struct PublicTx(GeneralizedTx); /// A public voting choice struct. pub struct Choice(pub u64); -/// A public voting proposal struct. -pub struct Proposal(pub Uuid); +/// A public voting proof struct, CBOR `undefined`. +pub struct Proof; + +/// A public voting proposal id struct. +pub struct PropId(pub Uuid); impl Deref for PublicTx { - type Target = GeneralizedTx; + type Target = GeneralizedTx; fn deref(&self) -> &Self::Target { &self.0 From 242e5e0aba4b61f51e680995e7986205938a4148 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 14:09:19 +0200 Subject: [PATCH 06/28] add more gen_tx decoding tests --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 106 ++++++++++++++++++++----- 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index 276f4785d02..dd645027bc7 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -95,6 +95,11 @@ where let vote_type = Uuid::decode(d, &mut ())?; let event = EventMap::decode(d, &mut ())?; let votes = Vec::>::decode(d, &mut ())?; + if votes.is_empty() { + return Err(minicbor::decode::Error::message( + "votes array must has at least one entry", + )); + } let voter_data = VoterData::decode(d, &mut ())?; Ok(Self { vote_type, @@ -414,8 +419,11 @@ mod tests { use super::*; use crate::Cbor; - type PropChoice = Vec; - type PropVote = (Vec, Vec, Vec); + type ChoiceT = Vec; + type ProofT = Vec; + type PropIdT = Vec; + + type PropVote = (Vec, ProofT, PropIdT); #[derive(Debug, Arbitrary)] enum PropEventKey { @@ -424,6 +432,16 @@ mod tests { I64(i64), } + impl From for EventKey { + fn from(key: PropEventKey) -> Self { + match key { + PropEventKey::Text(text) => EventKey::Text(text), + PropEventKey::U64(val) => EventKey::Int(val.into()), + PropEventKey::I64(val) => EventKey::Int(val.into()), + } + } + } + #[proptest] fn generalized_tx_from_bytes_to_bytes_test( vote_type: Vec, @@ -443,35 +461,85 @@ mod tests { let event = event .into_iter() .map(|(key, val)| { - let key = match key { - PropEventKey::Text(key) => EventKey::Text(key), - PropEventKey::U64(val) => EventKey::Int(val.into()), - PropEventKey::I64(val) => EventKey::Int(val.into()), - }; + let key = key.into(); let value = val.to_bytes().unwrap(); (key, value) }) .collect(); + let votes = votes + .into_iter() + .map(|(choices, proof, prop_id)| { + Vote { + choices: choices.into_iter().map(Choice).collect(), + proof: Proof(proof), + prop_id: PropId(prop_id), + } + }) + .collect(); let tx_body = TxBody { vote_type: Uuid(vote_type), event: EventMap(event), - votes: votes - .into_iter() - .map(|(choices, proof, prop_id)| { - Vote { - choices: choices.into_iter().map(Choice).collect(), - proof: Proof(proof), - prop_id: PropId(prop_id), - } - }) - .collect(), + votes, voter_data: VoterData(voter_data), }; - let generalized_tx = GeneralizedTx::new(tx_body); + let generalized_tx = GeneralizedTx::::new(tx_body); let bytes = generalized_tx.to_bytes().unwrap(); - let decoded = GeneralizedTx::from_bytes(&bytes).unwrap(); + let decoded = GeneralizedTx::::from_bytes(&bytes).unwrap(); assert_eq!(generalized_tx, decoded); } + + #[proptest] + fn invalid_generalized_tx_from_bytes_to_bytes_test( + vote_type: Vec, votes: Vec, event: Vec<(PropEventKey, u64)>, + voter_data: Vec, + ) { + let event: Vec<_> = event + .into_iter() + .map(|(key, val)| { + let key = key.into(); + let value = val.to_bytes().unwrap(); + (key, value) + }) + .collect(); + // Empty votes case + { + let empty_votes = Vec::>::new(); + let tx_body = TxBody { + vote_type: Uuid(vote_type.clone()), + event: EventMap(event.clone()), + votes: empty_votes, + voter_data: VoterData(voter_data.clone()), + }; + + let generalized_tx = GeneralizedTx::new(tx_body); + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + } + + // Empty choices case + { + let votes_with_empty_choices = votes + .into_iter() + .map(|(_, proof, prop_id)| { + Vote { + choices: Vec::>::new(), + proof: Proof(proof), + prop_id: PropId(prop_id), + } + }) + .collect(); + let tx_body = TxBody { + vote_type: Uuid(vote_type), + event: EventMap(event), + votes: votes_with_empty_choices, + voter_data: VoterData(voter_data), + }; + + let generalized_tx = GeneralizedTx::new(tx_body); + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + } + } } From 73a49b9029f1d26dcfa38eba30c07050e5ef325d Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 14:33:21 +0200 Subject: [PATCH 07/28] add separate tx_body mod --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 52 ------------------ rust/vote-tx-v2/src/gen_tx/mod.rs | 22 +------- rust/vote-tx-v2/src/gen_tx/tx_body.rs | 76 ++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 71 deletions(-) create mode 100644 rust/vote-tx-v2/src/gen_tx/tx_body.rs diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index dd645027bc7..71de0ac34e4 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -18,9 +18,6 @@ const CBOR_UUID_TAG: u64 = 37; /// `Vote` array struct length const VOTE_LEN: u64 = 3; -/// `TxBody` array struct length -const TX_BODY_LEN: u64 = 4; - /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; @@ -79,55 +76,6 @@ where } } -impl Decode<'_, ()> for TxBody -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - PropIdT: for<'a> Cbor<'a>, -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let Some(TX_BODY_LEN) = d.array()? else { - return Err(minicbor::decode::Error::message(format!( - "must be a defined sized array with {GENERALIZED_TX_LEN} entries" - ))); - }; - - let vote_type = Uuid::decode(d, &mut ())?; - let event = EventMap::decode(d, &mut ())?; - let votes = Vec::>::decode(d, &mut ())?; - if votes.is_empty() { - return Err(minicbor::decode::Error::message( - "votes array must has at least one entry", - )); - } - let voter_data = VoterData::decode(d, &mut ())?; - Ok(Self { - vote_type, - event, - votes, - voter_data, - }) - } -} - -impl Encode<()> for TxBody -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - PropIdT: for<'a> Cbor<'a>, -{ - fn encode( - &self, e: &mut Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.array(TX_BODY_LEN)?; - self.vote_type.encode(e, &mut ())?; - self.event.encode(e, &mut ())?; - self.votes.encode(e, &mut ())?; - self.voter_data.encode(e, &mut ())?; - Ok(()) - } -} - impl Decode<'_, ()> for EventMap { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(len) = d.map()? else { diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index d3385a2cca4..3db478385b4 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -4,8 +4,10 @@ // cspell: words Coap mod decoding; +mod tx_body; use minicbor::data::Int; +pub use tx_body::TxBody; use crate::Cbor; @@ -23,24 +25,6 @@ where signature: coset::CoseSign, } -/// A tx body struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TxBody -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, -{ - /// `vote-type` field - vote_type: Uuid, - /// `event` field - event: EventMap, - /// `votes` field - votes: Vec>, - /// `voter-data` field - voter_data: VoterData, -} - /// A vote struct. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Vote @@ -58,7 +42,7 @@ where } /// A CBOR map -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct EventMap(Vec<(EventKey, Vec)>); /// An `event-key` type definition. diff --git a/rust/vote-tx-v2/src/gen_tx/tx_body.rs b/rust/vote-tx-v2/src/gen_tx/tx_body.rs new file mode 100644 index 00000000000..775c9ef5e83 --- /dev/null +++ b/rust/vote-tx-v2/src/gen_tx/tx_body.rs @@ -0,0 +1,76 @@ +//! A generalised tx body struct. + +use minicbor::{Decode, Decoder, Encode, Encoder}; + +use super::{EventMap, Uuid, Vote, VoterData}; +use crate::Cbor; + +/// `TxBody` array struct length +const TX_BODY_LEN: u64 = 4; + +/// A tx body struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TxBody +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, +{ + /// `vote-type` field + pub(super) vote_type: Uuid, + /// `event` field + pub(super) event: EventMap, + /// `votes` field + pub(super) votes: Vec>, + /// `voter-data` field + pub(super) voter_data: VoterData, +} + +impl Decode<'_, ()> for TxBody +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, +{ + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let Some(TX_BODY_LEN) = d.array()? else { + return Err(minicbor::decode::Error::message(format!( + "must be a defined sized array with {TX_BODY_LEN} entries" + ))); + }; + + let vote_type = Uuid::decode(d, &mut ())?; + let event = EventMap::decode(d, &mut ())?; + let votes = Vec::>::decode(d, &mut ())?; + if votes.is_empty() { + return Err(minicbor::decode::Error::message( + "votes array must has at least one entry", + )); + } + let voter_data = VoterData::decode(d, &mut ())?; + Ok(Self { + vote_type, + event, + votes, + voter_data, + }) + } +} + +impl Encode<()> for TxBody +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, +{ + fn encode( + &self, e: &mut Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.array(TX_BODY_LEN)?; + self.vote_type.encode(e, &mut ())?; + self.event.encode(e, &mut ())?; + self.votes.encode(e, &mut ())?; + self.voter_data.encode(e, &mut ())?; + Ok(()) + } +} From 39d2d9072327cdf9e18add3c1496049b2ba0a944 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 14:38:59 +0200 Subject: [PATCH 08/28] move vote impl into vote mod --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 56 +------------------- rust/vote-tx-v2/src/gen_tx/mod.rs | 18 +------ rust/vote-tx-v2/src/gen_tx/vote.rs | 71 ++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 70 deletions(-) create mode 100644 rust/vote-tx-v2/src/gen_tx/vote.rs diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index 71de0ac34e4..f8126f0fe7b 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -7,17 +7,12 @@ use minicbor::{ Decode, Decoder, Encode, Encoder, }; -use super::{ - Choice, EventKey, EventMap, GeneralizedTx, Proof, PropId, TxBody, Uuid, Vote, VoterData, -}; +use super::{Choice, EventKey, EventMap, GeneralizedTx, Proof, PropId, TxBody, Uuid, VoterData}; use crate::Cbor; /// UUID CBOR tag . const CBOR_UUID_TAG: u64 = 37; -/// `Vote` array struct length -const VOTE_LEN: u64 = 3; - /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; @@ -194,52 +189,6 @@ impl Encode<()> for Uuid { } } -impl Decode<'_, ()> for Vote -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - PropIdT: for<'a> Cbor<'a>, -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let Some(VOTE_LEN) = d.array()? else { - return Err(minicbor::decode::Error::message(format!( - "must be a defined sized array with {VOTE_LEN} entries" - ))); - }; - - let choices = Vec::>::decode(d, &mut ())?; - if choices.is_empty() { - return Err(minicbor::decode::Error::message( - "choices array must has at least one entry", - )); - } - let proof = Proof::decode(d, &mut ())?; - let prop_id = PropId::decode(d, &mut ())?; - Ok(Self { - choices, - proof, - prop_id, - }) - } -} - -impl Encode<()> for Vote -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - PropIdT: for<'a> Cbor<'a>, -{ - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.array(VOTE_LEN)?; - self.choices.encode(e, &mut ())?; - self.proof.encode(e, &mut ())?; - self.prop_id.encode(e, &mut ())?; - Ok(()) - } -} - impl Decode<'_, ()> for Choice where T: for<'a> Cbor<'a> { @@ -364,8 +313,7 @@ mod tests { use proptest_derive::Arbitrary; use test_strategy::proptest; - use super::*; - use crate::Cbor; + use super::{super::Vote, *}; type ChoiceT = Vec; type ProofT = Vec; diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 3db478385b4..ff16996c656 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -5,9 +5,11 @@ mod decoding; mod tx_body; +mod vote; use minicbor::data::Int; pub use tx_body::TxBody; +pub use vote::Vote; use crate::Cbor; @@ -25,22 +27,6 @@ where signature: coset::CoseSign, } -/// A vote struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Vote -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, -{ - /// `choices` field - choices: Vec>, - /// `proof` field - proof: Proof, - /// `prop-id` field - prop_id: PropId, -} - /// A CBOR map #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct EventMap(Vec<(EventKey, Vec)>); diff --git a/rust/vote-tx-v2/src/gen_tx/vote.rs b/rust/vote-tx-v2/src/gen_tx/vote.rs new file mode 100644 index 00000000000..d5a10abe6f6 --- /dev/null +++ b/rust/vote-tx-v2/src/gen_tx/vote.rs @@ -0,0 +1,71 @@ +//! A generalised tx vote struct. + +use minicbor::{Decode, Decoder, Encode}; + +use super::{Choice, Proof, PropId}; +use crate::Cbor; + +/// `Vote` array struct length +const VOTE_LEN: u64 = 3; + +/// A vote struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Vote +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, +{ + /// `choices` field + pub(super) choices: Vec>, + /// `proof` field + pub(super) proof: Proof, + /// `prop-id` field + pub(super) prop_id: PropId, +} + +impl Decode<'_, ()> for Vote +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, +{ + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let Some(VOTE_LEN) = d.array()? else { + return Err(minicbor::decode::Error::message(format!( + "must be a defined sized array with {VOTE_LEN} entries" + ))); + }; + + let choices = Vec::>::decode(d, &mut ())?; + if choices.is_empty() { + return Err(minicbor::decode::Error::message( + "choices array must has at least one entry", + )); + } + let proof = Proof::decode(d, &mut ())?; + let prop_id = PropId::decode(d, &mut ())?; + Ok(Self { + choices, + proof, + prop_id, + }) + } +} + +impl Encode<()> for Vote +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, +{ + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.array(VOTE_LEN)?; + self.choices.encode(e, &mut ())?; + self.proof.encode(e, &mut ())?; + self.prop_id.encode(e, &mut ())?; + Ok(()) + } +} From 76cc88dee9e0aeec5a2fd1a5581900bb8998ba6c Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 15:25:20 +0200 Subject: [PATCH 09/28] move EventMap to event_map mod --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 75 ++----------------- rust/vote-tx-v2/src/gen_tx/event_map.rs | 97 +++++++++++++++++++++++++ rust/vote-tx-v2/src/gen_tx/mod.rs | 16 +--- 3 files changed, 104 insertions(+), 84 deletions(-) create mode 100644 rust/vote-tx-v2/src/gen_tx/event_map.rs diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index f8126f0fe7b..36c83041961 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -7,7 +7,7 @@ use minicbor::{ Decode, Decoder, Encode, Encoder, }; -use super::{Choice, EventKey, EventMap, GeneralizedTx, Proof, PropId, TxBody, Uuid, VoterData}; +use super::{Choice, GeneralizedTx, Proof, PropId, TxBody, Uuid, VoterData}; use crate::Cbor; /// UUID CBOR tag . @@ -71,74 +71,6 @@ where } } -impl Decode<'_, ()> for EventMap { - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let Some(len) = d.map()? else { - return Err(minicbor::decode::Error::message( - "must be a defined sized map", - )); - }; - - let map = (0..len) - .map(|_| { - let key = EventKey::decode(d, &mut ())?; - - let value = read_cbor_bytes(d).map_err(|_| { - minicbor::decode::Error::message("missing event map `value` field") - })?; - Ok((key, value)) - }) - .collect::>()?; - - Ok(EventMap(map)) - } -} - -impl Encode<()> for EventMap { - fn encode( - &self, e: &mut Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.map(self.0.len() as u64)?; - - for (key, value) in &self.0 { - key.encode(e, &mut ())?; - - e.writer_mut() - .write_all(value) - .map_err(minicbor::encode::Error::write)?; - } - - Ok(()) - } -} - -impl Decode<'_, ()> for EventKey { - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let pos = d.position(); - // try to decode as int - if let Ok(i) = d.int() { - Ok(EventKey::Int(i)) - } else { - // try to decode as text - d.set_position(pos); - let str = d.str()?; - Ok(EventKey::Text(str.to_string())) - } - } -} - -impl Encode<()> for EventKey { - fn encode( - &self, e: &mut Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - EventKey::Int(i) => e.int(*i)?, - EventKey::Text(s) => e.str(s)?, - }; - Ok(()) - } -} - impl Decode<'_, ()> for VoterData { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; @@ -313,7 +245,10 @@ mod tests { use proptest_derive::Arbitrary; use test_strategy::proptest; - use super::{super::Vote, *}; + use super::{ + super::{EventKey, EventMap, Vote}, + *, + }; type ChoiceT = Vec; type ProofT = Vec; diff --git a/rust/vote-tx-v2/src/gen_tx/event_map.rs b/rust/vote-tx-v2/src/gen_tx/event_map.rs new file mode 100644 index 00000000000..b14cddbad64 --- /dev/null +++ b/rust/vote-tx-v2/src/gen_tx/event_map.rs @@ -0,0 +1,97 @@ +//! A generalised tx event map struct. + +use minicbor::{data::Int, Decode, Decoder, Encode, Encoder}; + +/// A CBOR map +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct EventMap(pub(super) Vec<(EventKey, Vec)>); + +/// An `event-key` type definition. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EventKey { + /// CBOR `int` type + Int(Int), + /// CBOR `text` type + Text(String), +} + +impl Decode<'_, ()> for EventMap { + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let Some(len) = d.map()? else { + return Err(minicbor::decode::Error::message( + "must be a defined sized map", + )); + }; + + let map = (0..len) + .map(|_| { + let key = EventKey::decode(d, &mut ())?; + + let value = read_cbor_bytes(d).map_err(|_| { + minicbor::decode::Error::message("missing event map `value` field") + })?; + Ok((key, value)) + }) + .collect::>()?; + + Ok(EventMap(map)) + } +} + +impl Encode<()> for EventMap { + fn encode( + &self, e: &mut Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.map(self.0.len() as u64)?; + + for (key, value) in &self.0 { + key.encode(e, &mut ())?; + + e.writer_mut() + .write_all(value) + .map_err(minicbor::encode::Error::write)?; + } + + Ok(()) + } +} + +impl Decode<'_, ()> for EventKey { + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let pos = d.position(); + // try to decode as int + if let Ok(i) = d.int() { + Ok(EventKey::Int(i)) + } else { + // try to decode as text + d.set_position(pos); + let str = d.str()?; + Ok(EventKey::Text(str.to_string())) + } + } +} + +impl Encode<()> for EventKey { + fn encode( + &self, e: &mut Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + match self { + EventKey::Int(i) => e.int(*i)?, + EventKey::Text(s) => e.str(s)?, + }; + Ok(()) + } +} + +/// Reads CBOR bytes from the decoder and returns them as bytes. +fn read_cbor_bytes(d: &mut Decoder<'_>) -> Result, minicbor::decode::Error> { + let start = d.position(); + d.skip()?; + let end = d.position(); + let bytes = d + .input() + .get(start..end) + .ok_or(minicbor::decode::Error::end_of_input())? + .to_vec(); + Ok(bytes) +} diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index ff16996c656..987e89008c5 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -4,10 +4,11 @@ // cspell: words Coap mod decoding; +mod event_map; mod tx_body; mod vote; -use minicbor::data::Int; +pub use event_map::{EventKey, EventMap}; pub use tx_body::TxBody; pub use vote::Vote; @@ -27,19 +28,6 @@ where signature: coset::CoseSign, } -/// A CBOR map -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct EventMap(Vec<(EventKey, Vec)>); - -/// An `event-key` type definition. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum EventKey { - /// CBOR `int` type - Int(Int), - /// CBOR `text` type - Text(String), -} - /// A UUID struct. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Uuid(Vec); From 8177528a121ed3ffc5c26bf96eb511d817218b64 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 15:28:18 +0200 Subject: [PATCH 10/28] move VoterData to voter_data mod --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 30 ++------------------- rust/vote-tx-v2/src/gen_tx/mod.rs | 6 ++--- rust/vote-tx-v2/src/gen_tx/voter_data.rs | 33 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 32 deletions(-) create mode 100644 rust/vote-tx-v2/src/gen_tx/voter_data.rs diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index 36c83041961..e85413dadec 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -7,7 +7,7 @@ use minicbor::{ Decode, Decoder, Encode, Encoder, }; -use super::{Choice, GeneralizedTx, Proof, PropId, TxBody, Uuid, VoterData}; +use super::{Choice, GeneralizedTx, Proof, PropId, TxBody, Uuid}; use crate::Cbor; /// UUID CBOR tag . @@ -71,32 +71,6 @@ where } } -impl Decode<'_, ()> for VoterData { - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - let expected_tag = minicbor::data::IanaTag::Cbor.tag(); - if expected_tag != tag { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {}, provided: {}", - expected_tag.as_u64(), - tag.as_u64(), - ))); - } - let choice = d.bytes()?.to_vec(); - Ok(Self(choice)) - } -} - -impl Encode<()> for VoterData { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(IanaTag::Cbor.tag())?; - e.bytes(&self.0)?; - Ok(()) - } -} - impl Decode<'_, ()> for Uuid { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; @@ -246,7 +220,7 @@ mod tests { use test_strategy::proptest; use super::{ - super::{EventKey, EventMap, Vote}, + super::{EventKey, EventMap, Vote, VoterData}, *, }; diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 987e89008c5..4cd020619ff 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -7,10 +7,12 @@ mod decoding; mod event_map; mod tx_body; mod vote; +mod voter_data; pub use event_map::{EventKey, EventMap}; pub use tx_body::TxBody; pub use vote::Vote; +pub use voter_data::VoterData; use crate::Cbor; @@ -32,10 +34,6 @@ where #[derive(Debug, Clone, PartialEq, Eq)] pub struct Uuid(Vec); -/// A voter's data struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VoterData(Vec); - /// A choice struct. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Choice(T) diff --git a/rust/vote-tx-v2/src/gen_tx/voter_data.rs b/rust/vote-tx-v2/src/gen_tx/voter_data.rs new file mode 100644 index 00000000000..4483521f41a --- /dev/null +++ b/rust/vote-tx-v2/src/gen_tx/voter_data.rs @@ -0,0 +1,33 @@ +//! A generalised tx voter's data struct. + +use minicbor::{data::IanaTag, Decode, Decoder, Encode}; + +/// A voter's data struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VoterData(pub(super) Vec); + +impl Decode<'_, ()> for VoterData { + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let tag = d.tag()?; + let expected_tag = minicbor::data::IanaTag::Cbor.tag(); + if expected_tag != tag { + return Err(minicbor::decode::Error::message(format!( + "tag value must be: {}, provided: {}", + expected_tag.as_u64(), + tag.as_u64(), + ))); + } + let choice = d.bytes()?.to_vec(); + Ok(Self(choice)) + } +} + +impl Encode<()> for VoterData { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.tag(IanaTag::Cbor.tag())?; + e.bytes(&self.0)?; + Ok(()) + } +} From 0aa13c797842b4f260fa7e9ce8a7e39f32d94f55 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 15:41:08 +0200 Subject: [PATCH 11/28] wip --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 100 ++++--------------------- rust/vote-tx-v2/src/gen_tx/mod.rs | 18 +---- rust/vote-tx-v2/src/gen_tx/vote.rs | 9 ++- 3 files changed, 27 insertions(+), 100 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs index e85413dadec..230d0c3632b 100644 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ b/rust/vote-tx-v2/src/gen_tx/decoding.rs @@ -7,7 +7,7 @@ use minicbor::{ Decode, Decoder, Encode, Encoder, }; -use super::{Choice, GeneralizedTx, Proof, PropId, TxBody, Uuid}; +use super::{EncodedCbor, GeneralizedTx, TxBody, Uuid}; use crate::Cbor; /// UUID CBOR tag . @@ -95,7 +95,7 @@ impl Encode<()> for Uuid { } } -impl Decode<'_, ()> for Choice +impl Decode<'_, ()> for EncodedCbor where T: for<'a> Cbor<'a> { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { @@ -108,94 +108,24 @@ where T: for<'a> Cbor<'a> tag.as_u64(), ))); } - let choice_bytes = d.bytes()?.to_vec(); - let choice = T::from_bytes(&choice_bytes).map_err(minicbor::decode::Error::message)?; - Ok(Self(choice)) - } -} - -impl Encode<()> for Choice -where T: for<'a> Cbor<'a> -{ - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(IanaTag::Cbor.tag())?; - let choice_bytes = self - .0 - .to_bytes() - .map_err(minicbor::encode::Error::message)?; - e.bytes(&choice_bytes)?; - Ok(()) - } -} - -impl Decode<'_, ()> for Proof -where T: for<'a> Cbor<'a> -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - let expected_tag = minicbor::data::IanaTag::Cbor.tag(); - if expected_tag != tag { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {}, provided: {}", - expected_tag.as_u64(), - tag.as_u64(), - ))); - } - let proof_bytes = d.bytes()?.to_vec(); - let proof = T::from_bytes(&proof_bytes).map_err(minicbor::decode::Error::message)?; - Ok(Self(proof)) - } -} - -impl Encode<()> for Proof -where T: for<'a> Cbor<'a> -{ - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(IanaTag::Cbor.tag())?; - let proof_bytes = self - .0 - .to_bytes() - .map_err(minicbor::encode::Error::message)?; - e.bytes(&proof_bytes)?; - Ok(()) - } -} - -impl Decode<'_, ()> for PropId -where T: for<'a> Cbor<'a> -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - let expected_tag = IanaTag::Cbor.tag(); - if expected_tag != tag { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {}, provided: {}", - expected_tag.as_u64(), - tag.as_u64(), - ))); - } - let prop_id_bytes = d.bytes()?.to_vec(); - let prop_id = T::from_bytes(&prop_id_bytes).map_err(minicbor::decode::Error::message)?; - Ok(Self(prop_id)) + let cbor_bytes = d.bytes()?.to_vec(); + let cbor = T::from_bytes(&cbor_bytes).map_err(minicbor::decode::Error::message)?; + Ok(Self(cbor)) } } -impl Encode<()> for PropId +impl Encode<()> for EncodedCbor where T: for<'a> Cbor<'a> { fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { e.tag(IanaTag::Cbor.tag())?; - let prop_id_bytes = self + let cbor_bytes = self .0 .to_bytes() .map_err(minicbor::encode::Error::message)?; - e.bytes(&prop_id_bytes)?; + e.bytes(&cbor_bytes)?; Ok(()) } } @@ -220,7 +150,7 @@ mod tests { use test_strategy::proptest; use super::{ - super::{EventKey, EventMap, Vote, VoterData}, + super::{EncodedCbor, EventKey, EventMap, Vote, VoterData}, *, }; @@ -275,9 +205,9 @@ mod tests { .into_iter() .map(|(choices, proof, prop_id)| { Vote { - choices: choices.into_iter().map(Choice).collect(), - proof: Proof(proof), - prop_id: PropId(prop_id), + choices: choices.into_iter().map(EncodedCbor).collect(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), } }) .collect(); @@ -329,9 +259,9 @@ mod tests { .into_iter() .map(|(_, proof, prop_id)| { Vote { - choices: Vec::>::new(), - proof: Proof(proof), - prop_id: PropId(prop_id), + choices: Vec::>::new(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), } }) .collect(); diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 4cd020619ff..d2d71e36c36 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -11,7 +11,7 @@ mod voter_data; pub use event_map::{EventKey, EventMap}; pub use tx_body::TxBody; -pub use vote::Vote; +pub use vote::{Choice, Proof, PropId, Vote}; pub use voter_data::VoterData; use crate::Cbor; @@ -30,23 +30,13 @@ where signature: coset::CoseSign, } -/// A UUID struct. +/// A UUID struct, CBOR tag 37. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Uuid(Vec); -/// A choice struct. +/// An encoded CBOR struct, CBOR tag 24. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Choice(T) -where T: for<'a> Cbor<'a>; - -/// A proof struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Proof(T) -where T: for<'a> Cbor<'a>; - -/// A prop id struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PropId(T) +pub struct EncodedCbor(T) where T: for<'a> Cbor<'a>; impl GeneralizedTx diff --git a/rust/vote-tx-v2/src/gen_tx/vote.rs b/rust/vote-tx-v2/src/gen_tx/vote.rs index d5a10abe6f6..ff80bc648c8 100644 --- a/rust/vote-tx-v2/src/gen_tx/vote.rs +++ b/rust/vote-tx-v2/src/gen_tx/vote.rs @@ -2,12 +2,19 @@ use minicbor::{Decode, Decoder, Encode}; -use super::{Choice, Proof, PropId}; +use super::EncodedCbor; use crate::Cbor; /// `Vote` array struct length const VOTE_LEN: u64 = 3; +/// A vote choice type. +pub type Choice = EncodedCbor; +/// A vote proof type. +pub type Proof = EncodedCbor; +/// A vote prop-id type. +pub type PropId = EncodedCbor; + /// A vote struct. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Vote From 8c973f74fc09ccf042997d163738fa991d111e32 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 15:43:50 +0200 Subject: [PATCH 12/28] remove docoding mod --- rust/vote-tx-v2/src/gen_tx/decoding.rs | 280 ------------------------ rust/vote-tx-v2/src/gen_tx/event_map.rs | 15 +- rust/vote-tx-v2/src/gen_tx/mod.rs | 272 ++++++++++++++++++++++- 3 files changed, 273 insertions(+), 294 deletions(-) delete mode 100644 rust/vote-tx-v2/src/gen_tx/decoding.rs diff --git a/rust/vote-tx-v2/src/gen_tx/decoding.rs b/rust/vote-tx-v2/src/gen_tx/decoding.rs deleted file mode 100644 index 230d0c3632b..00000000000 --- a/rust/vote-tx-v2/src/gen_tx/decoding.rs +++ /dev/null @@ -1,280 +0,0 @@ -//! CBOR encoding and decoding implementation. -//! - -use coset::CborSerializable; -use minicbor::{ - data::{IanaTag, Tag}, - Decode, Decoder, Encode, Encoder, -}; - -use super::{EncodedCbor, GeneralizedTx, TxBody, Uuid}; -use crate::Cbor; - -/// UUID CBOR tag . -const CBOR_UUID_TAG: u64 = 37; - -/// `GeneralizedTx` array struct length -const GENERALIZED_TX_LEN: u64 = 2; - -impl Decode<'_, ()> for GeneralizedTx -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let Some(GENERALIZED_TX_LEN) = d.array()? else { - return Err(minicbor::decode::Error::message(format!( - "must be a defined sized array with {GENERALIZED_TX_LEN} entries" - ))); - }; - - let tx_body = TxBody::decode(d, &mut ())?; - - let signature = { - let sign_bytes = read_cbor_bytes(d) - .map_err(|_| minicbor::decode::Error::message("missing `signature` field"))?; - let mut sign = coset::CoseSign::from_slice(&sign_bytes).map_err(|_| { - minicbor::decode::Error::message("`signature` must be COSE_Sign encoded object") - })?; - // We don't need to hold the original encoded data of the COSE protected header - sign.protected.original_data = None; - sign - }; - - Ok(Self { tx_body, signature }) - } -} - -impl Encode<()> for GeneralizedTx -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - PropIdT: for<'a> Cbor<'a>, -{ - fn encode( - &self, e: &mut Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.array(GENERALIZED_TX_LEN)?; - self.tx_body.encode(e, &mut ())?; - - let sign_bytes = self - .signature - .clone() - .to_vec() - .map_err(minicbor::encode::Error::message)?; - e.writer_mut() - .write_all(&sign_bytes) - .map_err(minicbor::encode::Error::write)?; - - Ok(()) - } -} - -impl Decode<'_, ()> for Uuid { - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - if CBOR_UUID_TAG != tag.as_u64() { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {CBOR_UUID_TAG}, provided: {}", - tag.as_u64(), - ))); - } - let choice = d.bytes()?.to_vec(); - Ok(Self(choice)) - } -} - -impl Encode<()> for Uuid { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(Tag::new(CBOR_UUID_TAG))?; - e.bytes(&self.0)?; - Ok(()) - } -} - -impl Decode<'_, ()> for EncodedCbor -where T: for<'a> Cbor<'a> -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - let expected_tag = minicbor::data::IanaTag::Cbor.tag(); - if expected_tag != tag { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {}, provided: {}", - expected_tag.as_u64(), - tag.as_u64(), - ))); - } - let cbor_bytes = d.bytes()?.to_vec(); - let cbor = T::from_bytes(&cbor_bytes).map_err(minicbor::decode::Error::message)?; - Ok(Self(cbor)) - } -} - -impl Encode<()> for EncodedCbor -where T: for<'a> Cbor<'a> -{ - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(IanaTag::Cbor.tag())?; - let cbor_bytes = self - .0 - .to_bytes() - .map_err(minicbor::encode::Error::message)?; - e.bytes(&cbor_bytes)?; - Ok(()) - } -} - -/// Reads CBOR bytes from the decoder and returns them as bytes. -fn read_cbor_bytes(d: &mut Decoder<'_>) -> Result, minicbor::decode::Error> { - let start = d.position(); - d.skip()?; - let end = d.position(); - let bytes = d - .input() - .get(start..end) - .ok_or(minicbor::decode::Error::end_of_input())? - .to_vec(); - Ok(bytes) -} - -#[cfg(test)] -mod tests { - use proptest::{prelude::any_with, sample::size_range}; - use proptest_derive::Arbitrary; - use test_strategy::proptest; - - use super::{ - super::{EncodedCbor, EventKey, EventMap, Vote, VoterData}, - *, - }; - - type ChoiceT = Vec; - type ProofT = Vec; - type PropIdT = Vec; - - type PropVote = (Vec, ProofT, PropIdT); - - #[derive(Debug, Arbitrary)] - enum PropEventKey { - Text(String), - U64(u64), - I64(i64), - } - - impl From for EventKey { - fn from(key: PropEventKey) -> Self { - match key { - PropEventKey::Text(text) => EventKey::Text(text), - PropEventKey::U64(val) => EventKey::Int(val.into()), - PropEventKey::I64(val) => EventKey::Int(val.into()), - } - } - } - - #[proptest] - fn generalized_tx_from_bytes_to_bytes_test( - vote_type: Vec, - // generates a votes in range from 1 to 10, and choices in range from 1 to 10 - #[strategy(any_with::>(( - size_range(1..10usize), - ( - (size_range(1..10usize), Default::default()), - Default::default(), - Default::default(), - ), - )))] - votes: Vec, - event: Vec<(PropEventKey, u64)>, - voter_data: Vec, - ) { - let event = event - .into_iter() - .map(|(key, val)| { - let key = key.into(); - let value = val.to_bytes().unwrap(); - (key, value) - }) - .collect(); - let votes = votes - .into_iter() - .map(|(choices, proof, prop_id)| { - Vote { - choices: choices.into_iter().map(EncodedCbor).collect(), - proof: EncodedCbor(proof), - prop_id: EncodedCbor(prop_id), - } - }) - .collect(); - let tx_body = TxBody { - vote_type: Uuid(vote_type), - event: EventMap(event), - votes, - voter_data: VoterData(voter_data), - }; - - let generalized_tx = GeneralizedTx::::new(tx_body); - - let bytes = generalized_tx.to_bytes().unwrap(); - let decoded = GeneralizedTx::::from_bytes(&bytes).unwrap(); - assert_eq!(generalized_tx, decoded); - } - - #[proptest] - fn invalid_generalized_tx_from_bytes_to_bytes_test( - vote_type: Vec, votes: Vec, event: Vec<(PropEventKey, u64)>, - voter_data: Vec, - ) { - let event: Vec<_> = event - .into_iter() - .map(|(key, val)| { - let key = key.into(); - let value = val.to_bytes().unwrap(); - (key, value) - }) - .collect(); - // Empty votes case - { - let empty_votes = Vec::>::new(); - let tx_body = TxBody { - vote_type: Uuid(vote_type.clone()), - event: EventMap(event.clone()), - votes: empty_votes, - voter_data: VoterData(voter_data.clone()), - }; - - let generalized_tx = GeneralizedTx::new(tx_body); - let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); - } - - // Empty choices case - { - let votes_with_empty_choices = votes - .into_iter() - .map(|(_, proof, prop_id)| { - Vote { - choices: Vec::>::new(), - proof: EncodedCbor(proof), - prop_id: EncodedCbor(prop_id), - } - }) - .collect(); - let tx_body = TxBody { - vote_type: Uuid(vote_type), - event: EventMap(event), - votes: votes_with_empty_choices, - voter_data: VoterData(voter_data), - }; - - let generalized_tx = GeneralizedTx::new(tx_body); - let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); - } - } -} diff --git a/rust/vote-tx-v2/src/gen_tx/event_map.rs b/rust/vote-tx-v2/src/gen_tx/event_map.rs index b14cddbad64..0817afd5049 100644 --- a/rust/vote-tx-v2/src/gen_tx/event_map.rs +++ b/rust/vote-tx-v2/src/gen_tx/event_map.rs @@ -2,6 +2,8 @@ use minicbor::{data::Int, Decode, Decoder, Encode, Encoder}; +use super::read_cbor_bytes; + /// A CBOR map #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct EventMap(pub(super) Vec<(EventKey, Vec)>); @@ -82,16 +84,3 @@ impl Encode<()> for EventKey { Ok(()) } } - -/// Reads CBOR bytes from the decoder and returns them as bytes. -fn read_cbor_bytes(d: &mut Decoder<'_>) -> Result, minicbor::decode::Error> { - let start = d.position(); - d.skip()?; - let end = d.position(); - let bytes = d - .input() - .get(start..end) - .ok_or(minicbor::decode::Error::end_of_input())? - .to_vec(); - Ok(bytes) -} diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index d2d71e36c36..891d23513fd 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -3,13 +3,17 @@ // cspell: words Coap -mod decoding; mod event_map; mod tx_body; mod vote; mod voter_data; +use coset::CborSerializable; pub use event_map::{EventKey, EventMap}; +use minicbor::{ + data::{IanaTag, Tag}, + Decode, Decoder, Encode, Encoder, +}; pub use tx_body::TxBody; pub use vote::{Choice, Proof, PropId, Vote}; pub use voter_data::VoterData; @@ -39,6 +43,139 @@ pub struct Uuid(Vec); pub struct EncodedCbor(T) where T: for<'a> Cbor<'a>; +/// UUID CBOR tag . +const CBOR_UUID_TAG: u64 = 37; + +/// `GeneralizedTx` array struct length +const GENERALIZED_TX_LEN: u64 = 2; + +impl Decode<'_, ()> for GeneralizedTx +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, +{ + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let Some(GENERALIZED_TX_LEN) = d.array()? else { + return Err(minicbor::decode::Error::message(format!( + "must be a defined sized array with {GENERALIZED_TX_LEN} entries" + ))); + }; + + let tx_body = TxBody::decode(d, &mut ())?; + + let signature = { + let sign_bytes = read_cbor_bytes(d) + .map_err(|_| minicbor::decode::Error::message("missing `signature` field"))?; + let mut sign = coset::CoseSign::from_slice(&sign_bytes).map_err(|_| { + minicbor::decode::Error::message("`signature` must be COSE_Sign encoded object") + })?; + // We don't need to hold the original encoded data of the COSE protected header + sign.protected.original_data = None; + sign + }; + + Ok(Self { tx_body, signature }) + } +} + +impl Encode<()> for GeneralizedTx +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, +{ + fn encode( + &self, e: &mut Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.array(GENERALIZED_TX_LEN)?; + self.tx_body.encode(e, &mut ())?; + + let sign_bytes = self + .signature + .clone() + .to_vec() + .map_err(minicbor::encode::Error::message)?; + e.writer_mut() + .write_all(&sign_bytes) + .map_err(minicbor::encode::Error::write)?; + + Ok(()) + } +} + +impl Decode<'_, ()> for Uuid { + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let tag = d.tag()?; + if CBOR_UUID_TAG != tag.as_u64() { + return Err(minicbor::decode::Error::message(format!( + "tag value must be: {CBOR_UUID_TAG}, provided: {}", + tag.as_u64(), + ))); + } + let choice = d.bytes()?.to_vec(); + Ok(Self(choice)) + } +} + +impl Encode<()> for Uuid { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.tag(Tag::new(CBOR_UUID_TAG))?; + e.bytes(&self.0)?; + Ok(()) + } +} + +impl Decode<'_, ()> for EncodedCbor +where T: for<'a> Cbor<'a> +{ + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let tag = d.tag()?; + let expected_tag = minicbor::data::IanaTag::Cbor.tag(); + if expected_tag != tag { + return Err(minicbor::decode::Error::message(format!( + "tag value must be: {}, provided: {}", + expected_tag.as_u64(), + tag.as_u64(), + ))); + } + let cbor_bytes = d.bytes()?.to_vec(); + let cbor = T::from_bytes(&cbor_bytes).map_err(minicbor::decode::Error::message)?; + Ok(Self(cbor)) + } +} + +impl Encode<()> for EncodedCbor +where T: for<'a> Cbor<'a> +{ + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.tag(IanaTag::Cbor.tag())?; + let cbor_bytes = self + .0 + .to_bytes() + .map_err(minicbor::encode::Error::message)?; + e.bytes(&cbor_bytes)?; + Ok(()) + } +} + +/// Reads CBOR bytes from the decoder and returns them as bytes. +fn read_cbor_bytes(d: &mut Decoder<'_>) -> Result, minicbor::decode::Error> { + let start = d.position(); + d.skip()?; + let end = d.position(); + let bytes = d + .input() + .get(start..end) + .ok_or(minicbor::decode::Error::end_of_input())? + .to_vec(); + Ok(bytes) +} + impl GeneralizedTx where ChoiceT: for<'a> Cbor<'a>, @@ -61,3 +198,136 @@ where .build() } } + +#[cfg(test)] +mod tests { + use proptest::{prelude::any_with, sample::size_range}; + use proptest_derive::Arbitrary; + use test_strategy::proptest; + + use super::*; + + type ChoiceT = Vec; + type ProofT = Vec; + type PropIdT = Vec; + + type PropVote = (Vec, ProofT, PropIdT); + + #[derive(Debug, Arbitrary)] + enum PropEventKey { + Text(String), + U64(u64), + I64(i64), + } + + impl From for EventKey { + fn from(key: PropEventKey) -> Self { + match key { + PropEventKey::Text(text) => EventKey::Text(text), + PropEventKey::U64(val) => EventKey::Int(val.into()), + PropEventKey::I64(val) => EventKey::Int(val.into()), + } + } + } + + #[proptest] + fn generalized_tx_from_bytes_to_bytes_test( + vote_type: Vec, + // generates a votes in range from 1 to 10, and choices in range from 1 to 10 + #[strategy(any_with::>(( + size_range(1..10usize), + ( + (size_range(1..10usize), Default::default()), + Default::default(), + Default::default(), + ), + )))] + votes: Vec, + event: Vec<(PropEventKey, u64)>, + voter_data: Vec, + ) { + let event = event + .into_iter() + .map(|(key, val)| { + let key = key.into(); + let value = val.to_bytes().unwrap(); + (key, value) + }) + .collect(); + let votes = votes + .into_iter() + .map(|(choices, proof, prop_id)| { + Vote { + choices: choices.into_iter().map(EncodedCbor).collect(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), + } + }) + .collect(); + let tx_body = TxBody { + vote_type: Uuid(vote_type), + event: EventMap(event), + votes, + voter_data: VoterData(voter_data), + }; + + let generalized_tx = GeneralizedTx::::new(tx_body); + + let bytes = generalized_tx.to_bytes().unwrap(); + let decoded = GeneralizedTx::::from_bytes(&bytes).unwrap(); + assert_eq!(generalized_tx, decoded); + } + + #[proptest] + fn invalid_generalized_tx_from_bytes_to_bytes_test( + vote_type: Vec, votes: Vec, event: Vec<(PropEventKey, u64)>, + voter_data: Vec, + ) { + let event: Vec<_> = event + .into_iter() + .map(|(key, val)| { + let key = key.into(); + let value = val.to_bytes().unwrap(); + (key, value) + }) + .collect(); + // Empty votes case + { + let empty_votes = Vec::>::new(); + let tx_body = TxBody { + vote_type: Uuid(vote_type.clone()), + event: EventMap(event.clone()), + votes: empty_votes, + voter_data: VoterData(voter_data.clone()), + }; + + let generalized_tx = GeneralizedTx::new(tx_body); + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + } + + // Empty choices case + { + let votes_with_empty_choices = votes + .into_iter() + .map(|(_, proof, prop_id)| { + Vote { + choices: Vec::>::new(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), + } + }) + .collect(); + let tx_body = TxBody { + vote_type: Uuid(vote_type), + event: EventMap(event), + votes: votes_with_empty_choices, + voter_data: VoterData(voter_data), + }; + + let generalized_tx = GeneralizedTx::new(tx_body); + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + } + } +} From 8888f6cea406463e705e9890a0e682f457fb8b5f Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 15:51:24 +0200 Subject: [PATCH 13/28] wip --- rust/vote-tx-v2/src/gen_tx/mod.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 891d23513fd..2cfba8bb4a8 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -10,10 +10,7 @@ mod voter_data; use coset::CborSerializable; pub use event_map::{EventKey, EventMap}; -use minicbor::{ - data::{IanaTag, Tag}, - Decode, Decoder, Encode, Encoder, -}; +use minicbor::{data::Tag, Decode, Decoder, Encode, Encoder}; pub use tx_body::TxBody; pub use vote::{Choice, Proof, PropId, Vote}; pub use voter_data::VoterData; @@ -44,7 +41,10 @@ pub struct EncodedCbor(T) where T: for<'a> Cbor<'a>; /// UUID CBOR tag . -const CBOR_UUID_TAG: u64 = 37; +const UUID_TAG: u64 = 37; + +/// encoded-cbor CBOR tag . +const ENCODED_CBOR_TAG: u64 = 24; /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; @@ -107,9 +107,9 @@ where impl Decode<'_, ()> for Uuid { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; - if CBOR_UUID_TAG != tag.as_u64() { + if UUID_TAG != tag.as_u64() { return Err(minicbor::decode::Error::message(format!( - "tag value must be: {CBOR_UUID_TAG}, provided: {}", + "tag value must be: {UUID_TAG}, provided: {}", tag.as_u64(), ))); } @@ -122,7 +122,7 @@ impl Encode<()> for Uuid { fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { - e.tag(Tag::new(CBOR_UUID_TAG))?; + e.tag(Tag::new(UUID_TAG))?; e.bytes(&self.0)?; Ok(()) } @@ -133,11 +133,9 @@ where T: for<'a> Cbor<'a> { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let tag = d.tag()?; - let expected_tag = minicbor::data::IanaTag::Cbor.tag(); - if expected_tag != tag { + if ENCODED_CBOR_TAG != tag.as_u64() { return Err(minicbor::decode::Error::message(format!( - "tag value must be: {}, provided: {}", - expected_tag.as_u64(), + "tag value must be: {ENCODED_CBOR_TAG}, provided: {}", tag.as_u64(), ))); } @@ -153,7 +151,7 @@ where T: for<'a> Cbor<'a> fn encode( &self, e: &mut minicbor::Encoder, (): &mut (), ) -> Result<(), minicbor::encode::Error> { - e.tag(IanaTag::Cbor.tag())?; + e.tag(Tag::new(ENCODED_CBOR_TAG))?; let cbor_bytes = self .0 .to_bytes() From 7cca20bb978e144a4526a020687188b538362d44 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 18:59:30 +0200 Subject: [PATCH 14/28] add signature header validation --- rust/vote-tx-v2/src/gen_tx/mod.rs | 167 +++++++++++++++++++----------- 1 file changed, 106 insertions(+), 61 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 2cfba8bb4a8..21fcc6e3b3d 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -72,6 +72,13 @@ where })?; // We don't need to hold the original encoded data of the COSE protected header sign.protected.original_data = None; + + if sign.protected.header != cose_protected_header() { + return Err(minicbor::decode::Error::message( + "invalid `signature` COSE_Sign protected header", + )); + } + sign }; @@ -174,27 +181,11 @@ fn read_cbor_bytes(d: &mut Decoder<'_>) -> Result, minicbor::decode::Err Ok(bytes) } -impl GeneralizedTx -where - ChoiceT: for<'a> Cbor<'a>, - ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, -{ - /// Creates a new `GeneralizedTx` struct. - #[must_use] - pub fn new(tx_body: TxBody) -> Self { - let signature = coset::CoseSignBuilder::new() - .protected(Self::cose_protected_header()) - .build(); - Self { tx_body, signature } - } - - /// Returns the COSE protected header. - fn cose_protected_header() -> coset::Header { - coset::HeaderBuilder::new() - .content_format(coset::iana::CoapContentFormat::Cbor) - .build() - } +/// Returns the COSE protected header for `GeneralizedTx` signature. +fn cose_protected_header() -> coset::Header { + coset::HeaderBuilder::new() + .content_format(coset::iana::CoapContentFormat::Cbor) + .build() } #[cfg(test)] @@ -268,16 +259,47 @@ mod tests { votes, voter_data: VoterData(voter_data), }; + let signature = coset::CoseSignBuilder::new() + .protected(cose_protected_header()) + .build(); - let generalized_tx = GeneralizedTx::::new(tx_body); - + let generalized_tx = GeneralizedTx { tx_body, signature }; let bytes = generalized_tx.to_bytes().unwrap(); let decoded = GeneralizedTx::::from_bytes(&bytes).unwrap(); assert_eq!(generalized_tx, decoded); } #[proptest] - fn invalid_generalized_tx_from_bytes_to_bytes_test( + fn generalized_tx_with_empty_votes_from_bytes_to_bytes_test( + vote_type: Vec, event: Vec<(PropEventKey, u64)>, voter_data: Vec, + ) { + let event: Vec<_> = event + .into_iter() + .map(|(key, val)| { + let key = key.into(); + let value = val.to_bytes().unwrap(); + (key, value) + }) + .collect(); + + let empty_votes = Vec::>::new(); + let tx_body = TxBody { + vote_type: Uuid(vote_type.clone()), + event: EventMap(event.clone()), + votes: empty_votes, + voter_data: VoterData(voter_data.clone()), + }; + let signature = coset::CoseSignBuilder::new() + .protected(cose_protected_header()) + .build(); + + let generalized_tx = GeneralizedTx { tx_body, signature }; + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + } + + #[proptest] + fn generalized_tx_with_empty_choices_from_bytes_to_bytes_test( vote_type: Vec, votes: Vec, event: Vec<(PropEventKey, u64)>, voter_data: Vec, ) { @@ -289,43 +311,66 @@ mod tests { (key, value) }) .collect(); - // Empty votes case - { - let empty_votes = Vec::>::new(); - let tx_body = TxBody { - vote_type: Uuid(vote_type.clone()), - event: EventMap(event.clone()), - votes: empty_votes, - voter_data: VoterData(voter_data.clone()), - }; - - let generalized_tx = GeneralizedTx::new(tx_body); - let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); - } - // Empty choices case - { - let votes_with_empty_choices = votes - .into_iter() - .map(|(_, proof, prop_id)| { - Vote { - choices: Vec::>::new(), - proof: EncodedCbor(proof), - prop_id: EncodedCbor(prop_id), - } - }) - .collect(); - let tx_body = TxBody { - vote_type: Uuid(vote_type), - event: EventMap(event), - votes: votes_with_empty_choices, - voter_data: VoterData(voter_data), - }; - - let generalized_tx = GeneralizedTx::new(tx_body); - let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); - } + let votes_with_empty_choices = votes + .into_iter() + .map(|(_, proof, prop_id)| { + Vote { + choices: Vec::>::new(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), + } + }) + .collect(); + let tx_body = TxBody { + vote_type: Uuid(vote_type), + event: EventMap(event), + votes: votes_with_empty_choices, + voter_data: VoterData(voter_data), + }; + let signature = coset::CoseSignBuilder::new() + .protected(cose_protected_header()) + .build(); + + let generalized_tx = GeneralizedTx { tx_body, signature }; + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + } + + #[proptest] + fn generalized_tx_with_wrong_signature_from_bytes_to_bytes_test( + vote_type: Vec, votes: Vec, event: Vec<(PropEventKey, u64)>, + voter_data: Vec, + ) { + let event: Vec<_> = event + .into_iter() + .map(|(key, val)| { + let key = key.into(); + let value = val.to_bytes().unwrap(); + (key, value) + }) + .collect(); + + let votes = votes + .into_iter() + .map(|(choices, proof, prop_id)| { + Vote { + choices: choices.into_iter().map(EncodedCbor).collect(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), + } + }) + .collect(); + let tx_body = TxBody { + vote_type: Uuid(vote_type), + event: EventMap(event), + votes, + voter_data: VoterData(voter_data), + }; + let signature = coset::CoseSignBuilder::new().build(); + + let generalized_tx = GeneralizedTx { tx_body, signature }; + let bytes = generalized_tx.to_bytes().unwrap(); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); } } From 9655c64004010fdbb775fa4173d4f6a6bfb5e53b Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 19:08:08 +0200 Subject: [PATCH 15/28] add a gen_tx::builder --- rust/vote-tx-v2/src/gen_tx/builder.rs | 102 ++++++++++++++++++++++++++ rust/vote-tx-v2/src/gen_tx/mod.rs | 1 + 2 files changed, 103 insertions(+) create mode 100644 rust/vote-tx-v2/src/gen_tx/builder.rs diff --git a/rust/vote-tx-v2/src/gen_tx/builder.rs b/rust/vote-tx-v2/src/gen_tx/builder.rs new file mode 100644 index 00000000000..8b09e4388c4 --- /dev/null +++ b/rust/vote-tx-v2/src/gen_tx/builder.rs @@ -0,0 +1,102 @@ +//! A Catalyst generalised vote transaction builder + +#![allow(dead_code)] + +use anyhow::ensure; + +use super::{ + cose_protected_header, EncodedCbor, EventKey, EventMap, GeneralizedTx, TxBody, Uuid, Vote, + VoterData, +}; +use crate::Cbor; + +/// `GeneralizedTx` builder struct +#[allow(clippy::module_name_repetitions)] +pub struct GeneralizedTxBuilder +where + ChoiceT: for<'a> Cbor<'a>, + ProofT: for<'a> Cbor<'a>, + ProopIdT: for<'a> Cbor<'a>, +{ + /// The `vote_type` field + vote_type: Uuid, + /// The `event` field + event: EventMap, + /// The `votes` field + votes: Vec>, + /// The `voter_data` field + voter_data: VoterData, + /// The `signature` builder field + sign_builder: coset::CoseSignBuilder, +} + +impl GeneralizedTxBuilder +where + ChoiceT: for<'a> Cbor<'a> + Clone, + ProofT: for<'a> Cbor<'a> + Clone, + ProopIdT: for<'a> Cbor<'a> + Clone, +{ + /// Creates a new `GeneralizedTxBuilder` struct + #[must_use] + pub fn new(vote_type: Uuid, voter_data: VoterData) -> Self { + let event = EventMap::default(); + let votes = Vec::default(); + let sign_builder = coset::CoseSignBuilder::new().protected(cose_protected_header()); + Self { + vote_type, + event, + votes, + voter_data, + sign_builder, + } + } + + /// Adds an `EventMap` entry to the `event` field. + pub fn with_event(mut self, key: EventKey, value: ValueT) -> anyhow::Result + where ValueT: for<'a> Cbor<'a> + Clone { + let value = value.to_bytes()?; + self.event.0.push((key, value)); + Ok(self) + } + + /// Adds a `Vote` entry to the `votes` field. + /// + /// # Errors + /// - `choices` array must has at least one entry- + pub fn with_vote( + mut self, choices: Vec, proof: ProofT, prop_id: ProopIdT, + ) -> anyhow::Result { + ensure!( + !choices.is_empty(), + "`choices` array must has at least one entry" + ); + self.votes.push(Vote { + choices: choices.into_iter().map(EncodedCbor).collect(), + proof: EncodedCbor(proof), + prop_id: EncodedCbor(prop_id), + }); + Ok(self) + } + + /// Builds a new `GeneralizedTx` object. + /// + /// # Errors + /// - `votes` array must has at least one entry + pub fn build(&self) -> anyhow::Result> { + ensure!( + !self.votes.is_empty(), + "`votes` array must has at least one entry" + ); + + let tx_body = TxBody { + vote_type: self.vote_type.clone(), + event: self.event.clone(), + votes: self.votes.clone(), + voter_data: self.voter_data.clone(), + }; + let signature = coset::CoseSignBuilder::new() + .protected(cose_protected_header()) + .build(); + Ok(GeneralizedTx { tx_body, signature }) + } +} diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 21fcc6e3b3d..2ebbd2f0369 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -3,6 +3,7 @@ // cspell: words Coap +mod builder; mod event_map; mod tx_body; mod vote; From b2623dba967660cb3aa00a4518d4823299c81e7c Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 21:18:02 +0200 Subject: [PATCH 16/28] wip --- rust/vote-tx-v2/src/gen_tx/builder.rs | 18 ++++++++---------- rust/vote-tx-v2/src/gen_tx/mod.rs | 3 ++- rust/vote-tx-v2/src/gen_tx/voter_data.rs | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/builder.rs b/rust/vote-tx-v2/src/gen_tx/builder.rs index 8b09e4388c4..eef49249031 100644 --- a/rust/vote-tx-v2/src/gen_tx/builder.rs +++ b/rust/vote-tx-v2/src/gen_tx/builder.rs @@ -1,7 +1,5 @@ //! A Catalyst generalised vote transaction builder -#![allow(dead_code)] - use anyhow::ensure; use super::{ @@ -52,6 +50,8 @@ where } /// Adds an `EventMap` entry to the `event` field. + /// + /// # Errors pub fn with_event(mut self, key: EventKey, value: ValueT) -> anyhow::Result where ValueT: for<'a> Cbor<'a> + Clone { let value = value.to_bytes()?; @@ -82,21 +82,19 @@ where /// /// # Errors /// - `votes` array must has at least one entry - pub fn build(&self) -> anyhow::Result> { + pub fn build(self) -> anyhow::Result> { ensure!( !self.votes.is_empty(), "`votes` array must has at least one entry" ); let tx_body = TxBody { - vote_type: self.vote_type.clone(), - event: self.event.clone(), - votes: self.votes.clone(), - voter_data: self.voter_data.clone(), + vote_type: self.vote_type, + event: self.event, + votes: self.votes, + voter_data: self.voter_data, }; - let signature = coset::CoseSignBuilder::new() - .protected(cose_protected_header()) - .build(); + let signature = self.sign_builder.build(); Ok(GeneralizedTx { tx_body, signature }) } } diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 2ebbd2f0369..bd2280e014b 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -9,6 +9,7 @@ mod tx_body; mod vote; mod voter_data; +pub use builder::GeneralizedTxBuilder; use coset::CborSerializable; pub use event_map::{EventKey, EventMap}; use minicbor::{data::Tag, Decode, Decoder, Encode, Encoder}; @@ -34,7 +35,7 @@ where /// A UUID struct, CBOR tag 37. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Uuid(Vec); +pub struct Uuid(pub Vec); /// An encoded CBOR struct, CBOR tag 24. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/rust/vote-tx-v2/src/gen_tx/voter_data.rs b/rust/vote-tx-v2/src/gen_tx/voter_data.rs index 4483521f41a..425fb27e319 100644 --- a/rust/vote-tx-v2/src/gen_tx/voter_data.rs +++ b/rust/vote-tx-v2/src/gen_tx/voter_data.rs @@ -4,7 +4,7 @@ use minicbor::{data::IanaTag, Decode, Decoder, Encode}; /// A voter's data struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct VoterData(pub(super) Vec); +pub struct VoterData(pub Vec); impl Decode<'_, ()> for VoterData { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { From a7ea7b51f01865f77d64d794f6a201aed8d33371 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 21:27:23 +0200 Subject: [PATCH 17/28] refactor VoterData type --- rust/vote-tx-v2/src/gen_tx/builder.rs | 13 +++++---- rust/vote-tx-v2/src/gen_tx/mod.rs | 34 +++++++++++++---------- rust/vote-tx-v2/src/gen_tx/tx_body.rs | 18 ++++++++---- rust/vote-tx-v2/src/gen_tx/voter_data.rs | 33 ---------------------- rust/vote-tx-v2/src/public_tx/decoding.rs | 9 ++++-- rust/vote-tx-v2/src/public_tx/mod.rs | 18 ++++++++---- 6 files changed, 60 insertions(+), 65 deletions(-) delete mode 100644 rust/vote-tx-v2/src/gen_tx/voter_data.rs diff --git a/rust/vote-tx-v2/src/gen_tx/builder.rs b/rust/vote-tx-v2/src/gen_tx/builder.rs index eef49249031..ba7f14995a3 100644 --- a/rust/vote-tx-v2/src/gen_tx/builder.rs +++ b/rust/vote-tx-v2/src/gen_tx/builder.rs @@ -10,11 +10,12 @@ use crate::Cbor; /// `GeneralizedTx` builder struct #[allow(clippy::module_name_repetitions)] -pub struct GeneralizedTxBuilder +pub struct GeneralizedTxBuilder where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, ProopIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { /// The `vote_type` field vote_type: Uuid, @@ -23,20 +24,22 @@ where /// The `votes` field votes: Vec>, /// The `voter_data` field - voter_data: VoterData, + voter_data: VoterData, /// The `signature` builder field sign_builder: coset::CoseSignBuilder, } -impl GeneralizedTxBuilder +impl + GeneralizedTxBuilder where ChoiceT: for<'a> Cbor<'a> + Clone, ProofT: for<'a> Cbor<'a> + Clone, ProopIdT: for<'a> Cbor<'a> + Clone, + VoterDataT: for<'a> Cbor<'a> + Clone, { /// Creates a new `GeneralizedTxBuilder` struct #[must_use] - pub fn new(vote_type: Uuid, voter_data: VoterData) -> Self { + pub fn new(vote_type: Uuid, voter_data: VoterData) -> Self { let event = EventMap::default(); let votes = Vec::default(); let sign_builder = coset::CoseSignBuilder::new().protected(cose_protected_header()); @@ -82,7 +85,7 @@ where /// /// # Errors /// - `votes` array must has at least one entry - pub fn build(self) -> anyhow::Result> { + pub fn build(self) -> anyhow::Result> { ensure!( !self.votes.is_empty(), "`votes` array must has at least one entry" diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index bd2280e014b..6aa98b0f973 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -7,28 +7,27 @@ mod builder; mod event_map; mod tx_body; mod vote; -mod voter_data; pub use builder::GeneralizedTxBuilder; use coset::CborSerializable; pub use event_map::{EventKey, EventMap}; use minicbor::{data::Tag, Decode, Decoder, Encode, Encoder}; -pub use tx_body::TxBody; +pub use tx_body::{TxBody, VoterData}; pub use vote::{Choice, Proof, PropId, Vote}; -pub use voter_data::VoterData; use crate::Cbor; /// A generalized tx struct. #[derive(Debug, Clone, PartialEq)] -pub struct GeneralizedTx +pub struct GeneralizedTx where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, ProopIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { /// `tx-body` field - tx_body: TxBody, + tx_body: TxBody, /// `signature` field signature: coset::CoseSign, } @@ -51,11 +50,13 @@ const ENCODED_CBOR_TAG: u64 = 24; /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; -impl Decode<'_, ()> for GeneralizedTx +impl Decode<'_, ()> + for GeneralizedTx where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, ProopIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(GENERALIZED_TX_LEN) = d.array()? else { @@ -88,11 +89,13 @@ where } } -impl Encode<()> for GeneralizedTx +impl Encode<()> + for GeneralizedTx where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, PropIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { fn encode( &self, e: &mut Encoder, (): &mut (), @@ -201,6 +204,7 @@ mod tests { type ChoiceT = Vec; type ProofT = Vec; type PropIdT = Vec; + type VoterDataT = Vec; type PropVote = (Vec, ProofT, PropIdT); @@ -259,7 +263,7 @@ mod tests { vote_type: Uuid(vote_type), event: EventMap(event), votes, - voter_data: VoterData(voter_data), + voter_data: EncodedCbor(voter_data), }; let signature = coset::CoseSignBuilder::new() .protected(cose_protected_header()) @@ -267,7 +271,7 @@ mod tests { let generalized_tx = GeneralizedTx { tx_body, signature }; let bytes = generalized_tx.to_bytes().unwrap(); - let decoded = GeneralizedTx::::from_bytes(&bytes).unwrap(); + let decoded = GeneralizedTx::from_bytes(&bytes).unwrap(); assert_eq!(generalized_tx, decoded); } @@ -289,7 +293,7 @@ mod tests { vote_type: Uuid(vote_type.clone()), event: EventMap(event.clone()), votes: empty_votes, - voter_data: VoterData(voter_data.clone()), + voter_data: EncodedCbor(voter_data.clone()), }; let signature = coset::CoseSignBuilder::new() .protected(cose_protected_header()) @@ -297,7 +301,7 @@ mod tests { let generalized_tx = GeneralizedTx { tx_body, signature }; let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); } #[proptest] @@ -328,7 +332,7 @@ mod tests { vote_type: Uuid(vote_type), event: EventMap(event), votes: votes_with_empty_choices, - voter_data: VoterData(voter_data), + voter_data: EncodedCbor(voter_data), }; let signature = coset::CoseSignBuilder::new() .protected(cose_protected_header()) @@ -336,7 +340,7 @@ mod tests { let generalized_tx = GeneralizedTx { tx_body, signature }; let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); } #[proptest] @@ -367,12 +371,12 @@ mod tests { vote_type: Uuid(vote_type), event: EventMap(event), votes, - voter_data: VoterData(voter_data), + voter_data: EncodedCbor(voter_data), }; let signature = coset::CoseSignBuilder::new().build(); let generalized_tx = GeneralizedTx { tx_body, signature }; let bytes = generalized_tx.to_bytes().unwrap(); - assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); + assert!(GeneralizedTx::::from_bytes(&bytes).is_err()); } } diff --git a/rust/vote-tx-v2/src/gen_tx/tx_body.rs b/rust/vote-tx-v2/src/gen_tx/tx_body.rs index 775c9ef5e83..1140e991b50 100644 --- a/rust/vote-tx-v2/src/gen_tx/tx_body.rs +++ b/rust/vote-tx-v2/src/gen_tx/tx_body.rs @@ -2,19 +2,23 @@ use minicbor::{Decode, Decoder, Encode, Encoder}; -use super::{EventMap, Uuid, Vote, VoterData}; +use super::{EncodedCbor, EventMap, Uuid, Vote}; use crate::Cbor; /// `TxBody` array struct length const TX_BODY_LEN: u64 = 4; +/// A voter's data type. +pub type VoterData = EncodedCbor; + /// A tx body struct. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TxBody +pub struct TxBody where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, ProopIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { /// `vote-type` field pub(super) vote_type: Uuid, @@ -23,14 +27,16 @@ where /// `votes` field pub(super) votes: Vec>, /// `voter-data` field - pub(super) voter_data: VoterData, + pub(super) voter_data: VoterData, } -impl Decode<'_, ()> for TxBody +impl Decode<'_, ()> + for TxBody where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, PropIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { let Some(TX_BODY_LEN) = d.array()? else { @@ -57,11 +63,13 @@ where } } -impl Encode<()> for TxBody +impl Encode<()> + for TxBody where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, PropIdT: for<'a> Cbor<'a>, + VoterDataT: for<'a> Cbor<'a>, { fn encode( &self, e: &mut Encoder, (): &mut (), diff --git a/rust/vote-tx-v2/src/gen_tx/voter_data.rs b/rust/vote-tx-v2/src/gen_tx/voter_data.rs deleted file mode 100644 index 425fb27e319..00000000000 --- a/rust/vote-tx-v2/src/gen_tx/voter_data.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! A generalised tx voter's data struct. - -use minicbor::{data::IanaTag, Decode, Decoder, Encode}; - -/// A voter's data struct. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VoterData(pub Vec); - -impl Decode<'_, ()> for VoterData { - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - let expected_tag = minicbor::data::IanaTag::Cbor.tag(); - if expected_tag != tag { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {}, provided: {}", - expected_tag.as_u64(), - tag.as_u64(), - ))); - } - let choice = d.bytes()?.to_vec(); - Ok(Self(choice)) - } -} - -impl Encode<()> for VoterData { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(IanaTag::Cbor.tag())?; - e.bytes(&self.0)?; - Ok(()) - } -} diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs index 1a19b055bf3..981a16494c0 100644 --- a/rust/vote-tx-v2/src/public_tx/decoding.rs +++ b/rust/vote-tx-v2/src/public_tx/decoding.rs @@ -4,15 +4,20 @@ use minicbor::{Decode, Encode}; use super::{Choice, GeneralizedTx, Proof, PropId, PublicTx, Uuid}; +use crate::Cbor; -impl Decode<'_, ()> for PublicTx { +impl Decode<'_, ()> for PublicTx +where VoteDataT: for<'a> Cbor<'a> +{ fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { let gen_tx = GeneralizedTx::decode(d, &mut ())?; Ok(Self(gen_tx)) } } -impl Encode<()> for PublicTx { +impl Encode<()> for PublicTx +where VoteDataT: for<'a> Cbor<'a> +{ fn encode( &self, e: &mut minicbor::Encoder, ctx: &mut (), ) -> Result<(), minicbor::encode::Error> { diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index 4f067d44df1..3a0d763dfb4 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -3,12 +3,16 @@ use std::ops::{Deref, DerefMut}; -use crate::gen_tx::{GeneralizedTx, Uuid}; +use crate::{ + gen_tx::{GeneralizedTx, Uuid}, + Cbor, +}; mod decoding; /// A public vote tx struct. -pub struct PublicTx(GeneralizedTx); +pub struct PublicTx(GeneralizedTx) +where VoteDataT: for<'a> Cbor<'a>; /// A public voting choice struct. pub struct Choice(pub u64); @@ -19,15 +23,19 @@ pub struct Proof; /// A public voting proposal id struct. pub struct PropId(pub Uuid); -impl Deref for PublicTx { - type Target = GeneralizedTx; +impl Deref for PublicTx +where VoteDataT: for<'a> Cbor<'a> +{ + type Target = GeneralizedTx; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for PublicTx { +impl DerefMut for PublicTx +where VoteDataT: for<'a> Cbor<'a> +{ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } From cf4cdc305e0191ab3d98a355cd91e7a2eb5bff53 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 18 Nov 2024 22:00:44 +0200 Subject: [PATCH 18/28] refactor Cbor --- rust/vote-tx-v2/src/gen_tx/event_map.rs | 4 ++-- rust/vote-tx-v2/src/gen_tx/mod.rs | 4 ++-- rust/vote-tx-v2/src/gen_tx/tx_body.rs | 2 +- rust/vote-tx-v2/src/gen_tx/vote.rs | 2 +- rust/vote-tx-v2/src/lib.rs | 21 ++++++++++++++------- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/event_map.rs b/rust/vote-tx-v2/src/gen_tx/event_map.rs index 0817afd5049..be68de4ac17 100644 --- a/rust/vote-tx-v2/src/gen_tx/event_map.rs +++ b/rust/vote-tx-v2/src/gen_tx/event_map.rs @@ -5,11 +5,11 @@ use minicbor::{data::Int, Decode, Decoder, Encode, Encoder}; use super::read_cbor_bytes; /// A CBOR map -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct EventMap(pub(super) Vec<(EventKey, Vec)>); /// An `event-key` type definition. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub enum EventKey { /// CBOR `int` type Int(Int), diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 6aa98b0f973..9fae67b64b6 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -33,11 +33,11 @@ where } /// A UUID struct, CBOR tag 37. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub struct Uuid(pub Vec); /// An encoded CBOR struct, CBOR tag 24. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub struct EncodedCbor(T) where T: for<'a> Cbor<'a>; diff --git a/rust/vote-tx-v2/src/gen_tx/tx_body.rs b/rust/vote-tx-v2/src/gen_tx/tx_body.rs index 1140e991b50..e36e101a3a7 100644 --- a/rust/vote-tx-v2/src/gen_tx/tx_body.rs +++ b/rust/vote-tx-v2/src/gen_tx/tx_body.rs @@ -12,7 +12,7 @@ const TX_BODY_LEN: u64 = 4; pub type VoterData = EncodedCbor; /// A tx body struct. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub struct TxBody where ChoiceT: for<'a> Cbor<'a>, diff --git a/rust/vote-tx-v2/src/gen_tx/vote.rs b/rust/vote-tx-v2/src/gen_tx/vote.rs index ff80bc648c8..13e68d1ba59 100644 --- a/rust/vote-tx-v2/src/gen_tx/vote.rs +++ b/rust/vote-tx-v2/src/gen_tx/vote.rs @@ -16,7 +16,7 @@ pub type Proof = EncodedCbor; pub type PropId = EncodedCbor; /// A vote struct. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub struct Vote where ChoiceT: for<'a> Cbor<'a>, diff --git a/rust/vote-tx-v2/src/lib.rs b/rust/vote-tx-v2/src/lib.rs index c094a80df40..2cf9bd2b602 100644 --- a/rust/vote-tx-v2/src/lib.rs +++ b/rust/vote-tx-v2/src/lib.rs @@ -10,11 +10,24 @@ pub mod gen_tx; pub mod public_tx; /// Cbor encodable and decodable type trait. -pub trait Cbor<'a>: Encode<()> + Decode<'a, ()> { +pub trait Cbor<'a> { /// Encodes to CBOR encoded bytes. /// /// # Errors /// - Cannot encode + fn to_bytes(&self) -> anyhow::Result>; + + /// Decodes from the CBOR encoded bytes. + /// + /// # Errors + /// - Cannot decode + fn from_bytes(bytes: &'a [u8]) -> anyhow::Result + where Self: Sized; +} + +impl<'a, T> Cbor<'a> for T +where T: Encode<()> + Decode<'a, ()> +{ fn to_bytes(&self) -> anyhow::Result> { let mut bytes = Vec::new(); let mut e = Encoder::new(&mut bytes); @@ -23,10 +36,6 @@ pub trait Cbor<'a>: Encode<()> + Decode<'a, ()> { Ok(bytes) } - /// Decodes from the CBOR encoded bytes. - /// - /// # Errors - /// - Cannot decode fn from_bytes(bytes: &'a [u8]) -> anyhow::Result { let mut decoder = Decoder::new(bytes); let res = Self::decode(&mut decoder, &mut ()) @@ -34,5 +43,3 @@ pub trait Cbor<'a>: Encode<()> + Decode<'a, ()> { Ok(res) } } - -impl<'a, T> Cbor<'a> for T where T: Encode<()> + Decode<'a, ()> {} From 03e8d0e69ad9437624665eae9998138c7a22e345 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 10:41:21 +0200 Subject: [PATCH 19/28] update cddl specs, make voter-data generic --- .../catalyst_voting/cddl/gen_vote_tx.cddl | 13 +++++++------ .../catalyst_voting/cddl/vote_tx_v2_private.cddl | 2 +- .../catalyst_voting/cddl/vote_tx_v2_public.cddl | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl index 693e151dabb..5ec5c8bb6a5 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl @@ -1,18 +1,19 @@ -gen-vote-tx = [ - tx-body, +gen-vote-tx = [ + tx-body, signature ] -tx-body = [ +tx-body = [ vote-type event, votes, - voter-data, + voter-data, ] vote-type = UUID ; e.g. Public or Private vote -event = { * event-key => any } +event = { * event-key => event-value } event-key = int / text +event-value = any votes = [+ vote] vote = [ @@ -25,7 +26,7 @@ choice = #6.24(bytes .cbor choice-t) ; encoded-cbor proof = #6.24(bytes .cbor proof-t) ; encoded-cbor prop-id = #6.24(bytes .cbor prop-id-t) ; encoded-cbor -voter-data = encoded-cbor +voter-data = #6.24(bytes .cbor voter-data-t) ; encoded-cbor UUID = #6.37(bytes) ; UUID type signature = #6.98(cose.COSE_Sign) ; COSE signature diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl index 23c4d5ea9f7..e65dd4341e9 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl @@ -1,4 +1,4 @@ -vote-tx-v2 = gen-vote-tx +vote-tx-v2 = gen-vote-tx choice-data = ciphertext ciphertext = [group-element, group-element] diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl index de273ef4f0b..d1e180ef471 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl @@ -1,4 +1,4 @@ -vote-tx-v2-public = gen-vote-tx +vote-tx-v2-public = gen-vote-tx choice-data = uint proof-data = undefined From 6a7d9236f4b793c69e024d30ca7bb2ae560de629 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 10:44:49 +0200 Subject: [PATCH 20/28] move choice to the separate mod --- rust/vote-tx-v2/src/public_tx/choice.rs | 21 +++++++++++++++++++++ rust/vote-tx-v2/src/public_tx/decoding.rs | 17 +---------------- rust/vote-tx-v2/src/public_tx/mod.rs | 12 ++++++------ 3 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 rust/vote-tx-v2/src/public_tx/choice.rs diff --git a/rust/vote-tx-v2/src/public_tx/choice.rs b/rust/vote-tx-v2/src/public_tx/choice.rs new file mode 100644 index 00000000000..3d20dc60ab4 --- /dev/null +++ b/rust/vote-tx-v2/src/public_tx/choice.rs @@ -0,0 +1,21 @@ +//! A public vote tx choice struct. + +use minicbor::{Decode, Encode}; + +/// A public voting choice struct. +pub struct Choice(u64); + +impl Decode<'_, ()> for Choice { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let choice = d.u64()?; + Ok(Self(choice)) + } +} + +impl Encode<()> for Choice { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, &mut ()) + } +} diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs index 981a16494c0..b9d88ee77a5 100644 --- a/rust/vote-tx-v2/src/public_tx/decoding.rs +++ b/rust/vote-tx-v2/src/public_tx/decoding.rs @@ -3,7 +3,7 @@ use minicbor::{Decode, Encode}; -use super::{Choice, GeneralizedTx, Proof, PropId, PublicTx, Uuid}; +use super::{GeneralizedTx, Proof, PropId, PublicTx, Uuid}; use crate::Cbor; impl Decode<'_, ()> for PublicTx @@ -25,21 +25,6 @@ where VoteDataT: for<'a> Cbor<'a> } } -impl Decode<'_, ()> for Choice { - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - let choice = d.u64()?; - Ok(Self(choice)) - } -} - -impl Encode<()> for Choice { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, &mut ()) - } -} - impl Decode<'_, ()> for Proof { fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { d.undefined()?; diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index 3a0d763dfb4..a63d2fa3e58 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -1,27 +1,27 @@ //! A Catalyst public vote transaction v2 object, structured following this //! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/v2/#public-vote) +mod choice; +mod decoding; + use std::ops::{Deref, DerefMut}; +pub use choice::Choice; + use crate::{ gen_tx::{GeneralizedTx, Uuid}, Cbor, }; -mod decoding; - /// A public vote tx struct. pub struct PublicTx(GeneralizedTx) where VoteDataT: for<'a> Cbor<'a>; -/// A public voting choice struct. -pub struct Choice(pub u64); - /// A public voting proof struct, CBOR `undefined`. pub struct Proof; /// A public voting proposal id struct. -pub struct PropId(pub Uuid); +pub struct PropId(Uuid); impl Deref for PublicTx where VoteDataT: for<'a> Cbor<'a> From 597230cc5648769c37b1158af7f36d4df7217505 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 10:47:48 +0200 Subject: [PATCH 21/28] wip --- rust/vote-tx-v2/src/public_tx/decoding.rs | 57 ----------------------- rust/vote-tx-v2/src/public_tx/mod.rs | 36 +++++++++----- rust/vote-tx-v2/src/public_tx/proof.rs | 22 +++++++++ rust/vote-tx-v2/src/public_tx/prop_id.rs | 23 +++++++++ 4 files changed, 70 insertions(+), 68 deletions(-) delete mode 100644 rust/vote-tx-v2/src/public_tx/decoding.rs create mode 100644 rust/vote-tx-v2/src/public_tx/proof.rs create mode 100644 rust/vote-tx-v2/src/public_tx/prop_id.rs diff --git a/rust/vote-tx-v2/src/public_tx/decoding.rs b/rust/vote-tx-v2/src/public_tx/decoding.rs deleted file mode 100644 index b9d88ee77a5..00000000000 --- a/rust/vote-tx-v2/src/public_tx/decoding.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! CBOR encoding and decoding implementation. -//! - -use minicbor::{Decode, Encode}; - -use super::{GeneralizedTx, Proof, PropId, PublicTx, Uuid}; -use crate::Cbor; - -impl Decode<'_, ()> for PublicTx -where VoteDataT: for<'a> Cbor<'a> -{ - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - let gen_tx = GeneralizedTx::decode(d, &mut ())?; - Ok(Self(gen_tx)) - } -} - -impl Encode<()> for PublicTx -where VoteDataT: for<'a> Cbor<'a> -{ - fn encode( - &self, e: &mut minicbor::Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, ctx) - } -} - -impl Decode<'_, ()> for Proof { - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - d.undefined()?; - Ok(Self) - } -} - -impl Encode<()> for Proof { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.undefined()?; - Ok(()) - } -} - -impl Decode<'_, ()> for PropId { - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - let prop_id = Uuid::decode(d, &mut ())?; - Ok(Self(prop_id)) - } -} - -impl Encode<()> for PropId { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, &mut ()) - } -} diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index a63d2fa3e58..fe8bea924fb 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -2,27 +2,22 @@ //! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/v2/#public-vote) mod choice; -mod decoding; +mod proof; +mod prop_id; use std::ops::{Deref, DerefMut}; pub use choice::Choice; +use minicbor::{Decode, Encode}; +pub use proof::Proof; +pub use prop_id::PropId; -use crate::{ - gen_tx::{GeneralizedTx, Uuid}, - Cbor, -}; +use crate::{gen_tx::GeneralizedTx, Cbor}; /// A public vote tx struct. pub struct PublicTx(GeneralizedTx) where VoteDataT: for<'a> Cbor<'a>; -/// A public voting proof struct, CBOR `undefined`. -pub struct Proof; - -/// A public voting proposal id struct. -pub struct PropId(Uuid); - impl Deref for PublicTx where VoteDataT: for<'a> Cbor<'a> { @@ -40,3 +35,22 @@ where VoteDataT: for<'a> Cbor<'a> &mut self.0 } } + +impl Decode<'_, ()> for PublicTx +where VoteDataT: for<'a> Cbor<'a> +{ + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let gen_tx = GeneralizedTx::decode(d, &mut ())?; + Ok(Self(gen_tx)) + } +} + +impl Encode<()> for PublicTx +where VoteDataT: for<'a> Cbor<'a> +{ + fn encode( + &self, e: &mut minicbor::Encoder, ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, ctx) + } +} diff --git a/rust/vote-tx-v2/src/public_tx/proof.rs b/rust/vote-tx-v2/src/public_tx/proof.rs new file mode 100644 index 00000000000..27bd9a2836b --- /dev/null +++ b/rust/vote-tx-v2/src/public_tx/proof.rs @@ -0,0 +1,22 @@ +//! A public vote tx proof struct. + +use minicbor::{Decode, Encode}; + +/// A public voting proof struct, CBOR `undefined`. +pub struct Proof; + +impl Decode<'_, ()> for Proof { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + d.undefined()?; + Ok(Self) + } +} + +impl Encode<()> for Proof { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.undefined()?; + Ok(()) + } +} diff --git a/rust/vote-tx-v2/src/public_tx/prop_id.rs b/rust/vote-tx-v2/src/public_tx/prop_id.rs new file mode 100644 index 00000000000..74ae5d8c4cb --- /dev/null +++ b/rust/vote-tx-v2/src/public_tx/prop_id.rs @@ -0,0 +1,23 @@ +//! A public vote tx proposal id struct. + +use minicbor::{Decode, Encode}; + +use crate::gen_tx::Uuid; + +/// A public voting proposal id struct. +pub struct PropId(Uuid); + +impl Decode<'_, ()> for PropId { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let prop_id = Uuid::decode(d, &mut ())?; + Ok(Self(prop_id)) + } +} + +impl Encode<()> for PropId { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, &mut ()) + } +} From c822ff79f7d5d8165f9883c9e9ca9cc0585299e3 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 10:50:58 +0200 Subject: [PATCH 22/28] move Uuid impl to seprate mod --- rust/vote-tx-v2/src/gen_tx/builder.rs | 5 ++-- rust/vote-tx-v2/src/gen_tx/mod.rs | 32 +--------------------- rust/vote-tx-v2/src/gen_tx/tx_body.rs | 4 +-- rust/vote-tx-v2/src/lib.rs | 1 + rust/vote-tx-v2/src/public_tx/prop_id.rs | 2 +- rust/vote-tx-v2/src/uuid.rs | 34 ++++++++++++++++++++++++ 6 files changed, 41 insertions(+), 37 deletions(-) create mode 100644 rust/vote-tx-v2/src/uuid.rs diff --git a/rust/vote-tx-v2/src/gen_tx/builder.rs b/rust/vote-tx-v2/src/gen_tx/builder.rs index ba7f14995a3..a34fa62dbc1 100644 --- a/rust/vote-tx-v2/src/gen_tx/builder.rs +++ b/rust/vote-tx-v2/src/gen_tx/builder.rs @@ -3,10 +3,9 @@ use anyhow::ensure; use super::{ - cose_protected_header, EncodedCbor, EventKey, EventMap, GeneralizedTx, TxBody, Uuid, Vote, - VoterData, + cose_protected_header, EncodedCbor, EventKey, EventMap, GeneralizedTx, TxBody, Vote, VoterData, }; -use crate::Cbor; +use crate::{uuid::Uuid, Cbor}; /// `GeneralizedTx` builder struct #[allow(clippy::module_name_repetitions)] diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 9fae67b64b6..46764ed7c23 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -32,18 +32,11 @@ where signature: coset::CoseSign, } -/// A UUID struct, CBOR tag 37. -#[derive(Debug, Clone, PartialEq)] -pub struct Uuid(pub Vec); - /// An encoded CBOR struct, CBOR tag 24. #[derive(Debug, Clone, PartialEq)] pub struct EncodedCbor(T) where T: for<'a> Cbor<'a>; -/// UUID CBOR tag . -const UUID_TAG: u64 = 37; - /// encoded-cbor CBOR tag . const ENCODED_CBOR_TAG: u64 = 24; @@ -116,30 +109,6 @@ where } } -impl Decode<'_, ()> for Uuid { - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - if UUID_TAG != tag.as_u64() { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {UUID_TAG}, provided: {}", - tag.as_u64(), - ))); - } - let choice = d.bytes()?.to_vec(); - Ok(Self(choice)) - } -} - -impl Encode<()> for Uuid { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(Tag::new(UUID_TAG))?; - e.bytes(&self.0)?; - Ok(()) - } -} - impl Decode<'_, ()> for EncodedCbor where T: for<'a> Cbor<'a> { @@ -200,6 +169,7 @@ mod tests { use test_strategy::proptest; use super::*; + use crate::uuid::Uuid; type ChoiceT = Vec; type ProofT = Vec; diff --git a/rust/vote-tx-v2/src/gen_tx/tx_body.rs b/rust/vote-tx-v2/src/gen_tx/tx_body.rs index e36e101a3a7..12f494bc275 100644 --- a/rust/vote-tx-v2/src/gen_tx/tx_body.rs +++ b/rust/vote-tx-v2/src/gen_tx/tx_body.rs @@ -2,8 +2,8 @@ use minicbor::{Decode, Decoder, Encode, Encoder}; -use super::{EncodedCbor, EventMap, Uuid, Vote}; -use crate::Cbor; +use super::{EncodedCbor, EventMap, Vote}; +use crate::{uuid::Uuid, Cbor}; /// `TxBody` array struct length const TX_BODY_LEN: u64 = 4; diff --git a/rust/vote-tx-v2/src/lib.rs b/rust/vote-tx-v2/src/lib.rs index 2cf9bd2b602..84343e020e8 100644 --- a/rust/vote-tx-v2/src/lib.rs +++ b/rust/vote-tx-v2/src/lib.rs @@ -8,6 +8,7 @@ use minicbor::{Decode, Decoder, Encode, Encoder}; pub mod gen_tx; pub mod public_tx; +pub mod uuid; /// Cbor encodable and decodable type trait. pub trait Cbor<'a> { diff --git a/rust/vote-tx-v2/src/public_tx/prop_id.rs b/rust/vote-tx-v2/src/public_tx/prop_id.rs index 74ae5d8c4cb..41b93599ae1 100644 --- a/rust/vote-tx-v2/src/public_tx/prop_id.rs +++ b/rust/vote-tx-v2/src/public_tx/prop_id.rs @@ -2,7 +2,7 @@ use minicbor::{Decode, Encode}; -use crate::gen_tx::Uuid; +use crate::uuid::Uuid; /// A public voting proposal id struct. pub struct PropId(Uuid); diff --git a/rust/vote-tx-v2/src/uuid.rs b/rust/vote-tx-v2/src/uuid.rs new file mode 100644 index 00000000000..6bb3b5d5170 --- /dev/null +++ b/rust/vote-tx-v2/src/uuid.rs @@ -0,0 +1,34 @@ +//! A CBOR encoded/decoded UUID struct. + +use minicbor::{data::Tag, Decode, Decoder, Encode}; + +/// UUID CBOR tag . +const UUID_TAG: u64 = 37; + +/// A UUID struct, CBOR tag 37. +#[derive(Debug, Clone, PartialEq)] +pub struct Uuid(pub Vec); + +impl Decode<'_, ()> for Uuid { + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let tag = d.tag()?; + if UUID_TAG != tag.as_u64() { + return Err(minicbor::decode::Error::message(format!( + "tag value must be: {UUID_TAG}, provided: {}", + tag.as_u64(), + ))); + } + let choice = d.bytes()?.to_vec(); + Ok(Self(choice)) + } +} + +impl Encode<()> for Uuid { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.tag(Tag::new(UUID_TAG))?; + e.bytes(&self.0)?; + Ok(()) + } +} From 626ecc57643e0d10b5af259da0d99060fb4f6982 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 10:54:50 +0200 Subject: [PATCH 23/28] move EncodedCbor struct to separate mod --- rust/vote-tx-v2/src/encoded_cbor.rs | 46 +++++++++++++++++++++++++++ rust/vote-tx-v2/src/gen_tx/builder.rs | 6 ++-- rust/vote-tx-v2/src/gen_tx/mod.rs | 45 ++------------------------ rust/vote-tx-v2/src/gen_tx/tx_body.rs | 4 +-- rust/vote-tx-v2/src/gen_tx/vote.rs | 3 +- rust/vote-tx-v2/src/lib.rs | 1 + 6 files changed, 54 insertions(+), 51 deletions(-) create mode 100644 rust/vote-tx-v2/src/encoded_cbor.rs diff --git a/rust/vote-tx-v2/src/encoded_cbor.rs b/rust/vote-tx-v2/src/encoded_cbor.rs new file mode 100644 index 00000000000..981fc12737c --- /dev/null +++ b/rust/vote-tx-v2/src/encoded_cbor.rs @@ -0,0 +1,46 @@ +//! An encoded CBOR (tag 24) struct + +use minicbor::{data::Tag, Decode, Decoder, Encode}; + +use crate::Cbor; + +/// encoded-cbor CBOR tag . +const ENCODED_CBOR_TAG: u64 = 24; + +/// An encoded CBOR struct, CBOR tag 24. +#[derive(Debug, Clone, PartialEq)] +pub struct EncodedCbor(pub T) +where T: for<'a> Cbor<'a>; + +impl Decode<'_, ()> for EncodedCbor +where T: for<'a> Cbor<'a> +{ + fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { + let tag = d.tag()?; + if ENCODED_CBOR_TAG != tag.as_u64() { + return Err(minicbor::decode::Error::message(format!( + "tag value must be: {ENCODED_CBOR_TAG}, provided: {}", + tag.as_u64(), + ))); + } + let cbor_bytes = d.bytes()?.to_vec(); + let cbor = T::from_bytes(&cbor_bytes).map_err(minicbor::decode::Error::message)?; + Ok(Self(cbor)) + } +} + +impl Encode<()> for EncodedCbor +where T: for<'a> Cbor<'a> +{ + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.tag(Tag::new(ENCODED_CBOR_TAG))?; + let cbor_bytes = self + .0 + .to_bytes() + .map_err(minicbor::encode::Error::message)?; + e.bytes(&cbor_bytes)?; + Ok(()) + } +} diff --git a/rust/vote-tx-v2/src/gen_tx/builder.rs b/rust/vote-tx-v2/src/gen_tx/builder.rs index a34fa62dbc1..512fa2828bc 100644 --- a/rust/vote-tx-v2/src/gen_tx/builder.rs +++ b/rust/vote-tx-v2/src/gen_tx/builder.rs @@ -2,10 +2,8 @@ use anyhow::ensure; -use super::{ - cose_protected_header, EncodedCbor, EventKey, EventMap, GeneralizedTx, TxBody, Vote, VoterData, -}; -use crate::{uuid::Uuid, Cbor}; +use super::{cose_protected_header, EventKey, EventMap, GeneralizedTx, TxBody, Vote, VoterData}; +use crate::{encoded_cbor::EncodedCbor, uuid::Uuid, Cbor}; /// `GeneralizedTx` builder struct #[allow(clippy::module_name_repetitions)] diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 46764ed7c23..830ea67db1a 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -11,7 +11,7 @@ mod vote; pub use builder::GeneralizedTxBuilder; use coset::CborSerializable; pub use event_map::{EventKey, EventMap}; -use minicbor::{data::Tag, Decode, Decoder, Encode, Encoder}; +use minicbor::{Decode, Decoder, Encode, Encoder}; pub use tx_body::{TxBody, VoterData}; pub use vote::{Choice, Proof, PropId, Vote}; @@ -32,14 +32,6 @@ where signature: coset::CoseSign, } -/// An encoded CBOR struct, CBOR tag 24. -#[derive(Debug, Clone, PartialEq)] -pub struct EncodedCbor(T) -where T: for<'a> Cbor<'a>; - -/// encoded-cbor CBOR tag . -const ENCODED_CBOR_TAG: u64 = 24; - /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; @@ -109,39 +101,6 @@ where } } -impl Decode<'_, ()> for EncodedCbor -where T: for<'a> Cbor<'a> -{ - fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { - let tag = d.tag()?; - if ENCODED_CBOR_TAG != tag.as_u64() { - return Err(minicbor::decode::Error::message(format!( - "tag value must be: {ENCODED_CBOR_TAG}, provided: {}", - tag.as_u64(), - ))); - } - let cbor_bytes = d.bytes()?.to_vec(); - let cbor = T::from_bytes(&cbor_bytes).map_err(minicbor::decode::Error::message)?; - Ok(Self(cbor)) - } -} - -impl Encode<()> for EncodedCbor -where T: for<'a> Cbor<'a> -{ - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.tag(Tag::new(ENCODED_CBOR_TAG))?; - let cbor_bytes = self - .0 - .to_bytes() - .map_err(minicbor::encode::Error::message)?; - e.bytes(&cbor_bytes)?; - Ok(()) - } -} - /// Reads CBOR bytes from the decoder and returns them as bytes. fn read_cbor_bytes(d: &mut Decoder<'_>) -> Result, minicbor::decode::Error> { let start = d.position(); @@ -169,7 +128,7 @@ mod tests { use test_strategy::proptest; use super::*; - use crate::uuid::Uuid; + use crate::{encoded_cbor::EncodedCbor, uuid::Uuid}; type ChoiceT = Vec; type ProofT = Vec; diff --git a/rust/vote-tx-v2/src/gen_tx/tx_body.rs b/rust/vote-tx-v2/src/gen_tx/tx_body.rs index 12f494bc275..01eeb8daf87 100644 --- a/rust/vote-tx-v2/src/gen_tx/tx_body.rs +++ b/rust/vote-tx-v2/src/gen_tx/tx_body.rs @@ -2,8 +2,8 @@ use minicbor::{Decode, Decoder, Encode, Encoder}; -use super::{EncodedCbor, EventMap, Vote}; -use crate::{uuid::Uuid, Cbor}; +use super::{EventMap, Vote}; +use crate::{encoded_cbor::EncodedCbor, uuid::Uuid, Cbor}; /// `TxBody` array struct length const TX_BODY_LEN: u64 = 4; diff --git a/rust/vote-tx-v2/src/gen_tx/vote.rs b/rust/vote-tx-v2/src/gen_tx/vote.rs index 13e68d1ba59..057da632b1d 100644 --- a/rust/vote-tx-v2/src/gen_tx/vote.rs +++ b/rust/vote-tx-v2/src/gen_tx/vote.rs @@ -2,8 +2,7 @@ use minicbor::{Decode, Decoder, Encode}; -use super::EncodedCbor; -use crate::Cbor; +use crate::{encoded_cbor::EncodedCbor, Cbor}; /// `Vote` array struct length const VOTE_LEN: u64 = 3; diff --git a/rust/vote-tx-v2/src/lib.rs b/rust/vote-tx-v2/src/lib.rs index 84343e020e8..2cc28f79e94 100644 --- a/rust/vote-tx-v2/src/lib.rs +++ b/rust/vote-tx-v2/src/lib.rs @@ -6,6 +6,7 @@ use anyhow::anyhow; use minicbor::{Decode, Decoder, Encode, Encoder}; +pub mod encoded_cbor; pub mod gen_tx; pub mod public_tx; pub mod uuid; From 415ff9e20752b478a1b238d193988974e28b6cb4 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 11:14:26 +0200 Subject: [PATCH 24/28] add public tx test --- rust/vote-tx-v2/src/public_tx/choice.rs | 3 +- rust/vote-tx-v2/src/public_tx/mod.rs | 39 ++++++++++++++++++++++-- rust/vote-tx-v2/src/public_tx/proof.rs | 1 + rust/vote-tx-v2/src/public_tx/prop_id.rs | 23 -------------- 4 files changed, 39 insertions(+), 27 deletions(-) delete mode 100644 rust/vote-tx-v2/src/public_tx/prop_id.rs diff --git a/rust/vote-tx-v2/src/public_tx/choice.rs b/rust/vote-tx-v2/src/public_tx/choice.rs index 3d20dc60ab4..c173cb82b0a 100644 --- a/rust/vote-tx-v2/src/public_tx/choice.rs +++ b/rust/vote-tx-v2/src/public_tx/choice.rs @@ -3,7 +3,8 @@ use minicbor::{Decode, Encode}; /// A public voting choice struct. -pub struct Choice(u64); +#[derive(Debug, Clone, PartialEq)] +pub struct Choice(pub u64); impl Decode<'_, ()> for Choice { fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index fe8bea924fb..ed0ff81ad7a 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -3,18 +3,20 @@ mod choice; mod proof; -mod prop_id; use std::ops::{Deref, DerefMut}; pub use choice::Choice; use minicbor::{Decode, Encode}; pub use proof::Proof; -pub use prop_id::PropId; -use crate::{gen_tx::GeneralizedTx, Cbor}; +use crate::{gen_tx::GeneralizedTx, uuid::Uuid, Cbor}; + +/// A public voting proposal id struct. +pub type PropId = Uuid; /// A public vote tx struct. +#[derive(Debug, Clone, PartialEq)] pub struct PublicTx(GeneralizedTx) where VoteDataT: for<'a> Cbor<'a>; @@ -54,3 +56,34 @@ where VoteDataT: for<'a> Cbor<'a> self.0.encode(e, ctx) } } + +#[cfg(test)] +mod tests { + use proptest::sample::size_range; + use test_strategy::proptest; + + use super::*; + use crate::{encoded_cbor::EncodedCbor, gen_tx::GeneralizedTxBuilder, uuid::Uuid}; + + #[proptest] + fn public_tx_from_bytes_to_bytes_test( + vote_type: Vec, voter_data: Vec, + #[any(size_range(1..10_usize).lift())] choices: Vec, prop_id: Vec, + ) { + let gen_tx_builder = GeneralizedTxBuilder::::new( + Uuid(vote_type), + EncodedCbor(voter_data), + ); + let choices = choices.into_iter().map(Choice).collect(); + let gen_tx = gen_tx_builder + .with_vote(choices, Proof, Uuid(prop_id)) + .unwrap() + .build() + .unwrap(); + let public_tx = PublicTx(gen_tx); + + let bytes = public_tx.to_bytes().unwrap(); + let decoded = PublicTx::from_bytes(&bytes).unwrap(); + assert_eq!(public_tx, decoded); + } +} diff --git a/rust/vote-tx-v2/src/public_tx/proof.rs b/rust/vote-tx-v2/src/public_tx/proof.rs index 27bd9a2836b..e244b277af3 100644 --- a/rust/vote-tx-v2/src/public_tx/proof.rs +++ b/rust/vote-tx-v2/src/public_tx/proof.rs @@ -3,6 +3,7 @@ use minicbor::{Decode, Encode}; /// A public voting proof struct, CBOR `undefined`. +#[derive(Debug, Clone, PartialEq)] pub struct Proof; impl Decode<'_, ()> for Proof { diff --git a/rust/vote-tx-v2/src/public_tx/prop_id.rs b/rust/vote-tx-v2/src/public_tx/prop_id.rs deleted file mode 100644 index 41b93599ae1..00000000000 --- a/rust/vote-tx-v2/src/public_tx/prop_id.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! A public vote tx proposal id struct. - -use minicbor::{Decode, Encode}; - -use crate::uuid::Uuid; - -/// A public voting proposal id struct. -pub struct PropId(Uuid); - -impl Decode<'_, ()> for PropId { - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - let prop_id = Uuid::decode(d, &mut ())?; - Ok(Self(prop_id)) - } -} - -impl Encode<()> for PropId { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, &mut ()) - } -} From 548aa3f619162385052d18905ce3fcd56bdbe0dc Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Tue, 19 Nov 2024 12:27:46 +0200 Subject: [PATCH 25/28] fix spelling --- rust/vote-tx-v2/src/gen_tx/builder.rs | 18 +++++++++--------- rust/vote-tx-v2/src/gen_tx/event_map.rs | 2 +- rust/vote-tx-v2/src/gen_tx/mod.rs | 14 +++++++------- rust/vote-tx-v2/src/gen_tx/tx_body.rs | 8 ++++---- rust/vote-tx-v2/src/gen_tx/vote.rs | 8 ++++---- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/rust/vote-tx-v2/src/gen_tx/builder.rs b/rust/vote-tx-v2/src/gen_tx/builder.rs index 512fa2828bc..ab6cf9e264a 100644 --- a/rust/vote-tx-v2/src/gen_tx/builder.rs +++ b/rust/vote-tx-v2/src/gen_tx/builder.rs @@ -1,4 +1,4 @@ -//! A Catalyst generalised vote transaction builder +//! A Catalyst generalized vote transaction builder use anyhow::ensure; @@ -7,11 +7,11 @@ use crate::{encoded_cbor::EncodedCbor, uuid::Uuid, Cbor}; /// `GeneralizedTx` builder struct #[allow(clippy::module_name_repetitions)] -pub struct GeneralizedTxBuilder +pub struct GeneralizedTxBuilder where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, VoterDataT: for<'a> Cbor<'a>, { /// The `vote_type` field @@ -19,19 +19,19 @@ where /// The `event` field event: EventMap, /// The `votes` field - votes: Vec>, + votes: Vec>, /// The `voter_data` field voter_data: VoterData, /// The `signature` builder field sign_builder: coset::CoseSignBuilder, } -impl - GeneralizedTxBuilder +impl + GeneralizedTxBuilder where ChoiceT: for<'a> Cbor<'a> + Clone, ProofT: for<'a> Cbor<'a> + Clone, - ProopIdT: for<'a> Cbor<'a> + Clone, + PropIdT: for<'a> Cbor<'a> + Clone, VoterDataT: for<'a> Cbor<'a> + Clone, { /// Creates a new `GeneralizedTxBuilder` struct @@ -64,7 +64,7 @@ where /// # Errors /// - `choices` array must has at least one entry- pub fn with_vote( - mut self, choices: Vec, proof: ProofT, prop_id: ProopIdT, + mut self, choices: Vec, proof: ProofT, prop_id: PropIdT, ) -> anyhow::Result { ensure!( !choices.is_empty(), @@ -82,7 +82,7 @@ where /// /// # Errors /// - `votes` array must has at least one entry - pub fn build(self) -> anyhow::Result> { + pub fn build(self) -> anyhow::Result> { ensure!( !self.votes.is_empty(), "`votes` array must has at least one entry" diff --git a/rust/vote-tx-v2/src/gen_tx/event_map.rs b/rust/vote-tx-v2/src/gen_tx/event_map.rs index be68de4ac17..91b6947c4ae 100644 --- a/rust/vote-tx-v2/src/gen_tx/event_map.rs +++ b/rust/vote-tx-v2/src/gen_tx/event_map.rs @@ -1,4 +1,4 @@ -//! A generalised tx event map struct. +//! A generalized tx event map struct. use minicbor::{data::Int, Decode, Decoder, Encode, Encoder}; diff --git a/rust/vote-tx-v2/src/gen_tx/mod.rs b/rust/vote-tx-v2/src/gen_tx/mod.rs index 830ea67db1a..e142a9b7b15 100644 --- a/rust/vote-tx-v2/src/gen_tx/mod.rs +++ b/rust/vote-tx-v2/src/gen_tx/mod.rs @@ -1,4 +1,4 @@ -//! A Catalyst generalised vote transaction object, structured following this +//! A Catalyst generalized vote transaction object, structured following this //! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/gen_vote_tx/) // cspell: words Coap @@ -19,15 +19,15 @@ use crate::Cbor; /// A generalized tx struct. #[derive(Debug, Clone, PartialEq)] -pub struct GeneralizedTx +pub struct GeneralizedTx where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, VoterDataT: for<'a> Cbor<'a>, { /// `tx-body` field - tx_body: TxBody, + tx_body: TxBody, /// `signature` field signature: coset::CoseSign, } @@ -35,12 +35,12 @@ where /// `GeneralizedTx` array struct length const GENERALIZED_TX_LEN: u64 = 2; -impl Decode<'_, ()> - for GeneralizedTx +impl Decode<'_, ()> + for GeneralizedTx where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, VoterDataT: for<'a> Cbor<'a>, { fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result { diff --git a/rust/vote-tx-v2/src/gen_tx/tx_body.rs b/rust/vote-tx-v2/src/gen_tx/tx_body.rs index 01eeb8daf87..c129a3e15c3 100644 --- a/rust/vote-tx-v2/src/gen_tx/tx_body.rs +++ b/rust/vote-tx-v2/src/gen_tx/tx_body.rs @@ -1,4 +1,4 @@ -//! A generalised tx body struct. +//! A generalized tx body struct. use minicbor::{Decode, Decoder, Encode, Encoder}; @@ -13,11 +13,11 @@ pub type VoterData = EncodedCbor; /// A tx body struct. #[derive(Debug, Clone, PartialEq)] -pub struct TxBody +pub struct TxBody where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, VoterDataT: for<'a> Cbor<'a>, { /// `vote-type` field @@ -25,7 +25,7 @@ where /// `event` field pub(super) event: EventMap, /// `votes` field - pub(super) votes: Vec>, + pub(super) votes: Vec>, /// `voter-data` field pub(super) voter_data: VoterData, } diff --git a/rust/vote-tx-v2/src/gen_tx/vote.rs b/rust/vote-tx-v2/src/gen_tx/vote.rs index 057da632b1d..cd5c3c56eea 100644 --- a/rust/vote-tx-v2/src/gen_tx/vote.rs +++ b/rust/vote-tx-v2/src/gen_tx/vote.rs @@ -1,4 +1,4 @@ -//! A generalised tx vote struct. +//! A generalized tx vote struct. use minicbor::{Decode, Decoder, Encode}; @@ -16,18 +16,18 @@ pub type PropId = EncodedCbor; /// A vote struct. #[derive(Debug, Clone, PartialEq)] -pub struct Vote +pub struct Vote where ChoiceT: for<'a> Cbor<'a>, ProofT: for<'a> Cbor<'a>, - ProopIdT: for<'a> Cbor<'a>, + PropIdT: for<'a> Cbor<'a>, { /// `choices` field pub(super) choices: Vec>, /// `proof` field pub(super) proof: Proof, /// `prop-id` field - pub(super) prop_id: PropId, + pub(super) prop_id: PropId, } impl Decode<'_, ()> for Vote From 57bdaecf18bfd68fe62ba1f7a68cbe80739ffe3b Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 2 Dec 2024 11:08:49 +0200 Subject: [PATCH 26/28] fix, add missing comma --- .../08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl index 5ec5c8bb6a5..31e3619bab0 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl @@ -4,7 +4,7 @@ gen-vote-tx = [ ] tx-body = [ - vote-type + vote-type, event, votes, voter-data, From bf461c33d8598fde2a13a9581d977cc6c991201a Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 2 Dec 2024 12:29:31 +0200 Subject: [PATCH 27/28] move definition of public_tx/choice and public_tx/proof to the public_tx/vote mod --- rust/vote-tx-v2/src/public_tx/choice.rs | 22 ------------ rust/vote-tx-v2/src/public_tx/mod.rs | 11 ++---- rust/vote-tx-v2/src/public_tx/proof.rs | 23 ------------ rust/vote-tx-v2/src/public_tx/vote.rs | 47 +++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 53 deletions(-) delete mode 100644 rust/vote-tx-v2/src/public_tx/choice.rs delete mode 100644 rust/vote-tx-v2/src/public_tx/proof.rs create mode 100644 rust/vote-tx-v2/src/public_tx/vote.rs diff --git a/rust/vote-tx-v2/src/public_tx/choice.rs b/rust/vote-tx-v2/src/public_tx/choice.rs deleted file mode 100644 index c173cb82b0a..00000000000 --- a/rust/vote-tx-v2/src/public_tx/choice.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! A public vote tx choice struct. - -use minicbor::{Decode, Encode}; - -/// A public voting choice struct. -#[derive(Debug, Clone, PartialEq)] -pub struct Choice(pub u64); - -impl Decode<'_, ()> for Choice { - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - let choice = d.u64()?; - Ok(Self(choice)) - } -} - -impl Encode<()> for Choice { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, &mut ()) - } -} diff --git a/rust/vote-tx-v2/src/public_tx/mod.rs b/rust/vote-tx-v2/src/public_tx/mod.rs index ed0ff81ad7a..af2262c5851 100644 --- a/rust/vote-tx-v2/src/public_tx/mod.rs +++ b/rust/vote-tx-v2/src/public_tx/mod.rs @@ -1,19 +1,14 @@ //! A Catalyst public vote transaction v2 object, structured following this //! [spec](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/v2/#public-vote) -mod choice; -mod proof; +mod vote; use std::ops::{Deref, DerefMut}; -pub use choice::Choice; use minicbor::{Decode, Encode}; -pub use proof::Proof; +pub use vote::{Choice, Proof, PropId}; -use crate::{gen_tx::GeneralizedTx, uuid::Uuid, Cbor}; - -/// A public voting proposal id struct. -pub type PropId = Uuid; +use crate::{gen_tx::GeneralizedTx, Cbor}; /// A public vote tx struct. #[derive(Debug, Clone, PartialEq)] diff --git a/rust/vote-tx-v2/src/public_tx/proof.rs b/rust/vote-tx-v2/src/public_tx/proof.rs deleted file mode 100644 index e244b277af3..00000000000 --- a/rust/vote-tx-v2/src/public_tx/proof.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! A public vote tx proof struct. - -use minicbor::{Decode, Encode}; - -/// A public voting proof struct, CBOR `undefined`. -#[derive(Debug, Clone, PartialEq)] -pub struct Proof; - -impl Decode<'_, ()> for Proof { - fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { - d.undefined()?; - Ok(Self) - } -} - -impl Encode<()> for Proof { - fn encode( - &self, e: &mut minicbor::Encoder, (): &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.undefined()?; - Ok(()) - } -} diff --git a/rust/vote-tx-v2/src/public_tx/vote.rs b/rust/vote-tx-v2/src/public_tx/vote.rs new file mode 100644 index 00000000000..35f4fa89b51 --- /dev/null +++ b/rust/vote-tx-v2/src/public_tx/vote.rs @@ -0,0 +1,47 @@ +//! A public vote tx vote objects. + +use minicbor::{Decode, Encode}; + +use crate::uuid::Uuid; + +/// A public voting choice struct. +#[derive(Debug, Clone, PartialEq)] +pub struct Choice(pub u64); + +/// A public voting proof struct, CBOR `undefined`. +#[derive(Debug, Clone, PartialEq)] +pub struct Proof; + +/// A public voting proposal id struct. +pub type PropId = Uuid; + +impl Decode<'_, ()> for Choice { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + let choice = d.u64()?; + Ok(Self(choice)) + } +} + +impl Encode<()> for Choice { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + self.0.encode(e, &mut ()) + } +} + +impl Decode<'_, ()> for Proof { + fn decode(d: &mut minicbor::Decoder<'_>, (): &mut ()) -> Result { + d.undefined()?; + Ok(Self) + } +} + +impl Encode<()> for Proof { + fn encode( + &self, e: &mut minicbor::Encoder, (): &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.undefined()?; + Ok(()) + } +} From 2cde1b39048d0cd7bfddbd06cc739b8babb902f5 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 2 Dec 2024 12:37:28 +0200 Subject: [PATCH 28/28] add comment which specifies a CBOR encoding/decoding profile --- .../08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl | 3 +++ .../catalyst_voting/cddl/gen_vote_tx_cose_payload.cddl | 3 +++ .../08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl | 3 +++ .../08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl | 3 +++ .../architecture/08_concepts/immutable_ledger/cddl/block.cddl | 3 +++ .../immutable_ledger/cddl/genesis_to_prev_hash.cddl | 3 +++ .../architecture/08_concepts/immutable_ledger/cddl/hash.cddl | 3 +++ 7 files changed, 21 insertions(+) diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl index 31e3619bab0..c343b5658ba 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl @@ -1,3 +1,6 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + gen-vote-tx = [ tx-body, signature diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx_cose_payload.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx_cose_payload.cddl index f432c574042..e0a996f19c2 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx_cose_payload.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx_cose_payload.cddl @@ -1,2 +1,5 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + cose-payload = blake2b-256 blake2b-256 = #6.32782(bytes .size 32) ; Blake2b-256 hash bytes diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl index e65dd4341e9..8499c7ddf0f 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_private.cddl @@ -1,3 +1,6 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + vote-tx-v2 = gen-vote-tx choice-data = ciphertext diff --git a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl index d1e180ef471..8c5ef356160 100644 --- a/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl +++ b/docs/src/architecture/08_concepts/catalyst_voting/cddl/vote_tx_v2_public.cddl @@ -1,3 +1,6 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + vote-tx-v2-public = gen-vote-tx choice-data = uint diff --git a/docs/src/architecture/08_concepts/immutable_ledger/cddl/block.cddl b/docs/src/architecture/08_concepts/immutable_ledger/cddl/block.cddl index d65f0fc78a8..1c459ba02e2 100644 --- a/docs/src/architecture/08_concepts/immutable_ledger/cddl/block.cddl +++ b/docs/src/architecture/08_concepts/immutable_ledger/cddl/block.cddl @@ -1,3 +1,6 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + block = [ block-header, block-data, diff --git a/docs/src/architecture/08_concepts/immutable_ledger/cddl/genesis_to_prev_hash.cddl b/docs/src/architecture/08_concepts/immutable_ledger/cddl/genesis_to_prev_hash.cddl index c30ecbf9950..7d0d525063b 100644 --- a/docs/src/architecture/08_concepts/immutable_ledger/cddl/genesis_to_prev_hash.cddl +++ b/docs/src/architecture/08_concepts/immutable_ledger/cddl/genesis_to_prev_hash.cddl @@ -1,3 +1,6 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + genesis-to-prev-hash = [ chain-id: ULID, timestamp: #6.1(uint .ge 1722470400), ; Epoch-based date/time diff --git a/docs/src/architecture/08_concepts/immutable_ledger/cddl/hash.cddl b/docs/src/architecture/08_concepts/immutable_ledger/cddl/hash.cddl index 32ca39ae720..18c0d6e4a16 100644 --- a/docs/src/architecture/08_concepts/immutable_ledger/cddl/hash.cddl +++ b/docs/src/architecture/08_concepts/immutable_ledger/cddl/hash.cddl @@ -1,3 +1,6 @@ +; All encoders/decoders of this specification must follow deterministic cbor encoding rules +; https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cde-06 + hash-bytes = ( #6.32781(bytes) / ; Blake3 hash #6.32782(bytes) / ; Blake2b hash